#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include <list>
#include <cmath>
#include <iostream>

#include "arkanoid.hpp"
#include "bonus.hpp"

const double pi = 3.14159265358979323846;

void Arkanoid::addRandomBonus(sf::Vector2f position)
{
    if (m_bonuses.size() > kMaxNumBonuses)
        return;
    int max_rand = 10000;
    if ((rand() % max_rand) * 1.0f / max_rand < m_bonusProbability) 
    {
        int max = 4; int min = 1;
        int range = max - min + 1;
        int num = rand() % range + min;
        switch (num) {
            case 1:
                  m_bonuses.push_back(new TripleBallBonus(position));
                  break;        
            case 2:
                  m_bonuses.push_back(new EnlargePaddleBonus(position));
                  break;
            case 3:
                  m_bonuses.push_back(new ShrinkPaddleBonus(position));
                  break;
            case 4:
                  m_bonuses.push_back(new SlowingBonus(position));
                  break;
        }
    }
}

// Функция, которая обрабатывает все столкновения шарика 
void Arkanoid::handleBallCollisions(Ball& ball) 
{
    ball.handleWallsCollision(m_border);
    ball.handlePaddleCollision(m_paddle);

    auto indexes = ball.handleBrickGridCollision(m_brickGrid);
    if (indexes.first == -1)
        return;
    m_brickGrid.deactivateBrick(indexes);
    addRandomBonus(ball.position);
}


Arkanoid::Arkanoid(sf::FloatRect border, sf::Font& font) :
    m_time{0.0},
    m_border{border},
    m_paddle{{m_border.left + m_border.width / 2, m_border.top + m_border.height - 100}, {120, 20}},
    m_gameState{GameState::stuck},
    m_numLives{7}
{
    float gap = border.width / 10;
    m_brickGrid = BrickGrid({border.left + gap, border.top + gap, border.width - 2 * gap, border.height / 2}, 50, 30);
    m_bonusProbability = 0.1;

    m_endText.setFont(font);
    m_endText.setString("You Win!");
    m_endText.setCharacterSize(100);
    m_endText.setFillColor(sf::Color::White);
    sf::FloatRect textRect = m_endText.getLocalBounds();
    m_endText.setOrigin(textRect.left + textRect.width / 2.0f, textRect.top  + textRect.height / 2.0f);
    m_endText.setPosition({border.left + border.width / 2, border.top + border.height / 2});
}

sf::FloatRect Arkanoid::getBorder() const
{
    return m_border;
}

const Paddle& Arkanoid::getPaddle() const
{
    return m_paddle;
}

const BrickGrid& Arkanoid::getBrickGrid() const
{
    return m_brickGrid;
}

void Arkanoid::addBall(const Ball& ball)
{
    if (m_balls.size() < kMaxNumBalls)
        m_balls.push_back(ball);
}

bool Arkanoid::isMaxBalls()
{
    return m_balls.size() == kMaxNumBalls - 1;
}

// Эта функция вызывается каждый кадр
void Arkanoid::update(const sf::RenderWindow& window, float dt)
{
    m_time += dt;

    // Устанавливаем положение ракетки
    sf::Vector2f mousePosition = window.mapPixelToCoords(sf::Mouse::getPosition(window));
    m_paddle.position.x = mousePosition.x;

    // Обрабатываем шарики
    for (std::list<Ball>::iterator it = m_balls.begin(); it != m_balls.end();)
    {
        (*it).update(dt);
        handleBallCollisions(*it);
        if ((*it).position.y > m_border.top + m_border.height)
        {
            it = m_balls.erase(it);
        }
        else
        {
            it++;
        }
    }

    // Если шариков нет, то переходи в режим начала игры и уменьшаем кол-во жизней
    if (m_gameState == GameState::running && m_balls.size() == 0)
    {
        m_effects.clear();
        m_gameState = GameState::stuck;
        m_numLives--;
    }

    // Если жизни кончились, то переходим в состояние конца игры (проигрыш)
    if (m_numLives < 0)
    {
        m_gameState = GameState::endLose;
    }

    // Если блоки кончились, то переходим в состояние конца игры (победа)
    if (m_brickGrid.getNumActiveBricks() == 0)
    {
        m_gameState = GameState::endWin;
    }
    
    // Обрабатываем бонусы
    for (auto it = m_bonuses.begin(); it != m_bonuses.end();)
    {
        (*it)->update(dt);
        if ((*it)->isColiding(m_paddle))
        {
            (*it)->activate(*this);
            delete *it;
            it = m_bonuses.erase(it);
        }
        else if ((*it)->m_position.y > m_border.top + m_border.height)
        { 
            delete (*it);
            it = m_bonuses.erase(it);
        }
        else
        {
            it++;
        }
        
    }
    /* Обработка эффектов */
    for (auto it = m_effects.begin(); it != m_effects.end();)
    {
        if ((*it)->isExpired(m_time))
        {
            (*it)->deactivate(*this);
            delete *it;
            it = m_effects.erase(it);
        }
        else {
            it++;
        }
    }
}

void Arkanoid::draw(sf::RenderWindow& window)
{
    // Рисуем задний прямоугольник
    static sf::RectangleShape background {{m_border.width, m_border.height}};
    background.setPosition({m_border.left, m_border.top});
    background.setFillColor(kBackgroundColor);
    window.draw(background);

    // Рисуем блоки
    m_brickGrid.draw(window);

    // Рисуем шарики
    for (Ball& ball : m_balls)
    {
        ball.draw(window);
    }

    // Рисуем ракетку
    m_paddle.draw(window);

    // Если мы в режиме начала игры, то рисуем шарик на ракетке
    if (m_gameState == GameState::stuck)
    {
        m_initialBall.position = {m_paddle.position.x, m_paddle.position.y - m_paddle.size.y / 2 - m_initialBall.radius};
        m_initialBall.position = {m_paddle.position.x, m_paddle.position.y - m_paddle.size.y / 2 - m_initialBall.radius};
        m_initialBall.draw(window);
    }

    // Рисуем кол-во жизней вверху слева
    for (int i = 0; i < m_numLives; i++)
    {
        m_initialBall.position = {m_initialBall.radius * (3 * i + 2), 2 * m_initialBall.radius};
        m_initialBall.draw(window);
    }

    // Рисуем бонусы
    for (Bonus* pbonus : m_bonuses)
    {
        pbonus->draw(window);
    }

    // При завершении игры рисуем надпись
    if (m_gameState == GameState::endWin)
    {
        m_endText.setString("You Win!");
        window.draw(m_endText);
    }

    // При завершении игры рисуем надпись
    if (m_gameState == GameState::endLose)
    {
        m_endText.setString("You Lose!");
        window.draw(m_endText);
    }
}

void Arkanoid::onMousePressed(sf::Event& event)
{
    switch (m_gameState)
    {
        case GameState::stuck:
            if (event.mouseButton.button == sf::Mouse::Left)
            {
                m_gameState = GameState::running;
                float velocityAngle = (rand() % 100 + 40) * pi / 180;
                float velocityNorm = Ball::initialVelocity;
                sf::Vector2f newPosition = {m_paddle.position.x, m_paddle.position.y - m_paddle.size.y / 2.0f - m_initialBall.radius};
                sf::Vector2f newVelocity = {-velocityNorm * cosf(velocityAngle), -velocityNorm * sinf(velocityAngle)};
                addBall({m_initialBall.radius, newPosition, newVelocity});
            }
            break;

        case GameState::running:
            break;
        case GameState::endLose:
            break;
        case GameState::endWin:
            break;
    }
}
