#pragma once

#include "engine/prefabs/includes.h"
#include "ghhb_game.h"
#include <array>
#include <vector>

class InstrumentSelectScreen : public Scene
{
public:
    Font font = {0};

    static constexpr int SLOT_TOP = 0;
    static constexpr int SLOT_BOTTOM = 1;
    static constexpr int SLOT_LEFT = 2;
    static constexpr int SLOT_RIGHT = 3;

    static constexpr int BUTTON_SLOT[4] = {
        SLOT_TOP,   // GAMEPAD_BUTTON_LEFT_FACE_UP
        SLOT_BOTTOM, // GAMEPAD_BUTTON_LEFT_FACE_DOWN
        SLOT_LEFT,   // GAMEPAD_BUTTON_LEFT_FACE_LEFT
        SLOT_RIGHT,  // GAMEPAD_BUTTON_LEFT_FACE_RIGHT
    };
    static constexpr int FACE_BUTTONS[4] = {
        GAMEPAD_BUTTON_LEFT_FACE_UP,
        GAMEPAD_BUTTON_LEFT_FACE_DOWN,
        GAMEPAD_BUTTON_LEFT_FACE_LEFT,
        GAMEPAD_BUTTON_LEFT_FACE_RIGHT,
    };

    static const Color BORDER_COLOR;

    static constexpr const char* INSTRUMENT_IMAGE_PATHS[MAX_INSTRUMENT_TYPES] = {
        "assets/instrument_0.png",
        "assets/instrument_1.png",
        "assets/instrument_2.png",
        "assets/instrument_3.png",
    };

    std::array<int, MAX_INSTRUMENT_TYPES> instrument_owner = {-1, -1, -1, -1};
    std::vector<int> participant_order;

    void init_services() override { add_service<TextureService>(); }

    void init() override
    {
        auto font_manager = game->get_manager<FontManager>();
        font = font_manager->get_font("Roboto");
        instrument_owner.fill(-1);
        participant_order.clear();
    }

    int get_or_assign_gamepad_index(int physical_gamepad_id)
    {
        for (size_t i = 0; i < participant_order.size(); i++)
        {
            if (participant_order[i] == physical_gamepad_id)
                return static_cast<int>(i);
        }
        if (participant_order.size() < static_cast<size_t>(MAX_INSTRUMENT_TYPES))
        {
            participant_order.push_back(physical_gamepad_id);
            return static_cast<int>(participant_order.size() - 1);
        }
        return -1;
    }

    void update(float delta_time) override
    {
        for (int gp = 0; gp < MAX_GAMEPADS; gp++)
        {
            if (!IsGamepadAvailable(gp))
                continue;
            for (int b = 0; b < 4; b++)
            {
                if (IsGamepadButtonPressed(gp, FACE_BUTTONS[b]))
                {
                    int slot = BUTTON_SLOT[b];
                    int gi = get_or_assign_gamepad_index(gp);
                    if (gi >= 0)
                    {
                        if (instrument_owner[slot] == gi)
                            instrument_owner[slot] = -1;
                        else
                            instrument_owner[slot] = gi;
                    }
                }
            }
        }

        bool all_selected = true;
        for (int s = 0; s < MAX_INSTRUMENT_TYPES; s++)
        {
            if (instrument_owner[s] < 0)
            {
                all_selected = false;
                break;
            }
        }
        if (all_selected)
        {
            if (IsKeyPressed(KEY_ENTER))
            {
                for (int i = 0; i < MAX_INSTRUMENT_TYPES; i++)
                {
                    INSTRUMENT_GAMEPAD_INDEX[i] = instrument_owner[i];
                    INSTRUMENT_PHYSICAL_GAMEPAD[i] = participant_order[instrument_owner[i]];
                }
                game->go_to_scene("ghhb");
                return;
            }
            for (int gp = 0; gp < MAX_GAMEPADS; gp++)
            {
                if (IsGamepadAvailable(gp) && IsGamepadButtonPressed(gp, GAMEPAD_BUTTON_MIDDLE_RIGHT))
                {
                    for (int i = 0; i < MAX_INSTRUMENT_TYPES; i++)
                    {
                        INSTRUMENT_GAMEPAD_INDEX[i] = instrument_owner[i];
                        INSTRUMENT_PHYSICAL_GAMEPAD[i] = participant_order[instrument_owner[i]];
                    }
                    game->go_to_scene("ghhb");
                    return;
                }
            }
        }
    }

