#include <iostream>
#include <algorithm>
#include <random>
#include <sstream>
#include "Game.hpp"
#include "SDL.h"
#include "SDL_ttf.h"
#include "data/words.hpp"
#include "utils.hpp"
#include "default_font.hpp"
#include "State.hpp"

const int CHAR_SIZE = 30;
const int STEP_SIZE = CHAR_SIZE + CHAR_SIZE / 2;
const int UNDERSCORE_DY = 10;
const SDL_Color TEXT_COLOR = {255, 255, 255};
const int MAX_GUESSES = 8;

void Game::Run() {
    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
        std::cerr << "Failed to initialize SDL: " << SDL_GetError() << std::endl;
        exit(-1);
    }
    if (TTF_Init() < 0) {
        std::cerr << "Failed to initialize TTF: " << TTF_GetError() << std::endl;
        exit(-1);
    }
    SDL_Window *window = SDL_CreateWindow("hang man", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_SIZE.x,
                                          SCREEN_SIZE.y, 0);
    if (window == nullptr) {
        std::cerr << "Failed to create SDL_Window with error: " << SDL_GetError() << std::endl;
        exit(-1);
    }
    SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
    if (renderer == nullptr) {
        std::cerr << "Failed to create SDL_Renderer with error: " << SDL_GetError() << std::endl;
        exit(-1);
    }
    SDL_RendererInfo rendererInfo;
    if (SDL_GetRendererInfo(renderer, &rendererInfo) == 0) {
        std::cout << "Using renderer: " << rendererInfo.name << std::endl;
    }

    SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
    SDL_RenderClear(renderer);
    SDL_RenderPresent(renderer);

    Game game;
    bool quit = false;
    SDL_Event event;
    while (!quit) {
        game.draw(renderer);
        while (SDL_WaitEvent(&event)) {
            switch (event.type) {
                case SDL_QUIT:
                    quit = true;
                    break;
                case SDL_KEYDOWN :
                    game.handle_key(event.key.keysym.sym);
                    game.draw(renderer);
            }
        }

    }

    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_QuitSubSystem(SDL_INIT_VIDEO);
    SDL_Quit();
}

Game::Game() : m_wrong_guesses(0), m_game_state(State::PLAY) {
    const char *defaultFontPath = getDefaultFontPath();
    if (defaultFontPath == nullptr) {
        std::stringstream ss;
        ss << "Font path is not set for this platform (null)";
        auto s = ss.str();
        std::cerr << s << std::endl;
        throw std::runtime_error(s);
    }
    font = TTF_OpenFont(defaultFontPath, CHAR_SIZE);
    if (font == nullptr) {
        std::stringstream ss;
        ss << "Failed to load font: " << TTF_GetError();
        auto s = ss.str();
        std::cerr << s << std::endl;
        throw std::runtime_error(s);
    }

    std::random_device random_device{};
    std::mt19937 rng(random_device());

    size_t words_len = array_len(words);
    all_words = std::vector<const char *>();
    all_words.reserve(words_len);
    for (int i = 0; i < words_len; i++)
        all_words.push_back(words[i]);
    std::shuffle(all_words.begin(), all_words.end(), rng);
    word = all_words.back();
    all_words.pop_back();
    guess_corrector = std::make_unique<GuessCorrector>(word);
    std::cout << "Word: " << word << std::endl;
}

void Game::handle_key(SDL_Keycode event) {
    if (!isalpha(event))
        return;
    bool is_valid = guess_corrector->has_char(event);
    if (is_valid) {
        guess_corrector->add(event);
        if (guess_corrector->is_filled_out())
            m_game_state = State::WIN;
    } else {
        m_wrong_guesses += 1;
        if (m_wrong_guesses >= MAX_GUESSES) {
            m_game_state = State::GAME_OVER;
        }
    }
}

void Game::draw(SDL_Renderer *renderer) {
    SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
    SDL_RenderClear(renderer);

    draw_guesses(renderer);
}

void Game::draw_guesses(SDL_Renderer *renderer) {
    size_t len = strlen(word);
    int total_width = (len - 1) * STEP_SIZE + CHAR_SIZE;
    int start_x = (SCREEN_SIZE.x - total_width) / 2;
    int char_y = (SCREEN_SIZE.y / 4) * 3 - CHAR_SIZE / 2;

    for (int i = 0; i < len; i++) {
        int here = start_x + i * STEP_SIZE;
        const SDL_Rect rect = {here, char_y + UNDERSCORE_DY, CHAR_SIZE, 5};
        SDL_SetRenderDrawColor(renderer, 255, 0, 0, SDL_ALPHA_OPAQUE);
        SDL_RenderFillRect(renderer, &rect);

        std::optional<char> current_char = guess_corrector->guessed().lock()[i];
        if (current_char) {
            const char text_to_write[] = {*current_char, '\0'};

            SDL_Surface *surface = TTF_RenderText_Blended(font, text_to_write, TEXT_COLOR);
            if (surface == nullptr) {
                std::cerr << "Failed to create surface: " << TTF_GetError() << std::endl;
                continue;
            }

            SDL_Texture *txt = SDL_CreateTextureFromSurface(renderer, surface);
            if (txt == nullptr) {
                std::cerr << "Failed to create texture: " << SDL_GetError() << std::endl;
                SDL_FreeSurface(surface);
                continue;
            }

            int text_width = surface->w;
            int text_height = surface->h;
            SDL_Rect text_rect = {here + (CHAR_SIZE - text_width) / 2, char_y - text_height, text_width, text_height};

            SDL_RenderCopy(renderer, txt, nullptr, &text_rect);

            SDL_DestroyTexture(txt);
            SDL_FreeSurface(surface);
        }
    }

    SDL_RenderPresent(renderer);
}
