#include <frostbite2D/2d/sprite.h>
#include <frostbite2D/core/application.h>
#include <frostbite2D/graphics/renderer.h>
#include <frostbite2D/resource/npk_archive.h>
#include <SDL2/SDL.h>
#include <algorithm>

namespace frostbite2D {

Sprite::Sprite()
  : Actor() {
}

Sprite::Sprite(Ptr<Texture> texture)
  : Actor()
  , texture_(texture) {
}

Sprite::~Sprite() {
}

Ptr<Sprite> Sprite::createFromFile(const std::string &path) {
   Ptr<Texture> texture = Texture::loadFromFile(path);
   if (!texture) {
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load texture: %s", path.c_str());
    return nullptr;
  }

  auto sprite = MakePtr<Sprite>(texture);
  sprite->SetSizeToTexture();
  return sprite;
}

Ptr<Sprite> Sprite::createFromMemory(const uint8* data, int width, int height, int channels) {
  Ptr<Texture> texture = Texture::createFromMemory(data, width, height, channels);
  if (!texture) {
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, 
                  "Failed to create texture from memory");
    return nullptr;
  }

  auto sprite = MakePtr<Sprite>(texture);
  sprite->SetSizeToTexture();
  return sprite;
}

void Sprite::Render() {
  if (!IsVisible()) {
    RenderChildren();
    return;
  }

  if (!texture_) {
    RenderChildren();
    return;
  }

  Renderer& renderer = Renderer::get();
  
  Vec2 size = GetSize();
  if (size.x == 0 || size.y == 0) {
    size = Vec2((float)texture_->getWidth(), (float)texture_->getHeight());
  }
  
  // 使用完整的变换矩阵进行渲染
  Rect destRect(0, 0, size.x, size.y);
  Rect srcRect = srcRect_;
  if (srcRect.empty()) {
    srcRect = Rect(0, 0, (float)texture_->getWidth(), (float)texture_->getHeight());
  }
  
  float worldOpacity = GetWorldOpacity();
  Quad quad = Quad::createTextured(
    destRect, srcRect, 
    Vec2((float)texture_->getWidth(), (float)texture_->getHeight()),
    color_.r, color_.g, color_.b, color_.a * worldOpacity
  );
  
  if (flippedX_) {
    std::swap(quad.vertices[0].texCoord, quad.vertices[1].texCoord);
    std::swap(quad.vertices[2].texCoord, quad.vertices[3].texCoord);
  }
  if (flippedY_) {
    std::swap(quad.vertices[0].texCoord, quad.vertices[2].texCoord);
    std::swap(quad.vertices[1].texCoord, quad.vertices[3].texCoord);
  }
  
  auto* shader = renderer.getShaderManager().getShader("sprite");
  if (shader) {
    shader->use();
  }
  
  Transform2D worldTransform = GetWorldTransform();
  if (offset_ != Vec2::Zero()) {
    worldTransform = Transform2D::translation(offset_.x, offset_.y) * worldTransform;
  }
  
  renderer.getBatch().submitQuad(quad, worldTransform, texture_, shader, blendMode_);

  RenderChildren();
}

void Sprite::SetTexture(Ptr<Texture> texture) {
  texture_ = texture;
}

void Sprite::SetShader(const std::string& shaderName) {
  shaderName_ = shaderName;
}

void Sprite::SetDefaultShader() {
  shaderName_.clear();
}

void Sprite::SetSourceRect(const Rect& rect) {
  srcRect_ = rect;
}

void Sprite::SetColor(const Color& color) {
  color_ = color;
}

void Sprite::SetColor(float r, float g, float b, float a) {
  color_ = Color(r, g, b, a);
}

void Sprite::SetFlippedX(bool flipped) {
  flippedX_ = flipped;
}

void Sprite::SetFlippedY(bool flipped) {
  flippedY_ = flipped;
}

void Sprite::SetBlendMode(BlendMode mode) {
  blendMode_ = mode;
}

void Sprite::SetSizeToTexture() {
  if (texture_) {
    SetSize(texture_->getWidth(), texture_->getHeight());
  }
}

void Sprite::SetOffset(const Vec2& offset) {
  offset_ = offset;
}

void Sprite::SetOffset(float x, float y) {
  offset_.x = x;
  offset_.y = y;
}

