#include <frostbite2D/2d/text_sprite.h>
#include <frostbite2D/graphics/font_manager.h>
#include <frostbite2D/graphics/texture.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <vector>

namespace frostbite2D {

namespace {

Uint32 readSurfacePixel(const SDL_Surface* surface, int x, int y) {
  const uint8* row =
      static_cast<const uint8*>(surface->pixels) + y * surface->pitch;
  const uint8* pixel = row + x * surface->format->BytesPerPixel;

  switch (surface->format->BytesPerPixel) {
    case 1:
      return *pixel;
    case 2:
      return *reinterpret_cast<const uint16*>(pixel);
    case 3:
      if (SDL_BYTEORDER == SDL_BIG_ENDIAN) {
        return (pixel[0] << 16) | (pixel[1] << 8) | pixel[2];
      }
      return pixel[0] | (pixel[1] << 8) | (pixel[2] << 16);
    case 4:
      return *reinterpret_cast<const uint32*>(pixel);
    default:
      return 0;
  }
}

} // namespace

TextSprite::TextSprite()
  : Sprite() {
}

TextSprite::~TextSprite() {
}

Ptr<TextSprite> TextSprite::create() {
  return MakePtr<TextSprite>();
}

Ptr<TextSprite> TextSprite::create(const std::string& text, const std::string& fontName) {
  auto textSprite = MakePtr<TextSprite>();
  textSprite->SetFont(fontName);
  textSprite->SetText(text);
  return textSprite;
}

void TextSprite::SetText(const std::string& text) {
  if (text_ != text) {
    text_ = text;
    RenderText();
  }
}

void TextSprite::SetFont(const std::string& fontName) {
  if (fontName_ != fontName) {
    fontName_ = fontName;
    RenderText();
  }
}

void TextSprite::SetTextColor(const Color& color) {
  textColor_ = color;
  RenderText();
}

void TextSprite::SetTextColor(float r, float g, float b, float a) {
  textColor_ = Color(r, g, b, a);
  RenderText();
}

void TextSprite::SetAlign(Align align) {
  if (align_ != align) {
    align_ = align;
    RenderText();
  }
}

void TextSprite::RenderText() {
  if (text_.empty() || fontName_.empty()) {
    return;
  }

  FontManager& fontMgr = FontManager::get();
  TTF_Font* font = fontMgr.getFont(fontName_);
  if (!font) {
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Font '%s' not found for text rendering", fontName_.c_str());
    return;
  }

  SDL_Color sdlColor;
  sdlColor.r = static_cast<Uint8>(textColor_.r * 255.0f);
  sdlColor.g = static_cast<Uint8>(textColor_.g * 255.0f);
  sdlColor.b = static_cast<Uint8>(textColor_.b * 255.0f);
  sdlColor.a = static_cast<Uint8>(textColor_.a * 255.0f);

  SDL_Surface* surface = TTF_RenderUTF8_Blended(font, text_.c_str(), sdlColor);
  if (!surface) {
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to render text: %s", TTF_GetError());
    return;
  }

  int width = surface->w;
  int height = surface->h;

  std::vector<uint8> rgbaData(width * height * 4);
  if (SDL_MUSTLOCK(surface) && SDL_LockSurface(surface) != 0) {
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to lock text surface: %s",
                 SDL_GetError());
    SDL_FreeSurface(surface);
    return;
  }

  for (int y = 0; y < height; ++y) {
    for (int x = 0; x < width; ++x) {
      Uint8 r = 0;
      Uint8 g = 0;
      Uint8 b = 0;
      Uint8 a = 0;
      Uint32 pixel = readSurfacePixel(surface, x, y);
      SDL_GetRGBA(pixel, surface->format, &r, &g, &b, &a);

      size_t offset = static_cast<size_t>((y * width + x) * 4);
      rgbaData[offset + 0] = r;
      rgbaData[offset + 1] = g;
      rgbaData[offset + 2] = b;
      rgbaData[offset + 3] = a;
    }
  }

  if (SDL_MUSTLOCK(surface)) {
    SDL_UnlockSurface(surface);
  }

  SDL_FreeSurface(surface);

  Ptr<Texture> texture = Texture::createFromMemory(
    rgbaData.data(),
    width,
    height,
    4
  );

  if (texture) {
    texture->setSampling(TextureSampling::Linear);
    SetTexture(texture);
    SetSizeToTexture();
  }
}

Vec2 TextSprite::GetTextSize() const {
  if (text_.empty() || fontName_.empty()) {
    return Vec2::Zero();
  }

  FontManager& fontMgr = FontManager::get();
  TTF_Font* font = fontMgr.getFont(fontName_);
  if (!font) {
    return Vec2::Zero();
  }

  int w = 0;
  int h = 0;
  TTF_SizeUTF8(font, text_.c_str(), &w, &h);
  return Vec2(static_cast<float>(w), static_cast<float>(h));
}

void TextSprite::updateTexture() {
  RenderText();
}

}
