#include "mesh.h"

Mesh::Mesh() {
  VAO = 0;
  VBO = 0;
  IBO = 0;
  index_count = 0;
}

Mesh::~Mesh() {
  clear_mesh(this);
}

void create_mesh(
  Mesh *mesh,
  f32 *vertices,
  u32 *indices,
  u32 vertices_count,
  u32 indices_count
) {
  mesh->index_count = indices_count;

  glGenVertexArrays(1, &mesh->VAO);
  glBindVertexArray(mesh->VAO);

  glGenBuffers(1, &mesh->IBO);
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->IBO);
  glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices[0]) * indices_count, indices, GL_STATIC_DRAW);

  glGenBuffers(1, &mesh->VBO);
  glBindBuffer(GL_ARRAY_BUFFER, mesh->VBO);
  glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * vertices_count, vertices, GL_STATIC_DRAW);

  // Position vertices
  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(vertices[0]) * 8, 0);
  glEnableVertexAttribArray(0);

  // Texture coordinates
  glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertices[0]) * 8, (void *)(sizeof(vertices[0]) * 3));
  glEnableVertexAttribArray(1);

  glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(vertices[0]) * 8, (void *)(sizeof(vertices[0]) * 5));
  glEnableVertexAttribArray(2);

  // Unbinding
  glBindBuffer(GL_ARRAY_BUFFER, 0);
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
  glBindVertexArray(0);
}

void render_mesh(Mesh *mesh) {
  glBindVertexArray(mesh->VAO);
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->IBO);
  glDrawElements(GL_TRIANGLES, mesh->index_count, GL_UNSIGNED_INT, 0);

  // Unbinding
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
  glBindVertexArray(0);
}

void clear_mesh(Mesh *mesh) {
  if (mesh->IBO != 0) {
    glDeleteBuffers(1, &mesh->IBO);
    mesh->IBO = 0;
  }

  if (mesh->VBO != 0) {
    glDeleteBuffers(1, &mesh->VBO);
    mesh->VBO = 0;
  }

  if (mesh->VAO != 0) {
    glDeleteVertexArrays(1, &mesh->VAO);
    mesh->VAO = 0;
  }

  mesh->index_count = 0;
}

void calculate_normals_avg(f32 *vertices, u32 vertices_count, u32 vertices_length, u32 *indices, u32 indices_count, u32 normal_offset) {
  for (size_t i = 0; i< indices_count; i+=3) {
    u32 in0 = indices[i]   * vertices_length;
    u32 in1 = indices[i+1] * vertices_length;
    u32 in2 = indices[i+2] * vertices_length;

    Vector3 v1;
    v1.x = vertices[in1]   - vertices[in0];
    v1.y = vertices[in1+1] - vertices[in0+1];
    v1.z = vertices[in1+2] - vertices[in0+2];

    Vector3 v2;
    v2.x = vertices[in2]   - vertices[in0];
    v2.y = vertices[in2+1] - vertices[in0+1];
    v2.z = vertices[in2+2] - vertices[in0+2];
    
    Vector3 normal = glm::cross(v1, v2);
    normal = glm::normalize(normal);

    in0 += normal_offset;
    in1 += normal_offset;
    in2 += normal_offset;

    vertices[in0]   += normal.x;
    vertices[in0+1] += normal.y;
    vertices[in0+2] += normal.z;

    vertices[in1]   += normal.x;
    vertices[in1+1] += normal.y;
    vertices[in1+2] += normal.z;

    vertices[in2]   += normal.x;
    vertices[in2+1] += normal.y;
    vertices[in2+2] += normal.z;
  }

  for(size_t i=0; i<vertices_count/vertices_length; i++) {
    u32 n_offset = i * vertices_length + normal_offset;
    Vector3 vector(vertices[n_offset], vertices[n_offset+1], vertices[n_offset+2]);
    vector = glm::normalize(vector);

    vertices[n_offset]   = vector.x;
    vertices[n_offset+1] = vector.y;
    vertices[n_offset+2] = vector.z;
  }
}

