#include "Game.h"
#include <vector>
#include <string>
#include <fstream>
#include <sstream>
#include <cmath>

Game::Game()
    : snake(Point(BOARD_WIDTH / 2, BOARD_HEIGHT / 2)),
      score(0),
      bestScore(0),
      gameOver(false),
      paused(false),
      inMenu(true)
{
    loadBestScore();
    generateFood();
}

void Game::run()
{
    sf::RenderWindow window(sf::VideoMode(BOARD_WIDTH * CELL_SIZE, BOARD_HEIGHT * CELL_SIZE + 60), "Snake Game");
    sf::Clock clock;
    float timer = 0.0f;

    // Menu buttons initialization
    MenuButton startBtn, exitBtn, restartBtn, menuBtn;
    startBtn.rect = sf::FloatRect(CELL_SIZE * BOARD_WIDTH / 2 - 110, 180, 220, 48);
    startBtn.label = "Start";
    startBtn.color = sf::Color(80, 180, 80);
    startBtn.hoverColor = sf::Color(120, 220, 120);

    exitBtn.rect = sf::FloatRect(BOARD_WIDTH * CELL_SIZE / 2 - 110, 250, 220, 40);
    exitBtn.label = "Exit";
    exitBtn.color = sf::Color(180, 80, 80);
    exitBtn.hoverColor = sf::Color(220, 120, 120);

    restartBtn.rect = sf::FloatRect(BOARD_WIDTH * CELL_SIZE / 2 - 110, 180, 220, 48);
    restartBtn.label = "Restart";
    restartBtn.color = sf::Color(80, 180, 80);
    restartBtn.hoverColor = sf::Color(120, 220, 120);

    menuBtn.rect = sf::FloatRect(BOARD_WIDTH * CELL_SIZE / 2 - 110, 250, 220, 40);
    menuBtn.label = "Menu";
    menuBtn.color = sf::Color(180, 80, 80);
    menuBtn.hoverColor = sf::Color(220, 120, 120);

    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
                window.close();
            processEvent(event, &window, &startBtn, &exitBtn, &restartBtn, &menuBtn);
        }

        if (!paused && !gameOver && !inMenu)
        {
          float delay = 0.15f;
          timer += clock.restart().asSeconds();
            if (timer > delay)
            {
                update();
                timer = 0;
            }
        }
        else
        {
            clock.restart();
        }

        render(window, &startBtn, &exitBtn, &restartBtn, &menuBtn);
    }
}

// Handles all events, including mouse support for menu buttons
void Game::processEvent(const sf::Event& event, sf::RenderWindow* window,
                        MenuButton* startBtn, MenuButton* exitBtn,
                        MenuButton* restartBtn, MenuButton* menuBtn)
{
    sf::Vector2f mousePos;
    if (window)
        mousePos = window->mapPixelToCoords(sf::Mouse::getPosition(*window));

    // Reset hover state for all buttons
    if (startBtn) startBtn->hovered = false;
    if (exitBtn) exitBtn->hovered = false;
    if (restartBtn) restartBtn->hovered = false;
    if (menuBtn) menuBtn->hovered = false;

    if (inMenu)
    {
        if (startBtn && startBtn->rect.contains(mousePos))
            startBtn->hovered = true;
        if (exitBtn && exitBtn->rect.contains(mousePos))
            exitBtn->hovered = true;

        if (event.type == sf::Event::MouseButtonPressed && event.mouseButton.button == sf::Mouse::Left)
        {
            if (startBtn && startBtn->rect.contains(mousePos))
            {
                inMenu = false;
                gameOver = false;
                paused = false;
                reset();
                return;
            }
            if (exitBtn && exitBtn->rect.contains(mousePos))
            {
                if (window) window->close();
                return;
            }
        }
        if (event.type == sf::Event::KeyPressed)
        {
            if (event.key.code == sf::Keyboard::Enter)
            {
                inMenu = false;
                gameOver = false;
                paused = false;
                reset();
            }
            else if (event.key.code == sf::Keyboard::Escape)
            {
                if (window) window->close();
            }
        }
        return;
    }

    if (gameOver)
    {
        if (restartBtn && restartBtn->rect.contains(mousePos))
            restartBtn->hovered = true;
        if (menuBtn && menuBtn->rect.contains(mousePos))
            menuBtn->hovered = true;

        if (event.type == sf::Event::MouseButtonPressed && event.mouseButton.button == sf::Mouse::Left)
        {
            if (restartBtn && restartBtn->rect.contains(mousePos))
            {
                reset();
                gameOver = false;
                paused = false;
                inMenu = false;
                return;
            }
            if (menuBtn && menuBtn->rect.contains(mousePos))
            {
                inMenu = true;
                return;
            }
        }
        if (event.type == sf::Event::KeyPressed)
        {
            if (event.key.code == sf::Keyboard::Enter)
            {
                reset();
                gameOver = false;
                paused = false;
                inMenu = false;
            }
            else if (event.key.code == sf::Keyboard::Escape)
            {
                inMenu = true;
            }
        }
        return;
    }

    // Snake controls
    if (event.type == sf::Event::KeyPressed)
    {
        switch (event.key.code)
        {
            case sf::Keyboard::W:
            case sf::Keyboard::Up:
                if (snake.getDirection() != Direction::DOWN)
                    snake.setDirection(Direction::UP);
                paused = false;
                break;
            case sf::Keyboard::S:
            case sf::Keyboard::Down:
                if (snake.getDirection() != Direction::UP)
                    snake.setDirection(Direction::DOWN);
                paused = false;
                break;
            case sf::Keyboard::A:
            case sf::Keyboard::Left:
                if (snake.getDirection() != Direction::RIGHT)
                    snake.setDirection(Direction::LEFT);
                paused = false;
                break;
            case sf::Keyboard::D:
            case sf::Keyboard::Right:
                if (snake.getDirection() != Direction::LEFT)
                    snake.setDirection(Direction::RIGHT);
                paused = false;
                break;
            case sf::Keyboard::Space:
                paused = !paused;
                break;
            case sf::Keyboard::Q:
            case sf::Keyboard::Escape:
                inMenu = true;
                break;
            default:
                break;
        }
    }
}

