#include "world.hpp"

#include <window.h>
#include <resources.h>
#include "entity/entity.hpp"

#include "game.h"

#include <app.h>
#include <imgui/imgui.h>

namespace game {

World::World()
{
    this->physic = new b2World(b2Vec2(0, 0));

    // this->physic->SetDebugDraw(&this->debugger);
    this->debugger.SetFlags(this->debugger.e_shapeBit);

    this->physic->SetContactListener(&this->collider);

    this->player = nullptr;

    this->fxCollection.load("data/fx/game.fx.xml");
    this->bgEffect = this->fxCollection.createEffect("e1");
    this->bgEffect->pos = vec2f{20, 0};

    this->SetupWalls();

    this->Restart();

    resources::atlas.getIcon("gameover.png", this->gameOverIcon);
    this->gameOverIcon.scale.x = 1 / 4.0;
    this->gameOverIcon.scale.y = 1 / 4.0;
}

World::~World()
{
    delete this->physic;
    delete this->bgEffect;
}

void World::Show() noexcept
{
    this->bgEffect->start();
}

void World::Hide() noexcept
{
    this->bgEffect->stop();
}

void World::Restart()
{
    for (Block *block: this->blocks) {
        delete block;
    }

    for (entity::Entity *entity: this->entities) {
        delete entity;
    }

    this->entitiesForRemove.clear();
    this->entities.clear();
    this->blocks.clear();

    this->scores = 0;
    this->blocksPassed = 0;

    if (this->player) {
        delete player;
    }

    this->player = new entity::Hero(this);
    this->blocks.push_back(new Block(vec2f{30, 0}, this));
    this->blocks.push_back(new Block(vec2f{60, 0}, this));

    this->isGameOver = false;
}

b2Body* World::CreateBody(const vec2f &position, b2BodyType type) noexcept
{
    b2BodyDef definition;
    definition.type = type;
    definition.position.Set(position.x, position.y);
    definition.angle = 0;
    definition.fixedRotation = true;

    return this->physic->CreateBody(&definition);
}

void World::DeleteBody(b2Body *body) noexcept
{
    this->physic->DestroyBody(body);
}

void World::AddManagedEntity(entity::Entity *entity) noexcept
{
    this->entities.insert(entity);
}

void World::RemoveManagedEntity(entity::Entity *entity) noexcept
{
    this->entitiesForRemove.insert(entity);
}

void World::Update() noexcept
{
    if (this->isGameOver) {

        if (App::GetInput()->IsControlJustPressed(INPUT_RESTART)) {
            this->Restart();
        }

        return;
    }

    this->physic->Step(window::deltaTime, 8, 8);

    this->player->Update();

    for (entity::Entity *entity: this->entitiesForRemove) {
        if (this->entities.erase(entity)) {
            delete entity;
        }
    }

    this->entitiesForRemove.clear();

    for (entity::Entity *entity: this->entities) {
        entity->Update();
    }

    for (Block *block: this->blocks) {
        if (block->IsLeftScreen()) {
            delete block;
            this->blocks.remove(block);
            this->blocks.push_back(new Block(vec2f{30, 0}, this));
            this->blocksPassed++;
            break;
        }
    }

    bool moving = true;

    for (const Block *block: this->blocks) {
        if (block->IsStoped()) {
            moving = false;
            break;
        }
    }

    for (Block *block: this->blocks) {
        block->Update(moving);
    }

    this->bgEffect->update();
}

vec2f World::GetPlayerPosition() const noexcept
{
    return this->player->GetPosition();
}

void World::GameOver()
{
    this->isGameOver = true;
}

void World::Render() const noexcept
{
    this->bgEffect->draw();

    ImGui::SetNextWindowSize(ImVec2(300, 40), ImGuiCond_Always);
    ImGui::SetNextWindowPos(ImVec2(860, 40), ImGuiCond_Always);
    ImGui::Begin("a", nullptr, ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoDecoration);
    ImGui::Text("%d", this->scores);
    ImGui::End();

    this->player->Render();

    for (entity::Entity *entity: this->entities) {
        entity->Render();
    }

    if (this->isGameOver) {
        this->gameOverIcon.draw(vec2f{15, 8.5});
    }

    this->physic->DrawDebugData();
}

int World::GetDifficult() const noexcept
{
    int val = this->blocksPassed + 10 + scores;

    if (val > 1000) {
        val = 1000 + (val - 1000) / 4;
    }

    if (val > 2000) {
        return 2000;
    }

    return val;
}

void World::AddScores(int value) noexcept
{
    this->scores += value;
}

void World::SetupWalls()
{
    b2Body *body = this->CreateBody(vec2f(), b2_staticBody);

    b2PolygonShape shape;
    b2FixtureDef fixDef;
    fixDef.filter.categoryBits = TYPE_WALL;
    fixDef.filter.maskBits = TYPE_HERO;
    fixDef.shape = &shape;
    fixDef.density = 1.0f;

    shape.SetAsBox(0.5, 20, b2Vec2(25, 8.5), 0);
    body->CreateFixture(&fixDef);

    // fixDef.filter.categoryBits = 0b11001;
    fixDef.filter.maskBits = TYPE_HERO | TYPE_BULLET | TYPE_EBULLET;

    shape.SetAsBox(40, 0.5, b2Vec2(20, 17.5), 0);
    body->CreateFixture(&fixDef);

    shape.SetAsBox(40, 0.5, b2Vec2(20, -0.5), 0);
    body->CreateFixture(&fixDef);


    shape.SetAsBox(1, 20, b2Vec2(-1, 8.5), 0);
    body->CreateFixture(&fixDef);

    shape.SetAsBox(0.5, 20, b2Vec2(35, 8.5), 0);
    body->CreateFixture(&fixDef);

}

}

