#include <frostbite2D/2d/canvas_actor.h>
#include <frostbite2D/graphics/renderer.h>
#include <algorithm>

namespace frostbite2D {

CanvasActor::CanvasActor() {
  canvasRoot_ = MakePtr<Actor>();
  canvasRoot_->SetName("canvasRoot");
}

bool CanvasActor::Init(int width, int height) {
  return SetCanvasSize(width, height);
}

bool CanvasActor::SetCanvasSize(int width, int height) {
  width = std::max(width, 1);
  height = std::max(height, 1);

  if (!renderTexture_) {
    renderTexture_ = MakePtr<RenderTexture>();
  }

  if (!renderTexture_->Resize(width, height)) {
    return false;
  }

  canvasWidth_ = width;
  canvasHeight_ = height;
  canvasCamera_.setPosition(Vec2::Zero());
  canvasCamera_.setZoom(1.0f);
  canvasCamera_.setViewport(canvasWidth_, canvasHeight_);

  syncCanvasResources();
  dirty_ = true;
  return true;
}

void CanvasActor::SetClearColor(const Color& color) {
  clearColor_ = color;
  dirty_ = true;
}

void CanvasActor::SetDirty() {
  dirty_ = true;
}

bool CanvasActor::Redraw() {
  dirty_ = true;
  return redrawInternal();
}

void CanvasActor::SetCustomDrawCallback(std::function<void()> callback) {
  customDrawCallback_ = std::move(callback);
  dirty_ = true;
}

void CanvasActor::ClearCustomDrawCallback() {
  if (!customDrawCallback_) {
    return;
  }

  customDrawCallback_ = nullptr;
  dirty_ = true;
}

void CanvasActor::AddCanvasChild(RefPtr<Actor> child) {
  if (!child || child.Get() == this) {
    return;
  }

  canvasRoot_->AddChild(child);
  dirty_ = true;
}

void CanvasActor::RemoveCanvasChild(RefPtr<Actor> child) {
  if (!child) {
    return;
  }

  canvasRoot_->RemoveChild(child);
  dirty_ = true;
}

void CanvasActor::RemoveAllCanvasChildren() {
  canvasRoot_->RemoveAllChildren();
  dirty_ = true;
}

Ptr<Texture> CanvasActor::GetOutputTexture() const {
  return renderTexture_ ? renderTexture_->GetTexture() : nullptr;
}

void CanvasActor::OnUpdate(float deltaTime) {
  if (canvasRoot_) {
    canvasRoot_->Update(deltaTime);
  }
}

void CanvasActor::Render() {
  if (dirty_) {
    redrawInternal();
  }

  Sprite::Render();
}

bool CanvasActor::redrawInternal() {
  if (!renderTexture_ || !renderTexture_->IsValid()) {
    return false;
  }

  Renderer& renderer = Renderer::get();
  if (!renderer.beginRenderToTexture(*renderTexture_, &canvasCamera_, clearColor_)) {
    dirty_ = true;
    return false;
  }

  if (canvasRoot_) {
    canvasRoot_->Render();
  }
  if (customDrawCallback_) {
    customDrawCallback_();
  }

  renderer.endRenderToTexture();
  dirty_ = false;
  return true;
}

void CanvasActor::syncCanvasResources() {
  Ptr<Texture> outputTexture = renderTexture_ ? renderTexture_->GetTexture() : nullptr;
  if (outputTexture) {
    outputTexture->setSampling(TextureSampling::PixelArt);
  }

  SetTexture(outputTexture);
  SetSize(static_cast<float>(canvasWidth_), static_cast<float>(canvasHeight_));
}

} // namespace frostbite2D
