#include <cmath>
#include <sstream>

#include "PixelBuffer.hpp" // hoffmanncl72341
#include "Platform_Win32Console.hpp" // hoffmanncl72341
#include "Level.hpp" // kruegerzo72182
#include "Enemy.hpp" // hauerch71498
#include "Bomber.hpp" // hauerch71498
#include "Tank.hpp" // hauerch71498
#include "Trooper.hpp" // hauerch71498
#include "Enemyprojectile.hpp" // hauerch71498
#include "player.hpp" // weberma73121

//constexpr double PI = 3.14159265358979323846;
// <kruegerzo72182
bool debug = false;
float trooperTimer = 0.0f;
float bomberTimer = 25.0f;
int screenWidth = 320;
int screenHeigth = 320;

// global helper funktion to add "buildings"
void addBuilding(float x, float y, int size, Level &level_ptr)
{
    int thickness = 3;
    // adds a Wall to the level (length, thickness, position X, position Y, orientation)
    level_ptr.addWall(size, thickness, x, y, Orientation::Horizontal);
    level_ptr.addWall(size, thickness, x, y + size - thickness, Orientation::Horizontal);
    level_ptr.addWall(size, thickness, x, y, Orientation::Vertical);
    level_ptr.addWall(size, thickness, x + size - thickness, y, Orientation::Vertical);
}
// kruegerzo72182>
// <hoffmanncl72341
class GameLoop {
    int frame = 0;
    FloatSeconds time_elapsed{ 0.0f };

    DynamicPixelBuffer framebuffer = {screenWidth, screenHeigth};

public:
    void run(Platform& platform, FloatSeconds const &frame_time, Level &level, Player &player_) {
        for(int y = 0; y < framebuffer.h(); ++y) {
            for(int x = 0; x < framebuffer.w(); ++x) {
                framebuffer.at({ x, y }) =
                    (y == 0 || y == (framebuffer.h() - 1)) ? _t :
                    (x == 0 || x == (framebuffer.w() - 1)) ? _t :
                    _0;
            }
        }

        if(frame_time.count() != 0.0f) {
            if(!debug){
                platform.set_title(
                    "Bestestes ASCII Shooter Ever - " +
                     std::to_string(std::lround(1.0f / frame_time.count())) +
                     " FPS"
                );
            }
            else {
                platform.set_title(
                    "Player X: " +
                     std::to_string(std::lround(player_.getLeft())) +
                     " --- Player Y: " +
                     std::to_string(std::lround(player_.getTop()))
                );
            }
        }
        // hoffmanncl72341>
        // <kruegerzo72182
        //Spawn the troopers from the trooperspawner
        trooperTimer = trooperTimer + frame_time.count();
        if(level.getEnemySpawnCount() != 0){
            if(trooperTimer > 3.0f){
                level.enemyVector_PushBack(level.getTrooperSpawn());
                trooperTimer = 0.0f;
            }
        }
        //Spawn bombers from different directions endlessly
        bomberTimer = bomberTimer + frame_time.count();
        if(bomberTimer > 30.0f){
            int direction = rand() % 4;
            float bombX;
            float bombY;
            int offset = 20;
            switch ( direction ) {
                case 1: //Up
                    bombX = player_.getTopLeft().X;
                    bombY = static_cast<float>(screenHeigth + offset);
                    break;
                case 2: //left
                    bombX = static_cast<float>(screenWidth + offset);
                    bombY = player_.getTopLeft().Y;
                    break;
                case 3: //right
                    bombX = static_cast<float>(-offset);
                    bombY = player_.getTopLeft().Y;
                    break;
                default: //Down
                    bombX = player_.getTopLeft().X;
                    bombY = static_cast<float>(-offset);
                    break;
            }
            level.enemyVector_PushBack(std::make_unique<Bomber>(Bomber(bombX, bombY, direction, 0)));
            bomberTimer = 0.0f;
        }
        // kruegerzo72182>

        if(!level.GameOverFlag) //Hauerch71498
        {
        // <kruegerzo72182
        // player movement
        player_.move(platform, frame_time, level);

        // enemy movement
        level.doDoSteps(frame_time);
        }
        // Output to the console, Gamelogic should happen before this
        framebuffer.blit_topleft({ 0, 0 }, level.draw());
        // kruegerzo72182>
        // <hoffmanncl72341
        platform.render(framebuffer);

        ++frame;
        time_elapsed += frame_time;
    }
};

int run(Platform &platform)
{
    // hoffmanncl72341>
    // add the player
    Player player(15, 15);

    GameLoop gameloop;

    // build the level layout
    Level level1(screenWidth, screenHeigth, player);
    // adds a Wall to the level (length, thickness, position X, position Y, orientation)
    // buildings (global function, see on top)
    addBuilding(100, 100, 20, level1);
    addBuilding(130, 100, 25, level1);
    addBuilding(100, 130, 20, level1);
    addBuilding(135, 135, 30, level1);
    addBuilding(135, 165, 20, level1);
    addBuilding(10, 200, 15, level1);
    addBuilding(285, 270, 15, level1);
    // adds a scenery object to the level (position X, position Y, scenerytype)
    level1.addScenery(150, 80, SceneryType::Dead_Tree);
    level1.addScenery(50, 200, SceneryType::Dead_Tree);
    level1.addScenery(280, 290, SceneryType::Dead_Tree);
    level1.addScenery(200, 220, SceneryType::Dead_Tree);
    level1.addScenery(40, 15, SceneryType::Rock);
    level1.addScenery(40, 15, SceneryType::Rock);
    level1.addScenery(270, 65, SceneryType::Rock);
    level1.addScenery(230, 135, SceneryType::Rock);
    level1.addScenery(30, 90, SceneryType::Rock);
    level1.addScenery(100, 40, SceneryType::Rubble);
    level1.addScenery(250, 190, SceneryType::Rubble);
    level1.addScenery(60, 270, SceneryType::Rubble);
    level1.addScenery(205, 250, SceneryType::Rubble);
    level1.addScenery(200, 100, SceneryType::Bush);
    level1.addScenery(180, 280, SceneryType::Bush);
    level1.addScenery(240, 275, SceneryType::Bush);
    level1.addScenery(270, 240, SceneryType::Bush);
    level1.addScenery(100, 250, SceneryType::Bush);
    level1.addScenery(280, 165, SceneryType::Bush);
    level1.addScenery(245, 25, SceneryType::Bush);

    // add enemies to the level
    level1.addEnemy(std::make_unique<Tank>(Tank(50, 150, 0, 0)));
    level1.addEnemy(std::make_unique<Tank>(Tank(200, 160, 2, 0)));
    level1.fillTrooperSpawner(120, 170, 4);
    // kruegerzo72182>
    // <hoffmanncl72341
    while(true) {
        platform.frame([&](FloatSeconds const &frame_time) {
            gameloop.run(platform, frame_time, level1, player);
        });
    }
    return 0;
}

int main()
{
    auto platform = Platform_Win32Console::init();
    run(platform);
    platform.exit();
}
// hoffmanncl72341>