#include "renderer.h"

#include "level.h"
#include "hero.h"

constexpr unsigned int DEFAULT_CELL_WIDTH = 60;
constexpr unsigned int DEFAULT_CELL_HEIGHT = 35;
constexpr unsigned int DEFAULT_CELL_DEVIATION = 25;

constexpr unsigned int DEFAULT_WINDOW_SIDE = DEFAULT_CELL_WIDTH * 4;

Renderer::Renderer() :
    cell_width(DEFAULT_CELL_WIDTH),
    cell_height(DEFAULT_CELL_HEIGHT),
    cell_deviation(DEFAULT_CELL_DEVIATION),
    window_size(DEFAULT_WINDOW_SIDE),
    init_painter_x(60),
    init_painter_y(60),
    vertical_shift(0),
    horizontal_shift(0)
{
    font.loadFromFile("font/VeraMono.ttf");
    text_charges.setFont(font);
    text_charges.setFillColor(palette::White);
    text_charges.setCharacterSize(25);

    brush_background.setFillColor(palette::Black);
    brush_background.setPosition(0.f, 0.f);

    brush_cell.setPointCount(4);
    brush_cell.setPoint(0, sf::Vector2f(cell_deviation, 0.f));
    brush_cell.setPoint(1, sf::Vector2f(cell_deviation + cell_width, 0.f));
    brush_cell.setPoint(2, sf::Vector2f(cell_width, cell_height));
    brush_cell.setPoint(3, sf::Vector2f(0.f, cell_height));
    brush_cell.setFillColor(palette::Blue);
    brush_cell.setOutlineThickness(0);

    brush_wall.setPointCount(6); // Points 0 and 4 should be calculated each iteration of rendering
    brush_wall.setPoint(1, sf::Vector2f(cell_deviation + cell_width, 0.f));
    brush_wall.setPoint(2, sf::Vector2f(cell_width, cell_height));
    brush_wall.setPoint(3, sf::Vector2f(0.f, cell_height));
    brush_wall.setPoint(5, sf::Vector2f(cell_width, cell_height));
    brush_wall.setOutlineThickness(0);
}

bool Renderer::drawCell(const CellPtr &cell, sf::RenderWindow &main_window)
{
    vertical_shift = static_cast<float>(cell->heightShift());

    // If cell has any height value, we should draw walls for it
    if (vertical_shift > 0)
    {
        brush_wall.setPoint(0, sf::Vector2f(cell_deviation + cell_width, -vertical_shift));
        brush_wall.setPoint(4, sf::Vector2f(0.f, cell_height - vertical_shift));

        sf::Color wall_color(sf::Uint8(cell->color().r - 40), sf::Uint8(cell->color().g - 40), sf::Uint8(cell->color().b - 40));
        brush_wall.setFillColor(wall_color);
        brush_wall.setPosition(painter_x + horizontal_shift, painter_y);

        main_window.draw(brush_wall);
    }

    // Draw the top surface of the cell itself
    float final_x = painter_x + horizontal_shift;
    float final_y = painter_y - vertical_shift;

    brush_cell.setPosition(final_x, final_y);
    brush_cell.setFillColor(cell->color());

    main_window.draw(brush_cell);

    return true;
}

bool Renderer::render(const LevelPtr &level, const HeroPtr &hero, sf::RenderWindow &main_window)
{
    if (!hero || !level)
        return false;

    painter_x = init_painter_x;
    painter_y = init_painter_y;
    horizontal_shift = 0;
    vertical_shift = 0;

    brush_background.setSize({static_cast<float>(main_window.getSize().x), static_cast<float>(main_window.getSize().y)});
    main_window.draw(brush_background);

    brush_cell.setPosition(painter_x, painter_y);

    // Where is hero
    coordinate hero_row, hero_col;
    hero->position(hero_row, hero_col);

    // Draw map from 2D array
    for (coordinate c = 0; c < level->cols(); ++c)
    {
        horizontal_shift = static_cast<float>(level->cols()) * cell_deviation;

        for (coordinate r = 0; r < level->rows(); ++r)
        {
            drawCell(level->getCellAt(r, c), main_window);

            if (hero_row == r && hero_col == c)
            {
                // Draw the hero sprite
                brush_cell.setFillColor(palette::White);
                main_window.draw(brush_cell);
            }

            // Move painter to next cell of current column
            painter_y += cell_height;
            horizontal_shift -= cell_deviation;
        }

        // Move painter to next column
        painter_y  = init_painter_y;
        painter_x += cell_width;
    }

    text_charges.setPosition(50, 350);
    text_charges.setString("Available charges left: " + std::to_string(hero->charges()));
    main_window.draw(text_charges);

    return true;
}

unsigned int Renderer::windowSize() const
{
    return window_size;
}
