#include "gui/main_ui.hpp"
#include "components.hpp"
#include "easings.hpp"
#include <fmt/xchar.h>
#include "constants.hpp"

namespace gui {
  using namespace components;

  MainUI::MainUI(sf::RenderWindow& window) :
    $window(window),
    $rayview(RAY_VIEW_WIDTH, RAY_VIEW_HEIGHT),
    $camera($rayview)
  {
    $window.setVerticalSyncEnabled(VSYNC);
    $window.setFramerateLimit(FRAME_LIMIT);
  }

  void MainUI::dirty() {
    $needs_render = true;
  }

  void MainUI::init() {
    auto& player_position = $level.world->get<Position>($level.player);
    auto player = player_position.location;

    $rayview.init_shaders();
    $rayview.set_position(RAY_VIEW_X, RAY_VIEW_Y);
    $rayview.position_camera(player.x + 0.5, player.y + 0.5);

    $overlay_ui.init();
  }

  DinkyECS::Entity MainUI::camera_aim() {
    auto aimed_at = $camera.aimed_at();

    if($level.collision->occupied(aimed_at)) {
      return $level.collision->get(aimed_at);
    } else {
      return 0;
    }
  }

  void MainUI::render() {
    $rayview.aiming_at = camera_aim();
    if($needs_render) $rayview.render();
    $rayview.draw($window);

    $overlay_ui.render($window);
  }

  void MainUI::health_low() {
    $overlay_ui.show_sprite("middle", "blood_splatter");
  }

  bool MainUI::play_rotate() {
    bool done = $camera.play_rotate();
    $needs_render = !done;

    return done;
  }

  // this could be an optional that returs a Point
  std::optional<Point> MainUI::play_move() {
    if($camera.play_move()) {
      $needs_render = false;
      Point pos{
          size_t($camera.target_x),
          size_t($camera.target_y)};
      return std::make_optional<Point>(pos);
    } else {
      $needs_render = true;
      return std::nullopt;
    }
  }

  void MainUI::plan_rotate(int dir, float amount) {
    // -1 is left, 1 is right
    int extra = (amount == 0.5) * dir;
    $compass_dir = ($compass_dir + dir + extra) % COMPASS.size();
    $camera.plan_rotate(dir, amount);
  }

  Point MainUI::plan_move(int dir, bool strafe) {
    return $camera.plan_move(dir, strafe);
  }

  void MainUI::abort_plan() {
    $camera.abort_plan();
  }

  void MainUI::dead_entity(DinkyECS::Entity entity) {
    auto &sprite = $level.world->get<components::Sprite>(entity);
    $rayview.update_sprite(entity, sprite);
  }

  void MainUI::update_level(GameLevel level) {
    $level = level;
    auto& player_position = $level.world->get<Position>($level.player);
    auto player = player_position.location;

    $rayview.update_level($level);
    $rayview.position_camera(player.x + 0.5, player.y + 0.5);

    $compass_dir = 0;

    $overlay_ui.update_level(level);
    dirty();
  }

  void MainUI::mouse(int x, int y, bool hover) {
    $overlay_ui.$gui.mouse(x, y, hover);
  }
}