#include "guecs.hpp"
#include "sfml/backend.hpp"

namespace guecs {
  static Backend* BACKEND = nullptr;

  using std::make_shared;

  void init(Backend* backend) {
    BACKEND = backend;
  }

  void Textual::init(lel::Cell &cell, shared_ptr<sf::Font> font_ptr) {
    dbc::check(font_ptr != nullptr, "you failed to initialize this WideText");
    if(font == nullptr) font = font_ptr;
    if(text == nullptr) text = make_shared<sf::Text>(*font, content, size);
    text->setFillColor(color);

    if(centered) {
      auto bounds = text->getLocalBounds();
      auto text_cell = lel::center(bounds.size.x, bounds.size.y, cell);
      // this stupid / 2 is because SFML renders from baseline rather than from the claimed bounding box
      text->setPosition({float(text_cell.x), float(text_cell.y) - text_cell.h / 2});
    } else {
      text->setPosition({float(cell.x + padding * 2), float(cell.y + padding * 2)});
    }

    text->setCharacterSize(size);
  }

  void Textual::update(const wstring& new_content) {
    content = new_content;
    text->setString(content);
  }

  void Sprite::update(const string& new_name) {
    if(new_name != name) {
      name = new_name;
      auto sprite_texture = BACKEND->texture_get(name);
      sprite->setTexture(*sprite_texture.texture);
      sprite->setTextureRect(sprite_texture.sprite->getTextureRect());
    }
  }

  void Sprite::init(lel::Cell &cell) {
    auto sprite_texture = BACKEND->texture_get(name);

    sprite = make_shared<sf::Sprite>(
        *sprite_texture.texture,
        sprite_texture.sprite->getTextureRect());

    sprite->setPosition({
        float(cell.x + padding),
        float(cell.y + padding)});

    auto bounds = sprite->getLocalBounds();

    sprite->setScale({
        float(cell.w - padding * 2) / bounds.size.x,
        float(cell.h - padding * 2) / bounds.size.y});
  }

  void Rectangle::init(lel::Cell& cell) {
    sf::Vector2f size{float(cell.w) - padding * 2, float(cell.h) - padding * 2};
    if(shape == nullptr) shape = make_shared<sf::RectangleShape>(size);
    shape->setPosition({float(cell.x + padding), float(cell.y + padding)});
    shape->setFillColor(color);
    shape->setOutlineColor(border_color);
    shape->setOutlineThickness(border_px);
  }


  void Meter::init(lel::Cell& cell) {
    bar.init(cell);
  }

  void Meter::render(lel::Cell& cell) {
    float level = std::clamp(percent, 0.0f, 1.0f) * float(cell.w);
    // ZED: this 6 is a border width, make it a thing
    bar.shape->setSize({std::max(level, 0.0f), float(cell.h - 6)});
  }

  void Sound::play(bool hover) {
    if(!hover) {
      BACKEND->sound_play(on_click);
    }
  }

  void Sound::stop(bool hover) {
    if(!hover) {
      BACKEND->sound_stop(on_click);
    }
  }

  void Background::init() {
    sf::Vector2f size{float(w), float(h)};
    if(shape == nullptr) shape = make_shared<sf::RectangleShape>(size);
    shape->setPosition({float(x), float(y)});
    shape->setFillColor(color);
  }

  void Effect::init(lel::Cell &cell) {
    $shader = BACKEND->shader_get(name);
    $shader->setUniform("u_resolution", sf::Vector2f({float(cell.w), float(cell.h)}));
    $clock = std::make_shared<sf::Clock>();
  }

  void Effect::step() {
    sf::Time cur_time = $clock->getElapsedTime();
    float u_time = cur_time.asSeconds();

    if(u_time < $u_time_end) {
      $shader->setUniform("u_duration", duration);
      $shader->setUniform("u_time_end", $u_time_end);
      $shader->setUniform("u_time", u_time);
    } else {
      $active = false;
    }
  }

  void Effect::run() {
    $active = true;
    sf::Time u_time = $clock->getElapsedTime();
    $u_time_end = u_time.asSeconds() + duration;
  }

  shared_ptr<sf::Shader> Effect::checkout_ptr() {
    if(BACKEND->shader_updated()) {
      $shader = BACKEND->shader_get(name);
    }

    return $shader;
  }

  void Effect::stop() {
    $active = false;
  }
}
