//
// Created by jovian on 18/07/17.
//

#include <iostream>
#include "GameCore.h"
#include "Control/MouseCtrl.h"
#include "Control/JoyPadCtrl.h"
#include "Physics/b2Angle.h"

GameCore::GameCore()
        : m_world(nullptr), m_rend(nullptr) {}

GameCore::~GameCore() {
    // Destroy physic world
    if (m_world != nullptr) {
        delete m_world;
        m_world = nullptr;
    }

    // Destroy SDL renderer
    if (m_rend != nullptr) {
        delete m_rend;
        m_rend = nullptr;
    }
}

bool GameCore::initialize() {
    // Already initialized
    if (m_world != nullptr)
        return false;

    // Error check
    bool okay(true);

    // Create physic world
    b2Vec2 gravity(0.0f, 0.0f);
    m_world = new TinyWorld(gravity);

    // Create display
    m_rend = new Renderer;
    okay = okay && m_rend->initialize(2);

    // Create hardware interface
    m_input = new InputAndJoy;
    m_input->setWindow(m_rend->getWindow());

    // End
    return okay;
}

void GameCore::startQuickGame() {
    // Time
    Uint32 frameRate(60); // Frame per second
    Uint32 prevTime(0); // Previous chrono
    Uint32 waitTime(1000 / frameRate); // Time to wait between each frame
    Uint32 osBuffer(4); // To prevent SDL_Delay mistake : high > less mistake, more CPU usage

    float timeStep(1.0f / frameRate);
    int32 velocityIterations = 8;
    int32 positionIterations = 3;

    // Textures
    std::vector<Visual *> myScope, hisScope;

    // Create main character slot
    HumanSoldier *myGunner(nullptr);
    HumanSoldier *hisGunner(nullptr);

    // Create controller, game-pad if exists
    Controller *myCtrl(nullptr);
    Controller *hisCtrl(nullptr);

    if (m_input->getNbPads() == 0) {
        myCtrl = new MouseCtrl(m_input);
        hisCtrl = new MouseCtrl(m_input);
    } else if (m_input->getNbPads() == 1) {
        myCtrl = new MouseCtrl(m_input);
        hisCtrl = new JoyPadCtrl(m_input);
    } else {
        myCtrl = new JoyPadCtrl(m_input, 0);
        hisCtrl = new JoyPadCtrl(m_input, 1);
    }

    // Visor
    float visorAngle;
    b2Vec2 visorPos;

    // Create physical area
    m_world->createProceduralWorld();

    // Main loop
    while (!m_input->isFinished() && !m_input->getKey(SDL_SCANCODE_ESCAPE)) {
        // Update events
        m_input->updateEvents();
        myCtrl->refresh();
        hisCtrl->refresh();

        // New game
        if (m_input->getInstantKey(SDL_SCANCODE_P)) {
            // Disable focus on gunners
            myGunner = nullptr;
            hisGunner = nullptr;

            // Clear entities
            m_world->clearEveryEntity();

            // Recreate area
            m_world->createProceduralWorld();
        }

        // Update physic
        m_world->Step(timeStep, velocityIterations, positionIterations);

        if (myGunner == nullptr || !myGunner->isExist()) {
            myGunner = new HumanSoldier(m_world, myCtrl, b2Vec2(9.0f, -6.8f), 0);
            m_world->addEntity(myGunner);
        }

        if (hisGunner == nullptr || !hisGunner->isExist()) {
            hisGunner = new HumanSoldier(m_world, hisCtrl, b2Vec2(0.0f, -2.8f), 1);
            m_world->addEntity(hisGunner);
        }

        m_world->updateAll(); // Clean dead people, including my Gunner

        // Clear visuals
        clearVisuals(myScope);
        clearVisuals(hisScope);

        // Gather visible entities
        m_world->collectVisuals(myScope, myGunner->getPos(), m_rend->computeDiago(myGunner->getZoom(), 0));
        m_world->collectVisuals(hisScope, hisGunner->getPos(), m_rend->computeDiago(hisGunner->getZoom(), 1));

        // Display visor and life
        visorPos = 1.2f * myCtrl->getVisor() + myGunner->getPos();
        visorAngle = b2Angle(myCtrl->getVisor(), b2Vec2(0.0f, -1.0f));
        myScope.push_back(new Visual(11, visorPos, visorAngle));
        myScope.push_back(myGunner->makeLifeBar());

        visorPos = 1.2f * hisCtrl->getVisor() + hisGunner->getPos();
        visorAngle = b2Angle(hisCtrl->getVisor(), b2Vec2(0.0f, -1.0f));
        hisScope.push_back(new Visual(11, visorPos, visorAngle));
        hisScope.push_back(hisGunner->makeLifeBar());

        // Rendering
        m_rend->clearWindow();

        m_rend->renderScene(myScope, myGunner->getPos(), myGunner->getZoom());
        m_rend->renderScene(hisScope, hisGunner->getPos(), hisGunner->getZoom(), 1);

        m_rend->presentWindow();

        // todo Remove debug
        /*Uint32 debug_conso(SDL_GetTicks() - prevTime);
        std::cout << "Time use : " << debug_conso << std::endl;*/

        // Pause
        if (SDL_GetTicks() + osBuffer < prevTime + waitTime)
            SDL_Delay(waitTime + prevTime - SDL_GetTicks() - osBuffer);

        while (SDL_GetTicks() < prevTime + waitTime) {}

        prevTime = SDL_GetTicks();
    }

    // Destruction
    delete myCtrl;
    delete hisCtrl;
}