#include "GL/gl3w.h"
#include "GL/gl.h"
#include "basicShader.hpp"

#include "basicShaderGLSL.hpp"
#include "BehaviourScripts/Renderable.hpp"
#include "Color.hpp"

#define SHADER_VERTEX_INDEX 0
#define SHADER_COLOR_INDEX 1

#define SHADER_PACKAGE_SIZE sizeof(float) * (3 + 4)

TSE::OpenGL::BasicShader* TSE::OpenGL::BasicShader::instance = nullptr;

TSE::OpenGL::BasicShader *TSE::OpenGL::BasicShader::Instance()
{
    return instance;
}

void TSE::OpenGL::BasicShader::Destroy()
{
    if(instance != nullptr)
        delete instance;
    instance = nullptr;
}

void TSE::OpenGL::BasicShader::Init(float width, float height)
{
    std::vector<std::unique_ptr<ShaderPart>> parts;
    parts.push_back(ShaderPart::LoadFromString(vert, GL_VERTEX_SHADER));
    parts.push_back(ShaderPart::LoadFromString(frag, GL_FRAGMENT_SHADER));
    instance = new BasicShader(std::move(parts));
}

TSE::OpenGL::BasicShader::BasicShader(std::vector<std::unique_ptr<ShaderPart>> &&parts) : Shader(parts)
{
    PackageSize = SHADER_PACKAGE_SIZE;
}

void TSE::OpenGL::BasicShader::OnEnable() const
{
    glEnableVertexAttribArray(SHADER_VERTEX_INDEX);
    glVertexAttribPointer(SHADER_VERTEX_INDEX, 3, GL_FLOAT, false, SHADER_PACKAGE_SIZE, (void*)0);
    glEnableVertexAttribArray(SHADER_COLOR_INDEX);
    glVertexAttribPointer(SHADER_COLOR_INDEX, 4, GL_FLOAT, false, SHADER_PACKAGE_SIZE, (void*)(sizeof(float) * 3));
}

void TSE::OpenGL::BasicShader::OnDisable() const
{
    glDisableVertexAttribArray(SHADER_VERTEX_INDEX);
    glDisableVertexAttribArray(SHADER_COLOR_INDEX);
}

void TSE::OpenGL::BasicShader::OnFlush()
{
}

void TSE::OpenGL::BasicShader::OnDrawCall(int indexCount)
{
    glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT, NULL);
}

void TSE::OpenGL::BasicShader::OnSubmit(const Transformable &t, float *&target, TransformationStack &stack, void (*restartDrawcall)(IRenderer &), IRenderer &rnd)
{
    auto* r = dynamic_cast<Renderable*>(t.GetBehaviourScript(RENDERABLE));
    if (!r) return;


    const Vector3* verts = r->GetVertices();
    ushort vCount = r->GetVertexCount();
    const Color color = r->GetMaterial()->GetValue<Color>("mainColor");
    Matrix4x4 matr = t.GetLocalMatrix();

    stack.Push(matr);
    const Matrix4x4& top = stack.Top();

    //Todo transformable.lastmatrix hier ergänzen

    for (ushort i = 0; i < vCount; i++) {
        Vector3 p = top * verts[i];
        *target++ = p.x;
        *target++ = p.y;
        *target++ = p.z;
        *target++ = color.r;
        *target++ = color.g;
        *target++ = color.b;
        *target++ = color.a;
    }

    stack.Pop();
}
