#include "frame_buffer.h"
#include "logger.h"

bool init_frame_buffer(Frame_Buffer *frame_buffer, u32 width, u32 height) {
  frame_buffer->width = width;
  frame_buffer->height = height;

  glGenFramebuffers(1, &frame_buffer->buffer);
  glBindFramebuffer(GL_FRAMEBUFFER, frame_buffer->buffer);
  glEnable(GL_FRAMEBUFFER_SRGB);

  glGenTextures(1, &frame_buffer->color_tex);
  glBindTexture(GL_TEXTURE_2D, frame_buffer->color_tex);
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);

  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  glBindTexture(GL_TEXTURE_2D, 0);

  glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, frame_buffer->color_tex, 0);
  log(1, "%s: added color buffer\n", __FUNCTION__);

  glGenRenderbuffers(1, &frame_buffer->depth_buffer);
  glBindRenderbuffer(GL_RENDERBUFFER, frame_buffer->depth_buffer);
  glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height);
  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, frame_buffer->depth_buffer);
  glBindRenderbuffer(GL_RENDERBUFFER, 0);
  log(1, "%s: added depth render buffer\n", __FUNCTION__);

  glBindFramebuffer(GL_FRAMEBUFFER, 0);

  return check_frame_buffer_complete(frame_buffer);
}

bool check_frame_buffer_complete(Frame_Buffer *frame_buffer) {
  glBindFramebuffer(GL_FRAMEBUFFER, frame_buffer->buffer);

  GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
  if (result != GL_FRAMEBUFFER_COMPLETE) {
    log(1, "%s error: framebuffer is NOT complete\n", __FUNCTION__);
    return false;
  }

  glBindFramebuffer(GL_FRAMEBUFFER, 0);
    log(1, "%s: framebuffer is complete\n", __FUNCTION__);
  return true;
}

bool resize_frame_buffer(Frame_Buffer *frame_buffer, u32 width, u32 height) {
  frame_buffer->width = width;
  frame_buffer->height = height;

  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
  glDeleteTextures(1, &frame_buffer->color_tex);
  glDeleteRenderbuffers(1, &frame_buffer->depth_buffer);
  glDeleteFramebuffers(1, &frame_buffer->buffer);

  return init_frame_buffer(frame_buffer, width, height);
}

void draw_frame_buffer(Frame_Buffer *frame_buffer) {
  glBindFramebuffer(GL_READ_FRAMEBUFFER, frame_buffer->buffer);
  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
  glBlitFramebuffer(0, 0, frame_buffer->width, frame_buffer->height, 0, 0, frame_buffer->width, frame_buffer->height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
  glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
}

void bind_frame_buffer(Frame_Buffer *frame_buffer) {
  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frame_buffer->buffer);
}

void unbind_frame_buffer(Frame_Buffer *frame_buffer) {
  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
}

void cleanup_frame_buffer(Frame_Buffer *frame_buffer) {
  unbind_frame_buffer(frame_buffer);

  glDeleteTextures(1, &frame_buffer->color_tex);
  glDeleteRenderbuffers(1, &frame_buffer->depth_buffer);
  glDeleteFramebuffers(1, &frame_buffer->buffer);
}
