#include "main_ui.hpp"
#include "components.hpp"
#include "easings.hpp"

namespace gui {
  using namespace components;

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

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

  void MainUI::debug() {
    auto& dbg = $level.world->get_the<Debug>();
    dbg.FPS = !dbg.FPS;
    dbg.PATHS = !dbg.PATHS;

    if(dbg.FPS) {
      // it's on now, enable things
      auto player = $level.world->get_the<Player>();
      auto& player_combat = $level.world->get<Combat>(player.entity);
      player_combat.hp = player_combat.max_hp;
      $overlay_ui.show_text("top_left", "STATS");
    } else {
      // it's off now, close it
      $overlay_ui.close_text("top_left");
    }
  }

  void MainUI::draw_stats() {
    auto player = $level.world->get_the<Player>();
    auto player_combat = $level.world->get<Combat>(player.entity);
    auto map = $level.map;
    std::string stats = fmt::format("STATS\n"
        "HP: {}\n"
        "mean:{:>8.5}\n"
        "sdev: {:>8.5}\n"
        "min: {:>8.5}\n"
        "max: {:>8.5}\n"
        "count:{:<10}\n"
        "level: {} size: {}x{}\n\n"
        "dir: {:0.2},{:0.2}\n\n"
        "VSync? {}\n"
        "FR Limit: {}\n"
        "Debug? {}\n\n",
        player_combat.hp, $stats.mean(), $stats.stddev(), $stats.min,
        $stats.max, $stats.n, $level.index, map->width(), map->height(),
        $rayview.$dir_x, $rayview.$dir_y,
        VSYNC, FRAME_LIMIT, DEBUG_BUILD);

    $overlay_ui.show_text("top_left", stats);
  }

  void MainUI::draw_blood() {
    auto player = $level.world->get_the<Player>();
    auto player_combat = $level.world->get<Combat>(player.entity);
    if(float(player_combat.hp) / float(player_combat.max_hp) < 0.5) {
      $overlay_ui.show_sprite("middle", "blood_splatter");
    } else {
      $overlay_ui.close_sprite("middle");
    }
  }

  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.show_label("top_left", $compass[$compass_dir]);

    auto st = textures::get("down_the_well");
    auto bounds = st.sprite->getLocalBounds();
    st.sprite->setPosition({RAY_VIEW_X + bounds.size.x / 2,
        RAY_VIEW_Y + bounds.size.y / 2});
    st.sprite->setOrigin({bounds.size.x / 2, bounds.size.y / 2});

    $overlay_ui.init();
  }

  void MainUI::show_level() {
    $show_level = true;
  }

  void MainUI::render() {
    auto start = $stats.time_start();

    if($show_level) {
      auto time = $clock.getElapsedTime();
      auto st = textures::get("down_the_well");
      float tick = ease::in_out_back(ease::sine(time.asSeconds()));
      float scale = std::lerp(1.0, 1.3, tick);
      st.sprite->setScale({scale, scale});

      $window.draw(*st.sprite);
      $overlay_ui.show_label("middle", "INTO THE WELL YOU GO...");
    } else {
      if($needs_render) $rayview.render();
      $rayview.draw($window);
    }

    $stats.sample_time(start);

    $overlay_ui.render($window);

    auto debug = $level.world->get_the<Debug>();
    if(debug.FPS) draw_stats();

    // draw_blood();
  }

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

  // this could be an optional that returs a Point
  std::optional<Point> MainUI::play_move() {
    if($camera.play_move($rayview)) {
      $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) {
    // -1 is left, 1 is right
    $compass_dir = ($compass_dir + dir) % $compass.size();
    $overlay_ui.show_label("top_left", $compass[$compass_dir]);
    $camera.plan_rotate($rayview, dir);
  }

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

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

  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.show_label("top_left", $compass[$compass_dir]);
    dirty();
  }

  void MainUI::mouse(int x, int y) {
    if($show_level) {
      $show_level = false;
      $level.world->send<Events::GUI>(Events::GUI::STAIRS_DOWN, $level.player, {});
      $overlay_ui.close_label("middle");
    } else {
      $overlay_ui.$gui.mouse(x, y);
    }
  }
}