#include "SDL_log.h"
#include <SDL2/SDL.h>
#include <frostbite2D/graphics/renderer.h>
#include <glad/glad.h>
#include <glm/gtc/type_ptr.hpp>

namespace frostbite2D {

Renderer& Renderer::get() {
  static Renderer instance;
  return instance;
}

Renderer::Renderer() = default;

// 移除析构函数中的自动销毁，改为在 Application::shutdown() 中手动调用

bool Renderer::init() {
  if (initialized_) {
    return true;
  }
  
  if (!batch_.init()) {
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize batch system");
    return false;
  }

  //初始化着色器管理器
  if (!shaderManager_.init("assets/shaders")) {
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize shader manager");
    return false;
  }
  
  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  
  SDL_Log("Renderer initialized");
  initialized_ = true;
  return true;
}

void Renderer::shutdown() {
  batch_.shutdown();
  shaderManager_.shutdown();
  initialized_ = false;
}

void Renderer::beginFrame() {
  glViewport(viewportX_, viewportY_, viewportWidth_, viewportHeight_);
  glClearColor(clearColor_[0] / 255.0f, clearColor_[1] / 255.0f, 
                clearColor_[2] / 255.0f, clearColor_[3] / 255.0f);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  
  updateUniforms();
  batch_.begin();
}

void Renderer::endFrame() {
  batch_.end();
}

void Renderer::flush() {
  batch_.flush();
}

void Renderer::setViewport(int x, int y, int width, int height) {
  viewportX_ = x;
  viewportY_ = y;
  viewportWidth_ = width;
  viewportHeight_ = height;
  
  if (camera_) {
    camera_->setViewport(width, height);
  }
}

void Renderer::setClearColor(float r, float g, float b, float a) {
  clearColor_[0] = static_cast<uint8>(r * 255);
  clearColor_[1] = static_cast<uint8>(g * 255);
  clearColor_[2] = static_cast<uint8>(b * 255);
  clearColor_[3] = static_cast<uint8>(a * 255);
}

void Renderer::setClearColor(const Color& color) {
  clearColor_[0] = color.r;
  clearColor_[1] = color.g;
  clearColor_[2] = color.b;
  clearColor_[3] = color.a;
}

void Renderer::clear(uint32_t flags) {
  glClear(flags);
}

void Renderer::setCamera(Camera* camera) {
  camera_ = camera;
  if (camera) {
    camera_->setViewport(viewportWidth_, viewportHeight_);
  }
}

void Renderer::setupBlendMode(BlendMode mode) {
  switch (mode) {
    case BlendMode::None:
      glDisable(GL_BLEND);
      glBlendEquation(GL_FUNC_ADD);
      break;
    case BlendMode::Normal:
      glEnable(GL_BLEND);
      glBlendEquation(GL_FUNC_ADD);
      glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
                          GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
      break;
    case BlendMode::Additive:
      glEnable(GL_BLEND);
      glBlendEquation(GL_FUNC_ADD);
      glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE,
                          GL_ONE, GL_ONE);
      break;
    case BlendMode::Screen:
      glEnable(GL_BLEND);
      glBlendEquation(GL_FUNC_ADD);
      glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_COLOR,
                          GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
      break;
    case BlendMode::Premultiplied:
      glEnable(GL_BLEND);
      glBlendEquation(GL_FUNC_ADD);
      glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA,
                          GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
      break;
    case BlendMode::Subtractive:
      glEnable(GL_BLEND);
      glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
                          GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
      glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
      break;
    case BlendMode::Multiply:
      glEnable(GL_BLEND);
      glBlendEquation(GL_FUNC_ADD);
      glBlendFuncSeparate(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA,
                          GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
      break;
  }
}

void Renderer::updateUniforms() {
  if (camera_) {
    auto* spriteShader = shaderManager_.getShader("sprite");
    if (spriteShader) {
      spriteShader->use();
      spriteShader->setMat4("u_view", camera_->getViewMatrix());
      spriteShader->setMat4("u_projection", camera_->getProjectionMatrix());
    }
    
    auto* coloredShader = shaderManager_.getShader("colored_quad");
    if (coloredShader) {
      coloredShader->use();
      coloredShader->setMat4("u_view", camera_->getViewMatrix());
      coloredShader->setMat4("u_projection", camera_->getProjectionMatrix());
    }
  }
}

void Renderer::drawQuad(const Vec2& pos, const Size& size, float cr, float cg, 
                         float cb, float ca) {
  Rect rect(pos.x, pos.y, size.width, size.height);
  Quad quad = Quad::create(rect, cr, cg, cb, ca);
  Transform2D transform = Transform2D::identity();
  
  auto* shader = shaderManager_.getShader("colored_quad");
  if (shader) {
    shader->use();
  }
  
  batch_.submitQuad(quad, transform, nullptr, shader, BlendMode::Normal);
}

void Renderer::drawQuad(const Vec2& pos, const Size& size, Ptr<Texture> texture) {
  Rect rect(pos.x, pos.y, size.width, size.height);
  Quad quad = Quad::createTextured(rect, Rect(0, 0, size.width, size.height),
                                     Vec2(size.width, size.height));
  Transform2D transform = Transform2D::identity();
  
  auto* shader = shaderManager_.getShader("sprite");
  if (shader) {
    shader->use();
  }
  
  batch_.submitQuad(quad, transform, texture, shader, BlendMode::Normal);
}

void Renderer::drawQuad(const Rect& rect, const Color& color) {
  drawQuad(Vec2(rect.left(), rect.top()), 
            Size(rect.width(), rect.height()),
            color.r, color.g, color.b, color.a);
}

void Renderer::drawSprite(const Vec2& pos, const Size& size, Ptr<Texture> texture) {
  drawQuad(pos, size, texture);
}

void Renderer::drawSprite(const Vec2& pos, const Rect& srcRect, const Vec2& texSize,
                         Ptr<Texture> texture, const Color& color) {
  Rect destRect(pos.x, pos.y, srcRect.width(), srcRect.height());
  Quad quad = Quad::createTextured(destRect, srcRect, texSize,
                                     color.r, color.g, color.b, color.a);
  Transform2D transform = Transform2D::identity();
  
  auto* shader = shaderManager_.getShader("sprite");
  if (shader) {
    shader->use();
  }
  
  batch_.submitQuad(quad, transform, texture, shader, BlendMode::Normal);
}

}
