#include <extra2d/graphics/backends/opengl/gl_sprite_batch.h>
#include <extra2d/graphics/backends/opengl/gl_texture.h>
#include <extra2d/graphics/shader/shader_manager.h>
#include <extra2d/utils/logger.h>

namespace extra2d {

GLSpriteBatch::GLSpriteBatch()
    : vao_(0), currentTexture_(nullptr), drawCallCount_(0) {}

GLSpriteBatch::~GLSpriteBatch() { shutdown(); }

bool GLSpriteBatch::init() {
  // 从ShaderManager获取精灵着色器
  shader_ = ShaderManager::getInstance().getBuiltin("sprite");
  if (!shader_) {
    E2D_LOG_ERROR("Failed to get builtin sprite shader");
    return false;
  }

  // 创建 VAO
  glGenVertexArrays(1, &vao_);
  glBindVertexArray(vao_);

  // 初始化 VBO（顶点缓冲区）- 动态使用模式
  BufferDesc vboDesc;
  vboDesc.type = BufferType::Vertex;
  vboDesc.usage = BufferUsage::Dynamic;
  vboDesc.size = SpriteBatch::MAX_VERTICES * sizeof(SpriteVertex);
  vboDesc.initialData = nullptr;
  if (!vbo_.init(vboDesc)) {
    E2D_LOG_ERROR("Failed to initialize sprite batch VBO");
    return false;
  }
  vbo_.bind();

  // 设置顶点属性
  glEnableVertexAttribArray(0);
  glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(SpriteVertex),
                        reinterpret_cast<void *>(offsetof(SpriteVertex, position)));

  glEnableVertexAttribArray(1);
  glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(SpriteVertex),
                        reinterpret_cast<void *>(offsetof(SpriteVertex, texCoord)));

  glEnableVertexAttribArray(2);
  glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(SpriteVertex),
                        reinterpret_cast<void *>(offsetof(SpriteVertex, color)));

  // 初始化 EBO（索引缓冲区）- 静态使用模式
  BufferDesc eboDesc;
  eboDesc.type = BufferType::Index;
  eboDesc.usage = BufferUsage::Static;
  eboDesc.size = batch_.getIndices().size() * sizeof(uint16_t);
  eboDesc.initialData = batch_.getIndices().data();
  if (!ebo_.init(eboDesc)) {
    E2D_LOG_ERROR("Failed to initialize sprite batch EBO");
    return false;
  }
  ebo_.bind();

  glBindVertexArray(0);

  return true;
}

void GLSpriteBatch::shutdown() {
  // 使用 GLBuffer::shutdown() 释放缓冲区资源
  vbo_.shutdown();
  ebo_.shutdown();

  if (vao_ != 0) {
    glDeleteVertexArrays(1, &vao_);
    vao_ = 0;
  }
}

void GLSpriteBatch::begin(const glm::mat4 &viewProjection) {
  batch_.begin(viewProjection);
  batches_.clear();
  currentTexture_ = nullptr;
  drawCallCount_ = 0;
  // 保存 viewProjection 矩阵供后续使用
  viewProjection_ = viewProjection;

  // 绑定 VAO 和缓冲区
  glBindVertexArray(vao_);
  vbo_.bind();
  ebo_.bind();
}

void GLSpriteBatch::end() {
  if (batch_.getSpriteCount() > 0) {
    flush();
  }

  // 解绑缓冲区
  vbo_.unbind();
  ebo_.unbind();
  glBindVertexArray(0);
}

void GLSpriteBatch::draw(const Texture &texture, const SpriteData &data) {
  const GLTexture *glTex = dynamic_cast<const GLTexture *>(&texture);
  if (!glTex) {
    E2D_LOG_WARN("Invalid texture type for sprite batch");
    return;
  }

  // 如果纹理改变或批次已满，先提交当前批次
  if (currentTexture_ != glTex || batch_.needsFlush()) {
    if (batch_.getSpriteCount() > 0) {
      submitBatch();
    }
    currentTexture_ = glTex;
  }

  // 使用 batch 层生成顶点
  batch_.draw(data);
}

void GLSpriteBatch::drawBatch(const Texture &texture,
                              const std::vector<SpriteData> &sprites) {
  const GLTexture *glTex = dynamic_cast<const GLTexture *>(&texture);
  if (!glTex) {
    E2D_LOG_WARN("Invalid texture type for sprite batch");
    return;
  }

  // 批量处理精灵
  for (const auto &data : sprites) {
    // 如果纹理改变或批次已满，先提交当前批次
    if (currentTexture_ != glTex || batch_.needsFlush()) {
      if (batch_.getSpriteCount() > 0) {
        submitBatch();
      }
      currentTexture_ = glTex;
    }

    // 使用 batch 层生成顶点
    batch_.draw(data);
  }
}

void GLSpriteBatch::submitBatch() {
  if (batch_.getSpriteCount() == 0) {
    return;
  }

  // 记录批次信息
  Batch batchInfo;
  batchInfo.texture = currentTexture_;
  batchInfo.startVertex = 0; // 每次提交都是新的缓冲区
  batchInfo.vertexCount = batch_.getSpriteCount() * 4;
  batches_.push_back(batchInfo);

  // 绑定着色器并设置uniform
  if (shader_) {
    shader_->bind();
    // 设置视图投影矩阵
    shader_->setMat4("u_viewProjection", viewProjection_);
    // 设置模型矩阵为单位矩阵（精灵位置已经在顶点生成时计算）
    glm::mat4 model(1.0f);
    shader_->setMat4("u_model", model);
    // 设置纹理采样器
    shader_->setInt("u_texture", 0);
    // 设置透明度
    shader_->setFloat("u_opacity", 1.0f);
  }

  // 上传顶点数据 - 使用 orphaning 策略优化动态缓冲区
  // 通过传入 nullptr 进行 orphaning，告诉驱动器可以丢弃旧缓冲区并分配新内存
  // 这样可以避免 GPU 等待，提高性能
  size_t vertexDataSize = batch_.getVertices().size() * sizeof(SpriteVertex);
  vbo_.setData(nullptr, vertexDataSize);  // orphaning
  vbo_.updateData(batch_.getVertices().data(), 0, vertexDataSize);

  // 绘制
  currentTexture_->bind(0);
  
  size_t indexCount = batch_.getSpriteCount() * 6;
  glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(indexCount),
                 GL_UNSIGNED_SHORT, nullptr);

  drawCallCount_++;

  // 清空 batch 层，准备下一批
  batch_.clear();
}

void GLSpriteBatch::flush() {
  // 提交最后的批次
  if (batch_.getSpriteCount() > 0) {
    submitBatch();
  }

  // 重置状态
  batches_.clear();
  currentTexture_ = nullptr;
}

} // namespace extra2d