// Rendering with interactive menu buttons and centered text
void Game::render(sf::RenderWindow& window,
                  MenuButton* startBtn, MenuButton* exitBtn,
                  MenuButton* restartBtn, MenuButton* menuBtn) const {
    // Draw gradient background
    sf::VertexArray background(sf::Quads, 4);
    background[0].position = sf::Vector2f(0, 0);
    background[1].position = sf::Vector2f(window.getSize().x, 0);
    background[2].position = sf::Vector2f(window.getSize().x, window.getSize().y);
    background[3].position = sf::Vector2f(0, window.getSize().y);
    background[0].color = sf::Color(40, 40, 60);
    background[1].color = sf::Color(40, 40, 60);
    background[2].color = sf::Color(20, 20, 30);
    background[3].color = sf::Color(20, 20, 30);
    window.draw(background);

    // Draw game border
    sf::RectangleShape border(sf::Vector2f(BOARD_WIDTH * CELL_SIZE, BOARD_HEIGHT * CELL_SIZE));
    border.setPosition(0, 0);
    border.setFillColor(sf::Color::Transparent);
    border.setOutlineThickness(3);
    border.setOutlineColor(sf::Color(180, 180, 220));
    window.draw(border);

    // Draw food with highlight
    sf::CircleShape foodCircle(CELL_SIZE / 2 - 3);
    foodCircle.setFillColor(sf::Color(255, 80, 80));
    foodCircle.setOutlineThickness(2);
    foodCircle.setOutlineColor(sf::Color(255, 180, 180));
    foodCircle.setOrigin(foodCircle.getRadius(), foodCircle.getRadius());
    foodCircle.setPosition(food.getPosition().x * CELL_SIZE + CELL_SIZE / 2, food.getPosition().y * CELL_SIZE + CELL_SIZE / 2);
    window.draw(foodCircle);

    // Draw snake
    for (const auto& segment : snake.getBody())
    {
        sf::RectangleShape cell(sf::Vector2f(CELL_SIZE - 4, CELL_SIZE - 4));
        cell.setPosition(segment.x * CELL_SIZE + 2, segment.y * CELL_SIZE + 2);
        if (segment == snake.getHead()) {
            cell.setFillColor(sf::Color(80, 255, 80));
            cell.setOutlineThickness(2);
            cell.setOutlineColor(sf::Color(40, 180, 40));
        } else {
            cell.setFillColor(sf::Color(100, 200, 100));
            cell.setOutlineThickness(1);
            cell.setOutlineColor(sf::Color(60, 120, 60));
        }
        window.draw(cell);
    }

    // Helper for drawing shadowed text
    auto drawShadowedText = [&](sf::RenderTarget& target, const sf::Text& text, sf::Color shadowColor = sf::Color(0,0,0,120), float offset = 2.f) {
        sf::Text shadow = text;
        shadow.setFillColor(shadowColor);
        shadow.move(offset, offset);
        target.draw(shadow);
        target.draw(text);
    };

    static sf::Font font;
    static bool fontLoaded = false;
    if (!fontLoaded) {
        fontLoaded = font.loadFromFile("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf");
    }

    if (fontLoaded)
    {
        sf::Text text;
        text.setFont(font);
        text.setCharacterSize(22);
        text.setFillColor(sf::Color::White);

        if (inMenu)
        {
            // Title
            text.setString("SNAKE GAME");
            text.setCharacterSize(38);
            text.setStyle(sf::Text::Bold);
            sf::FloatRect titleRect = text.getLocalBounds();
            text.setOrigin(titleRect.left + titleRect.width / 2.0f, titleRect.top + titleRect.height / 2.0f);
            text.setPosition(BOARD_WIDTH * CELL_SIZE / 2.0f, 80);
            drawShadowedText(window, text);

            // Best score
            text.setString("Best score: " + std::to_string(bestScore));
            text.setCharacterSize(22);
            text.setStyle(sf::Text::Regular);
            sf::FloatRect bestRect = text.getLocalBounds();
            text.setOrigin(bestRect.left + bestRect.width / 2.0f, bestRect.top + bestRect.height / 2.0f);
            text.setPosition(BOARD_WIDTH * CELL_SIZE / 2.0f, 130);
            drawShadowedText(window, text);

            // Start button
            sf::RectangleShape startShape(sf::Vector2f(startBtn->rect.width, startBtn->rect.height));
            startShape.setPosition(startBtn->rect.left, startBtn->rect.top);
            startShape.setFillColor(startBtn->hovered ? startBtn->hoverColor : startBtn->color);
            startShape.setOutlineThickness(3);
            startShape.setOutlineColor(sf::Color(40, 120, 40));
            window.draw(startShape);

            text.setString("Start");
            text.setCharacterSize(24);
            text.setFillColor(sf::Color::White);
            sf::FloatRect startTextRect = text.getLocalBounds();
            text.setOrigin(startTextRect.left + startTextRect.width / 2.0f, startTextRect.top + startTextRect.height / 2.0f);
            text.setPosition(
                startBtn->rect.left + startBtn->rect.width / 2.0f,
                startBtn->rect.top + startBtn->rect.height / 2.0f
            );
            drawShadowedText(window, text);

            // Exit button
            sf::RectangleShape exitShape(sf::Vector2f(exitBtn->rect.width, exitBtn->rect.height));
            exitShape.setPosition(exitBtn->rect.left, exitBtn->rect.top);
            exitShape.setFillColor(exitBtn->hovered ? exitBtn->hoverColor : exitBtn->color);
            exitShape.setOutlineThickness(3);
            exitShape.setOutlineColor(sf::Color(120, 40, 40));
            window.draw(exitShape);

            text.setString("Exit");
            text.setCharacterSize(22);
            text.setFillColor(sf::Color::White);
            sf::FloatRect exitTextRect = text.getLocalBounds();
            text.setOrigin(exitTextRect.left + exitTextRect.width / 2.0f, exitTextRect.top + exitTextRect.height / 2.0f);
            text.setPosition(
                exitBtn->rect.left + exitBtn->rect.width / 2.0f,
                exitBtn->rect.top + exitBtn->rect.height / 2.0f
            );
            drawShadowedText(window, text);
        }
        else if (gameOver)
        {
            // Game Over title
            text.setString("GAME OVER");
            text.setCharacterSize(38);
            text.setStyle(sf::Text::Bold);
            text.setFillColor(sf::Color(255, 80, 80));
            sf::FloatRect overRect = text.getLocalBounds();
            text.setOrigin(overRect.left + overRect.width / 2.0f, overRect.top + overRect.height / 2.0f);
            text.setPosition(BOARD_WIDTH * CELL_SIZE / 2.0f, 80);
            drawShadowedText(window, text, sf::Color(0,0,0,180), 3.f);

            // Score info
            text.setString("Score: " + std::to_string(score) + "   Best: " + std::to_string(bestScore));
            text.setCharacterSize(22);
            text.setStyle(sf::Text::Regular);
            text.setFillColor(sf::Color::White);
            sf::FloatRect scoreRect = text.getLocalBounds();
            text.setOrigin(scoreRect.left + scoreRect.width / 2.0f, scoreRect.top + scoreRect.height / 2.0f);
            text.setPosition(BOARD_WIDTH * CELL_SIZE / 2.0f, 130);
            drawShadowedText(window, text);

            // Restart button
            sf::RectangleShape restartShape(sf::Vector2f(restartBtn->rect.width, restartBtn->rect.height));
            restartShape.setPosition(restartBtn->rect.left, restartBtn->rect.top);
            restartShape.setFillColor(restartBtn->hovered ? restartBtn->hoverColor : restartBtn->color);
            restartShape.setOutlineThickness(3);
            restartShape.setOutlineColor(sf::Color(40, 120, 40));
            window.draw(restartShape);

            text.setString("Restart");
            text.setCharacterSize(24);
            text.setFillColor(sf::Color::White);
            sf::FloatRect restartTextRect = text.getLocalBounds();
            text.setOrigin(restartTextRect.left + restartTextRect.width / 2.0f, restartTextRect.top + restartTextRect.height / 2.0f);
            text.setPosition(
                restartBtn->rect.left + restartBtn->rect.width / 2.0f,
                restartBtn->rect.top + restartBtn->rect.height / 2.0f
            );
            drawShadowedText(window, text);

            // Menu button
            sf::RectangleShape menuShape(sf::Vector2f(menuBtn->rect.width, menuBtn->rect.height));
            menuShape.setPosition(menuBtn->rect.left, menuBtn->rect.top);
            menuShape.setFillColor(menuBtn->hovered ? menuBtn->hoverColor : menuBtn->color);
            menuShape.setOutlineThickness(3);
            menuShape.setOutlineColor(sf::Color(120, 40, 40));
            window.draw(menuShape);

            text.setString("Menu");
            text.setCharacterSize(22);
            text.setFillColor(sf::Color::White);
            sf::FloatRect menuTextRect = text.getLocalBounds();
            text.setOrigin(menuTextRect.left + menuTextRect.width / 2.0f, menuTextRect.top + menuTextRect.height / 2.0f);
            text.setPosition(
                menuBtn->rect.left + menuBtn->rect.width / 2.0f,
                menuBtn->rect.top + menuBtn->rect.height / 2.0f
            );
            drawShadowedText(window, text);
        }
        else
        {
            // Score bar
            text.setString("Score: " + std::to_string(score) + "   Best: " + std::to_string(bestScore) +
                           (paused ? "   [PAUSED]" : ""));
            text.setCharacterSize(22);
            text.setFillColor(sf::Color::White);
            text.setStyle(sf::Text::Regular);
            text.setPosition(5, BOARD_HEIGHT * CELL_SIZE + 8);
            drawShadowedText(window, text);

            // Controls
            text.setString("WASD / Arrows - Move   SPACE - Pause   ESC - Menu");
            text.setCharacterSize(16);
            text.setFillColor(sf::Color(180, 180, 220));
            text.setPosition(5, BOARD_HEIGHT * CELL_SIZE + 36);
            drawShadowedText(window, text, sf::Color(0,0,0,100), 1.5f);
        }
    }

    window.display();
}

