/*
 * Renderer.cpp
 *
 *  Created on: 4 nov. 2020
 *      Author: simon
 */

#ifndef ANDROID
#include <glbinding/Binding.h>
#endif
#include "render/Renderer.h"
#include "render/GL.h"
#include <stdio.h>
#include "misc/Time.h"
#include "misc/Easing.h"

namespace td {
namespace render {

Renderer::Renderer() {

}

Renderer::~Renderer() {

}

void Renderer::updateIsometricView() {
    float isometricEased = utils::easeInOutExpo(m_IsometricShade);
    m_WorldShader->start();
    m_WorldShader->setIsometricView(isometricEased);
    m_EntityShader->start();
    m_EntityShader->setIsometricView(isometricEased);
}

void Renderer::initShader() {
    m_WorldShader = std::make_unique<WorldShader>();
    m_WorldShader->loadShader();
    m_EntityShader = std::make_unique<EntityShader>();
    m_EntityShader->loadShader();
    setIsometricView(true);
    updateIsometricView();
}

bool Renderer::init() {
#ifndef ANDROID
    glbinding::Binding::initialize();
#endif
    glEnable(GL_TEXTURE_2D);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    initShader();
    return true;
}

void Renderer::renderVAO(const GL::VertexArray& vao) {
    m_WorldShader->start();
    vao.bind();
    glDrawArrays(GL_TRIANGLES, 0, vao.getVertexCount());
    vao.unbind();
}

void Renderer::renderModel(const Model& model) {
    m_EntityShader->start();
    m_EntityShader->setModelPos(model.positon);
    model.vao->bind();
    glDrawArrays(GL_TRIANGLES, 0, model.vao->getVertexCount());
    model.vao->unbind();
}

void Renderer::updateIsometricFade() {
    static std::uint64_t lastTime = utils::getTime();
    if (m_IsometricShade != (float)m_IsometricView) {
        float step = (float)(utils::getTime() - lastTime) / 1000.0f * m_AnimationSpeed;
        if (m_IsometricShade < m_IsometricView) {
            m_IsometricShade += step;
        } else {
            m_IsometricShade -= step;
        }
        m_IsometricShade = std::min(m_IsometricShade, 1.0f);
        m_IsometricShade = std::max(m_IsometricShade, 0.0f);
        updateIsometricView();
    }
    lastTime = utils::getTime();
}

void Renderer::prepare() {
    glClear(GL_COLOR_BUFFER_BIT);
    glClearColor(0, 0, 0, 0);
    updateIsometricFade();
}

void Renderer::resize(int width, int height) {
    m_WorldShader->start();
    m_WorldShader->setAspectRatio((float)width / height);
    m_EntityShader->start();
    m_EntityShader->setAspectRatio((float)width / height);
    glViewport(0, 0, width, height);
}

void Renderer::setZoom(float zoom) {
    m_WorldShader->start();
    m_WorldShader->setZoom(zoom);
    m_EntityShader->start();
    m_EntityShader->setZoom(zoom);
}

void Renderer::setCamMovement(const glm::vec2& mov) {
    m_CamPos.x += mov.x * (1 - m_IsometricView) + (0.5 * mov.x - mov.y) * m_IsometricView;
    m_CamPos.y += -mov.y * (1 - m_IsometricView) + (-0.5 * mov.x - mov.y) * m_IsometricView;
    setCamPos(m_CamPos);
}

void Renderer::setCamPos(const glm::vec2& newPos) {
    m_CamPos = newPos;
    m_WorldShader->start();
    m_WorldShader->setCamPos(newPos);
    m_EntityShader->start();
    m_EntityShader->setCamPos(newPos);
}

void Renderer::setIsometricView(bool isometric) {
    m_IsometricView = isometric;
}

glm::vec2 Renderer::getCursorWorldPos(const glm::vec2& cursorPos, float aspectRatio, float zoom, float windowWidth, float windowHeight) {
    float isometricEased = utils::easeInOutExpo(m_IsometricShade);

    float relativeX = (cursorPos.x / windowWidth * 2) - 1;
    float relativeY = (cursorPos.y / windowHeight * 2) - 1;

    float deltaX = relativeX * aspectRatio / zoom;
    float deltaY = relativeY / zoom;

    float worldX = m_CamPos.x + deltaX * (1 - isometricEased) + (0.5 * deltaX + deltaY) * isometricEased;
    float worldY = m_CamPos.y + deltaY * (1 - isometricEased) + (-0.5 * deltaX + deltaY) * isometricEased;

    return { worldX, worldY };
}


} // namespace render
} // namespace td
