#include "game.h"

// Constructor
Game::Game(SDL_Renderer *renderer, Screen *screen, Asset *asset, Input *input)
{
    // Copia punteros
    this->renderer = renderer;
    this->asset = asset;
    this->screen = screen;
    this->input = input;

    // Reserva memoria para los objetos
    eventHandler = new SDL_Event();
    map = new Map(asset->get("01.map"), renderer, asset);
    player = new Player(renderer, asset, input, map);
    debugText = new Text(asset->get("debug.png"), asset->get("debug.txt"), renderer);
    music = JA_LoadMusic(asset->get("music_surface.ogg").c_str());

    // Inicializa variables
    ticks = 0;
    ticksSpeed = 15;

    section.name = SECTION_PROG_GAME;
    section.subsection = SUBSECTION_GAME_PLAY;

    debug = false;
}

// Destructor
Game::~Game()
{
    delete eventHandler;
    delete map;
    delete player;
    delete debugText;
    JA_DeleteMusic(music);
}

// Bucle para el juego
section_t Game::run()
{
    JA_PlayMusic(music);

    while (section.name == SECTION_PROG_GAME)
    {
        // Sección juego jugando
        if (section.subsection == SUBSECTION_GAME_PLAY)
        {
            update();
            render();
        }
    }

    JA_StopMusic();

    return section;
}

// Actualiza el juego, las variables, comprueba la entrada, etc.
void Game::update()
{
    // Comprueba que la diferencia de ticks sea mayor a la velocidad del juego
    if (SDL_GetTicks() - ticks > ticksSpeed)
    {
        // Actualiza el contador de ticks
        ticks = SDL_GetTicks();

        // Comprueba los eventos que hay en la cola
        while (SDL_PollEvent(eventHandler) != 0)
        {
            // Evento de salida de la aplicación
            if (eventHandler->type == SDL_QUIT)
            {
                section.name = SECTION_PROG_QUIT;
                break;
            }
        }

        player->update();
        checkScreenBorders();
        map->update();
        checkInput();
    }
}

// Pinta los objetos en pantalla
void Game::render()
{
    // Prepara para dibujar el frame
    screen->start();
    screen->clean();

    // Dibuja los objetos
    map->render();
    player->render();
    renderDebugInfo();

    // Actualiza la pantalla
    screen->blit();
}

// Comprueba la entrada
void Game::checkInput()
{
    if (input->checkInput(INPUT_BUTTON_2, REPEAT_FALSE))
    {
        debug = !debug;
    }

    if (input->checkInput(INPUT_BUTTON_3, REPEAT_FALSE))
    {
        delete map;
        map = new Map(asset->get("01.map"),renderer, asset);
        delete player;
        player = new Player(renderer, asset, input, map);
    }
}

// Muestra información de depuración
void Game::renderDebugInfo()
{
    if (!debug)
    {
        return;
    }

    // Pinta la rejilla
    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 16);
    for (int i = 0; i < 240; i += 8)
    {
        SDL_RenderDrawLine(renderer, 0, i, 320, i);
    }
    for (int i = 0; i < 320; i += 8)
    {
        SDL_RenderDrawLine(renderer, i, 0, i, 240);
    }

    // Pinta el texto
    int line = 0;
    std::string text = "";
    
    text = "R - Reload player and map";
    debugText->write(1, 210, text, -1);

    text = "D - Toggle debug mode";
    debugText->write(1, 216, text, -1);

    text = std::to_string((int)player->sprite->getPosX()) + "," + std::to_string((int)player->sprite->getPosY());
    debugText->write(0, line, text, -1);

    text = "VY " + std::to_string(player->vy) + " " + std::to_string(player->jumpStrenght);
    debugText->write(0, line += 6, text, -1);

    text = "VX " + std::to_string(player->vx);
    debugText->write(0, line += 6, text, -1);

    text = "jump_pressed " + std::to_string(player->jumpPressed);
    debugText->write(0, line += 6, text, -1);

    text = "isOnFloor " + std::to_string(player->isOnFloor());
    debugText->write(0, line += 6, text, -1);

    const std::string foot_x = std::to_string(player->underFeet[0].x);
    const std::string foot_y = std::to_string(player->underFeet[0].y);
    const std::string gettile = std::to_string(player->map->getTile(player->underFeet[0]));
    text = "getTile(" + foot_x + "," + foot_y + ") = " + gettile;
    debugText->write(0, line += 6, text, -1);

    text = "state " + std::to_string(player->state);
    debugText->write(0, line += 6, text, -1);

    text = map->getRoomFileName(b_top) + " " +map->getRoomFileName(b_right) + " " +map->getRoomFileName(b_bottom) + " " +map->getRoomFileName(b_left);
    debugText->write(0, line += 6, text, -1);
}

// Cambia el mapa
bool Game::changeMap(std::string file)
{
    bool success = false;
    // En las habitaciones los limites tienen la cadena del fichero o un 0 en caso de no limitar con nada
    if (file != "0")
        // Verifica que exista el fichero que se va a cargar
        if (asset->get(file) != "")
        {
            // Elimina la habitación actual
            delete map;

            // Crea un objeto habitación nuevo a partir del fichero
            map = new Map(asset->get(file), renderer, asset);
            success = true;
        }

    return success;
}

// Comprueba si el jugador está en el borde y se ha de cambiar el mapa
void Game::checkScreenBorders()
{
    if (player->isOnScreenBorder())
    {
        if (changeMap(map->getRoomFileName(player->getBorder())))
        {
            player->setMap(map);
            player->switchBorders();
        }
    }
}