void Game::update()
{
    snake.updateDirection();
    Point newHead = getTeleportedHead();

    if (snake.checkSelfCollision(newHead))
    {
        gameOver = true;
        if (score > bestScore)
        {
            bestScore = score;
            saveBestScore();
        }
        return;
    }

    if (newHead == food.getPosition())
    {
        snake.grow(newHead);
        score += 10;
        generateFood();
    }
    else
    {
        snake.move(newHead);
    }
}

Point Game::getTeleportedHead() const
{
    Point newHead = snake.getNextHeadPosition();
    if (newHead.x < 0) newHead.x = BOARD_WIDTH - 1;
    else if (newHead.x >= BOARD_WIDTH) newHead.x = 0;
    if (newHead.y < 0) newHead.y = BOARD_HEIGHT - 1;
    else if (newHead.y >= BOARD_HEIGHT) newHead.y = 0;
    return newHead;
}

void Game::generateFood()
{
    std::vector<Point> occupiedPositions(snake.getBody().begin(), snake.getBody().end());
    food.generateNew(BOARD_WIDTH, BOARD_HEIGHT, occupiedPositions);
}

void Game::loadBestScore()
{
    std::ifstream in(BEST_SCORE_FILE);
    if (in)
        in >> bestScore;
    else
        bestScore = 0;
}

void Game::saveBestScore() {
  std::ofstream out(BEST_SCORE_FILE);
  if (out)
    out << bestScore;
}

void Game::reset()
{
    snake = Snake(Point(BOARD_WIDTH / 2, BOARD_HEIGHT / 2));
    score = 0;
    generateFood();
}