void Sprite::updateTransform() {
  Vec2 pos = GetPosition();
  float rotation = GetRotation();
  Vec2 scale = GetScale();

  transform_ = Transform2D::translation(pos.x, pos.y) * 
               Transform2D::rotation(rotation) * 
               Transform2D::scaling(scale.x, scale.y);
}

Shader* Sprite::getActiveShader() const {
  Renderer& renderer = Renderer::get();
  ShaderManager& shaderManager = renderer.getShaderManager();

  if (!shaderName_.empty()) {
    Shader* shader = shaderManager.getShader(shaderName_);
    if (shader && shader->isValid()) {
      return shader;
    } else {
      SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, 
                 "Shader '%s' not found or invalid, fallback to default", 
                 shaderName_.c_str());
    }
  }

  return shaderManager.getShader(DEFAULT_SHADER);
}

Quad Sprite::createQuad() const {
  Rect srcRect = srcRect_;
  if (srcRect.empty()) {
    srcRect = Rect(0, 0, texture_->getWidth(), texture_->getHeight());
  }

  Vec2 destPos(0, 0);
  Vec2 destSizeVec = GetSize();
  if (destSizeVec.x == 0) {
    destSizeVec.x = srcRect.width();
  }
  if (destSizeVec.y == 0) {
    destSizeVec.y = srcRect.height();
  }

  Rect destRect(0, 0, destSizeVec.x, destSizeVec.y);
  
  Vec2 texSize(texture_->getWidth(), texture_->getHeight());

  float worldOpacity = GetWorldOpacity();
  Quad quad = Quad::createTextured(
    destRect, srcRect, texSize,
    color_.r, color_.g, color_.b, color_.a * worldOpacity
  );

  if (flippedX_) {
    std::swap(quad.vertices[0].texCoord, quad.vertices[1].texCoord);
    std::swap(quad.vertices[2].texCoord, quad.vertices[3].texCoord);
  }
  if (flippedY_) {
    std::swap(quad.vertices[0].texCoord, quad.vertices[2].texCoord);
    std::swap(quad.vertices[1].texCoord, quad.vertices[3].texCoord);
  }

  return quad;
}

Ptr<Sprite> Sprite::createFromNpk(const std::string& imgPath, size_t frameIndex) {
  NpkArchive& npk = NpkArchive::get();
  
  auto imgOpt = npk.getImg(imgPath);
  if (imgOpt) {
    auto frameOpt = npk.getImageFrame(*imgOpt, frameIndex);
    if (frameOpt && !frameOpt->data.empty()) {
      const ImageFrame& frame = *frameOpt;
      
      std::vector<uint8> convertedData(frame.data.size());
      for (size_t i = 0; i < frame.data.size(); i += 4) {
        uint8 b = frame.data[i + 0];
        uint8 g = frame.data[i + 1];
        uint8 r = frame.data[i + 2];
        uint8 a = frame.data[i + 3];
        convertedData[i + 0] = r;
        convertedData[i + 1] = g;
        convertedData[i + 2] = b;
        convertedData[i + 3] = a;
      }
      
      auto sprite = createFromMemory(
        convertedData.data(),
        frame.width,
        frame.height,
        4
      );
      
      if (sprite) {
        sprite->SetOffset(static_cast<float>(frame.xPos), static_cast<float>(frame.yPos));
      }
      
      return sprite;
    }
  }
  
  const std::string& defaultPath = npk.getDefaultImgPath();
  if (!defaultPath.empty()) {
    auto defaultImgOpt = npk.getImg(defaultPath);
    if (defaultImgOpt) {
      auto defaultFrameOpt = npk.getImageFrame(*defaultImgOpt, npk.getDefaultImgFrame());
      if (defaultFrameOpt && !defaultFrameOpt->data.empty()) {
        const ImageFrame& frame = *defaultFrameOpt;
        
        std::vector<uint8> convertedData(frame.data.size());
        for (size_t i = 0; i < frame.data.size(); i += 4) {
          uint8 b = frame.data[i + 0];
          uint8 g = frame.data[i + 1];
          uint8 r = frame.data[i + 2];
          uint8 a = frame.data[i + 3];
          convertedData[i + 0] = r;
          convertedData[i + 1] = g;
          convertedData[i + 2] = b;
          convertedData[i + 3] = a;
        }
        
        return createFromMemory(
          convertedData.data(),
          frame.width,
          frame.height,
          4
        );
      }
    }
  }
  
  return nullptr;
}

}

