#include "radiosity.h"

RadiosityScene::RadiosityScene() : m_vbo(0), m_vao(0), m_pDrawProg(nullptr)
{

}

void RadiosityScene::initGL()
{
  m_pDrawProg   = std::make_unique<ShaderProgram>("../main/DrawPatch.vert", "../main/DrawPatch.geom", "../main/DrawPatch.frag");
  m_pDrawIdProg = std::make_unique<ShaderProgram>("../main/DrawPatchId.vert", "../main/DrawPatchId.geom", "../main/DrawPatchId.frag");
}

RadiosityScene::~RadiosityScene()
{
  if (m_vao != 0)
    glDeleteVertexArrays(1, &m_vao);

  if (m_vbo != 0)
    glDeleteBuffers(1, &m_vbo);
}

void RadiosityScene::CopyToGL()
{
  if (m_patches.size() == 0 || m_pDrawProg == nullptr)
    return;

  if (m_vbo == 0)
    glGenBuffers(1, &m_vbo);

  glBindBuffer(GL_ARRAY_BUFFER, m_vbo);                                                                      CHECK_GL_ERRORS;
  glBufferData(GL_ARRAY_BUFFER, m_patches.size() * sizeof(PatchGeom), (GLfloat*)&m_patches[0], GL_STATIC_DRAW);  CHECK_GL_ERRORS;

  if (m_vao == 0)
  {
    glGenVertexArrays(1, &m_vao);

    // bind vertex params to vao
    //
    glBindVertexArray(m_vao);

    GLint vminLoc = glGetAttribLocation(m_pDrawProg->program, "vmin");
    GLint vmaxLoc = glGetAttribLocation(m_pDrawProg->program, "vmax");
    GLint normLoc = glGetAttribLocation(m_pDrawProg->program, "normal");
    GLint colorLoc = glGetAttribLocation(m_pDrawProg->program, "color");

    glBindBuffer(GL_ARRAY_BUFFER, m_vbo); CHECK_GL_ERRORS;

    if (vminLoc >= 0)
    {
      glEnableVertexAttribArray(vminLoc);                                            CHECK_GL_ERRORS;
      glVertexAttribPointer(vminLoc, 3, GL_FLOAT, GL_FALSE, sizeof(PatchGeom), 0);   CHECK_GL_ERRORS;
    }

    if (vmaxLoc >= 0)
    {
      glEnableVertexAttribArray(vmaxLoc);                                                                  CHECK_GL_ERRORS;
      glVertexAttribPointer(vmaxLoc, 3, GL_FLOAT, GL_FALSE, sizeof(PatchGeom), (void*)(sizeof(float3)));   CHECK_GL_ERRORS;
    }

    if (normLoc >= 0)
    {
      glEnableVertexAttribArray(normLoc);                                                                     CHECK_GL_ERRORS;
      glVertexAttribPointer(normLoc, 3, GL_FLOAT, GL_FALSE, sizeof(PatchGeom), (void*)(2 * sizeof(float3)));  CHECK_GL_ERRORS;
    }

    if (colorLoc >= 0)
    {
      glEnableVertexAttribArray(colorLoc);                                                                     CHECK_GL_ERRORS;
      glVertexAttribPointer(colorLoc, 3, GL_FLOAT, GL_FALSE, sizeof(PatchGeom), (void*)(3 * sizeof(float3)));  CHECK_GL_ERRORS;
    }

    glBindVertexArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
  }
}

void RadiosityScene::Draw(float4x4 worldView, float4x4 projection)
{
  glUseProgram(m_pDrawProg->program);  CHECK_GL_ERRORS;

  setUniform(m_pDrawProg->program, "modelViewMatrix", worldView);
  setUniform(m_pDrawProg->program, "projectionMatrix", projection);  // set matrix we have calculated in "reshape" funtion

  glBindVertexArray(m_vao);                           CHECK_GL_ERRORS;
  glDrawArrays(GL_POINTS, 0, int(m_patches.size()));  CHECK_GL_ERRORS;
}

void RadiosityScene::DrawIndices(float4x4 worldView, float4x4 projection)
{
  glUseProgram(m_pDrawIdProg->program);  CHECK_GL_ERRORS;

  setUniform(m_pDrawIdProg->program, "modelViewMatrix",  worldView);
  setUniform(m_pDrawIdProg->program, "projectionMatrix", projection);  // set matrix we have calculated in "reshape" funtion

  glBindVertexArray(m_vao);                           CHECK_GL_ERRORS;
  glDrawArrays(GL_POINTS, 0, int(m_patches.size()));  CHECK_GL_ERRORS;
}

