#include "rendererManager.h"

#include "../application.h"
#include "../assets/assetManager.h"

#include <iostream>

using namespace std;
using namespace quokka_gfx;
//------------------------------------------------------------
/* Constructor */
RendererManager::RendererManager(Application *app)
{
  m_app = app;
}
//------------------------------------------------------------
/* Get the underlying SDL_Renderer */
SDL_Renderer *RendererManager::getSDLRenderer() const
{
  if (!m_app || !m_app->m_sdlRenderer)
    throw std::runtime_error("SDL Renderer not initialized");

  return m_app->m_sdlRenderer;
}
//------------------------------------------------------------
/* Present the rendered content to the screen */
void RendererManager::present() const
{
  SDL_RenderPresent(getSDLRenderer());
}
//------------------------------------------------------------
/* Clear the renderer with a specific color */
void RendererManager::clear(const Color &color) const
{
  setDrawColor(color);
  SDL_RenderClear(getSDLRenderer());
}
//------------------------------------------------------------
/* Draw a rectangle */
void RendererManager::drawRect(const Rect &rect, const Color &color, const bool filled) const
{
  setDrawColor(color);
  const auto sdlRect = rect.toSDL();
  if (filled)
    SDL_RenderFillRect(getSDLRenderer(), &sdlRect);
  else
    SDL_RenderDrawRect(getSDLRenderer(), &sdlRect);
}
//------------------------------------------------------------
/* Draw a line between two points */
void RendererManager::drawLine(const Point &start, const Point &end, const Color &color) const
{
  setDrawColor(color);
  SDL_RenderDrawLine(getSDLRenderer(), start.x, start.y, end.x, end.y);
}
//------------------------------------------------------------
/* Draw a single point */
void RendererManager::drawPoint(const Point &point, const Color &color) const
{
  setDrawColor(color);
  SDL_RenderDrawPoint(getSDLRenderer(), point.x, point.y);
}
//------------------------------------------------------------
/* Draw text at a specific position */
void RendererManager::drawText(const std::string &text, const HashID &fontID, const Point &point, const Color &color, int fontSize) const
{
  const auto font = m_app->m_assetManager->getFont(fontID);

  const auto textTexture = renderTextTexture(text, font, color);
  if (!textTexture)
    return;

  int texW, texH;
  SDL_QueryTexture(textTexture.get(), nullptr, nullptr, &texW, &texH);

  const Rect destRect(point.x, point.y, texW, texH);
  drawTexture(textTexture.get(), destRect);
}
//------------------------------------------------------------
/* Draw text centered within a rectangle */
void RendererManager::drawTextCentered(const std::string &text, const HashID &fontID, const Rect &rect, const Color &color, int fontSize) const
{
  const auto font = m_app->m_assetManager->getFont(fontID);

  const auto textTexture = renderTextTexture(text, font, color);
  if (!textTexture)
    return;

  int texW, texH;
  SDL_QueryTexture(textTexture.get(), nullptr, nullptr, &texW, &texH);

  // Center the text within the given rectangle
  const int x = rect.x + (rect.w - texW) / 2;
  const int y = rect.y + (rect.h - texH) / 2;

  const Rect destRect(x, y, texW, texH);
  drawTexture(textTexture.get(), destRect);
}
//------------------------------------------------------------
/* Draw a texture at a specific position and size */
void RendererManager::drawTexture(SDL_Texture *texture, const Rect &dest, const Rect &source) const
{
  if (!texture)
    return;

  const SDL_Rect  sdlDest = dest.toSDL();
  const SDL_Rect *sdlSrc  = nullptr;
  SDL_Rect        srcRect;

  if (source)
  {
    srcRect = source.toSDL();
    sdlSrc  = &srcRect;
  }

  SDL_RenderCopy(getSDLRenderer(), texture, sdlSrc, &sdlDest);
}
//------------------------------------------------------------
/* Set the current drawing color */
void RendererManager::setDrawColor(const Color &color) const
{
  SDL_SetRenderDrawColor(getSDLRenderer(),
                         color.r(),
                         color.g(),
                         color.b(),
                         color.a());
}
//------------------------------------------------------------
/* Set the clipping rectangle (nullptr to disable) */
void RendererManager::setClipRect(const Rect *rect) const
{
  if (rect)
  {
    const SDL_Rect sdlRect = rect->toSDL();
    SDL_RenderSetClipRect(getSDLRenderer(), &sdlRect);
  }
  else
  {
    SDL_RenderSetClipRect(getSDLRenderer(), nullptr);
  }
}
//------------------------------------------------------------
/* Get the current window size in pixels */
SDL_Point RendererManager::getWindowSize() const
{
  int w, h;
  SDL_GetRendererOutputSize(getSDLRenderer(), &w, &h);
  return { w, h };
}
//------------------------------------------------------------
/* Render text to a texture for drawing */
Texture RendererManager::renderTextTexture(const std::string &text, TTF_Font *font, const Color &color) const
{
  if (text.empty())
    return { nullptr, SDL_DestroyTexture };

  const SDL_Color sdlColor = { color.r(), color.g(), color.b(), color.a() };
  SDL_Surface    *surface  = TTF_RenderText_Blended(font, text.c_str(), sdlColor);
  if (!surface)
  {
    std::cerr << "Failed to render text surface: " << TTF_GetError() << std::endl;
    return { nullptr, SDL_DestroyTexture };
  }

  SDL_Texture *texture = SDL_CreateTextureFromSurface(getSDLRenderer(), surface);
  SDL_FreeSurface(surface);

  if (!texture)
    std::cerr << "Failed to create texture from text: " << SDL_GetError() << std::endl;

  return { texture, SDL_DestroyTexture };
}
//------------------------------------------------------------