    void draw() override
    {
        float w = static_cast<float>(GetScreenWidth());
        float h = static_cast<float>(GetScreenHeight());
        float cx = w * 0.5f;
        float cy = h * 0.5f;

        /* Vertices in counter-clockwise order so Raylib fills the triangle. */
        Vector2 top_tri[3] = {{cx, cy}, {w, 0}, {0, 0}};
        Vector2 bottom_tri[3] = {{0, h}, {w, h}, {cx, cy}};
        Vector2 left_tri[3] = {{0, 0}, {0, h}, {cx, cy}};
        Vector2 right_tri[3] = {{cx, cy}, {w, h}, {w, 0}};

        auto draw_wedge = [this](const Vector2* v, int slot) {
            DrawTriangle(v[0], v[1], v[2], INSTRUMENT_COLORS[slot]);
        };

        draw_wedge(top_tri, SLOT_TOP);
        draw_wedge(bottom_tri, SLOT_BOTTOM);
        draw_wedge(left_tri, SLOT_LEFT);
        draw_wedge(right_tri, SLOT_RIGHT);

        DrawLineEx(Vector2{0, 0}, Vector2{w, h}, 3.0f, BORDER_COLOR);
        DrawLineEx(Vector2{w, 0}, Vector2{0, h}, 3.0f, BORDER_COLOR);

        auto* tex_svc = get_service<TextureService>();
        auto draw_center_icon = [this, tex_svc](float ix, float iy, int slot) {
            const char* path = INSTRUMENT_IMAGE_PATHS[slot];
            if (FileExists(path))
            {
                Texture2D& tex = tex_svc->get_texture(path);
                float tw = static_cast<float>(tex.width);
                float th = static_cast<float>(tex.height);
                float scale = 1.0f;
                float max_dim = 120.0f;
                if (tw > max_dim || th > max_dim)
                    scale = max_dim / (tw > th ? tw : th);
                float dw = tw * scale;
                float dh = th * scale;
                DrawTextureEx(tex, Vector2{ix - dw * 0.5f, iy - dh * 0.5f}, 0.0f, scale, WHITE);
            }
            else
            {
                DrawRectangle(static_cast<int>(ix - 40), static_cast<int>(iy - 40), 80, 80,
                              Color{80, 80, 100, 255});
            }
        };

        const float icon_y_top = h / 6.0f;
        const float icon_y_bottom = h * 5.0f / 6.0f;
        const float icon_x_left = w / 6.0f;
        const float icon_x_right = w * 5.0f / 6.0f;
        draw_center_icon(cx, icon_y_top, SLOT_TOP);
        draw_center_icon(cx, icon_y_bottom, SLOT_BOTTOM);
        draw_center_icon(icon_x_left, cy, SLOT_LEFT);
        draw_center_icon(icon_x_right, cy, SLOT_RIGHT);

        const char* player_labels[4] = {"P1", "P2", "P3", "P4"};
        const float label_font_size = 60.0f;
        const float label_offset = 50.0f;
        const float label_offset_bottom = 110.0f;
        auto draw_player_label = [this, &player_labels, label_font_size, label_offset,
                                  label_offset_bottom](float ix, float iy, int slot) {
            if (instrument_owner[slot] < 0)
                return;
            const char* label = player_labels[instrument_owner[slot]];
            float lw = MeasureTextEx(font, label, label_font_size, 1.0f).x;
            float ly = (slot == SLOT_BOTTOM) ? iy - label_offset_bottom : iy + label_offset;
            DrawTextEx(font, label, Vector2{ix - lw * 0.5f, ly}, label_font_size, 1.0f, WHITE);
        };
        draw_player_label(cx, icon_y_top, SLOT_TOP);
        draw_player_label(cx, icon_y_bottom, SLOT_BOTTOM);
        draw_player_label(icon_x_left, cy, SLOT_LEFT);
        draw_player_label(icon_x_right, cy, SLOT_RIGHT);

        bool all_selected = true;
        for (int s = 0; s < MAX_INSTRUMENT_TYPES; s++)
        {
            if (instrument_owner[s] < 0)
            {
                all_selected = false;
                break;
            }
        }
        if (all_selected)
        {
            const char* prompt = "Press Start to begin";
            float font_size = 24.0f;
            float pw = MeasureTextEx(font, prompt, font_size, 1.0f).x;
            DrawTextEx(font, prompt, Vector2{cx - pw * 0.5f, h - 40.0f}, font_size, 1.0f, WHITE);
        }
    }
};

inline const Color InstrumentSelectScreen::BORDER_COLOR = {45, 55, 72, 255};
