#pragma once

#include "foundation/result.h"

#include <array>
#include <cstdio>
#include <cstddef>
#include <string>
#include <string_view>

namespace pp::app {

inline constexpr std::array<int, 6> document_resolution_values {
    512,
    1024,
    1536,
    2048,
    4096,
    8192,
};

inline constexpr std::array<std::string_view, 6> document_resolution_labels {
    "2K",
    "4K",
    "6K",
    "8K",
    "16K",
    "32K",
};

struct RecordingFrameLabel {
    bool visible = false;
    std::string text;
};

struct RendererDiagnosticsInput {
    bool framebuffer_fetch = false;
    bool float32_render_targets = false;
    bool float32_linear_filtering = false;
    bool float16_render_targets = false;
};

struct RendererDiagnosticIndicator {
    bool supported = false;
    std::string_view label;
};

struct RendererDiagnosticsPlan {
    RendererDiagnosticIndicator framebuffer_fetch;
    RendererDiagnosticIndicator floating_point_targets;
};

[[nodiscard]] inline pp::foundation::Result<int> display_resolution_from_index(int index)
{
    if (index < 0 || static_cast<std::size_t>(index) >= document_resolution_values.size()) {
        return pp::foundation::Result<int>::failure(
            pp::foundation::Status::out_of_range("document resolution index is out of range"));
    }
    return pp::foundation::Result<int>::success(
        document_resolution_values[static_cast<std::size_t>(index)]);
}

[[nodiscard]] inline pp::foundation::Result<std::size_t> document_resolution_to_index(int resolution)
{
    for (std::size_t index = 0; index < document_resolution_values.size(); ++index) {
        if (document_resolution_values[index] == resolution) {
            return pp::foundation::Result<std::size_t>::success(index);
        }
    }
    return pp::foundation::Result<std::size_t>::failure(
        pp::foundation::Status::out_of_range("document resolution is not supported"));
}

[[nodiscard]] inline pp::foundation::Result<std::string_view> document_resolution_label(int resolution)
{
    const auto index = document_resolution_to_index(resolution);
    if (!index) {
        return pp::foundation::Result<std::string_view>::failure(index.status());
    }
    return pp::foundation::Result<std::string_view>::success(document_resolution_labels[index.value()]);
}

[[nodiscard]] inline std::string make_document_title(
    std::string_view document_name,
    bool has_unsaved_changes,
    int resolution)
{
    const auto label = document_resolution_label(resolution);
    const auto resolution_label = label ? label.value() : std::string_view("unknown");
    std::string title = "Panodoc: ";
    title.append(document_name);
    if (has_unsaved_changes) {
        title.push_back('*');
    }
    title.append(" (");
    title.append(resolution_label);
    title.push_back(')');
    return title;
}

[[nodiscard]] inline std::string make_dpi_label(float zoom)
{
    char buffer[64] {};
    std::snprintf(buffer, sizeof(buffer), "%.1fx-dpi", zoom);
    return buffer;
}

[[nodiscard]] inline std::string make_history_memory_label(std::size_t bytes)
{
    char buffer[128] {};
    std::snprintf(
        buffer,
        sizeof(buffer),
        "History memory: %.2f Mb",
        static_cast<double>(bytes) / 1024.0 / 1024.0);
    return buffer;
}

[[nodiscard]] inline RecordingFrameLabel make_recording_frame_label(
    bool is_recording,
    bool encoder_available,
    int encoded_frames)
{
    if (!is_recording || !encoder_available) {
        return {};
    }

    char buffer[128] {};
    std::snprintf(buffer, sizeof(buffer), "Recorded %d frames", encoded_frames);
    return {
        true,
        buffer,
    };
}

[[nodiscard]] inline RendererDiagnosticsPlan plan_renderer_diagnostics(
    RendererDiagnosticsInput input) noexcept
{
    RendererDiagnosticsPlan plan;
    plan.framebuffer_fetch = {
        input.framebuffer_fetch,
        "FBF",
    };

    if (input.float32_linear_filtering) {
        plan.floating_point_targets = {
            true,
            "F32L",
        };
    } else if (input.float32_render_targets) {
        plan.floating_point_targets = {
            true,
            "F32",
        };
    } else if (input.float16_render_targets) {
        plan.floating_point_targets = {
            true,
            "F16",
        };
    } else {
        plan.floating_point_targets = {
            false,
            "",
        };
    }

    return plan;
}

}
