#include "status_ui.hpp"
#include "components.hpp"
#include "inventory.hpp"
#include "color.hpp"
#include "guecs.hpp"
#include "rand.hpp"

namespace gui {
  using namespace guecs;
  using std::any, std::any_cast, std::string, std::make_any;

  StatusUI::StatusUI(GameLevel level) :
    $level(level)
  {
    $gui.position(STATUS_UI_X, STATUS_UI_Y, STATUS_UI_WIDTH, STATUS_UI_HEIGHT);
    $gui.layout(
        "[inv_slot1 | inv_slot2 | inv_slot3]"
        "[inv_slot4 | inv_slot5 | inv_slot6]"
        "[inv_slot7 | inv_slot8 | inv_slot9]"
        "[*%(100,300)log_view]"
        "[_]"
        "[_]");

    size_t inv_id = 0;
    for(auto [name, entity] : $gui.$name_ents) {
      if(name.starts_with("inv_")) {
        $slots[name] = inv_id++;
      }
    }
  }

  void StatusUI::init() {
    $gui.world().set_the<Background>({$gui.$parser});

    for(auto& [name, cell] : $gui.cells()) {
      if(name == "log_view") {
        $log_to = $gui.entity("log_view");
        $gui.set<Rectangle>($log_to, {});
        $gui.set<Textual>($log_to, {"Welcome to the Game!", 20});
      } else {
        auto button = $gui.entity(name);
        $gui.set<Rectangle>(button, {});
        $gui.set<Textual>(button, {""});
        $gui.set<ActionData>(button, {make_any<string>(name)});
        $gui.set<Clickable>(button, {
            [this](auto ent, auto data){ select_slot(ent, data); }
        });
      }
    }

    $gui.init();
  }

  void StatusUI::select_slot(DinkyECS::Entity ent, any slot_name) {
    dbc::check(slot_name.has_value(), "passed select_slot an any without a value");

    auto cn = $gui.get<CellName>(ent);
    auto world = $level.world;

    if(world->has<components::Inventory>($level.player)) {
      auto& inventory = world->get<components::Inventory>($level.player);
      size_t inv_id = $slots[any_cast<string>(slot_name)];

      if(inventory.has_item(inv_id)) {
        auto [used, name] = inventory.use($level, inv_id);

        if(used) {
          log(fmt::format("Used item: {}", name));
        } else {
          log(fmt::format("You are out of {}.", name));
        }

        update();
      }
    }
  }

  /* WARNING: This is really not the greatest way to do this. */
  void StatusUI::update() {
    if($gui.has<Textual>($log_to)) {
      auto& text = $gui.get<Textual>($log_to);
      string log;
      for(auto msg : $messages) {
        log += msg + "\n";
      }
      text.update(log);
    }

    auto world = $level.world;
    if(world->has<components::Inventory>($level.player)) {
      auto& inventory = world->get<components::Inventory>($level.player);

      for(auto& [slot_name, inv_id] : $slots) {
        if(inventory.has_item(inv_id)) {
          auto slot = $gui.entity(slot_name);
          auto& item = inventory.get(inv_id);
          auto comp_sprite = components::get<components::Sprite>(item.data);
          $gui.set_init<guecs::Sprite>(slot, {comp_sprite.name});
          string count_label = fmt::format("{}", item.count);
          auto& label = $gui.get<Textual>(slot);
          label.text->setString(count_label);

          auto& sprite = $gui.get<guecs::Sprite>(slot);

          if(item.count == 0) {
            sprite.sprite->setColor({125, 125, 125});
          } else {
            sprite.sprite->setColor({255, 255, 255});
          }
        }
      }
    }
  }

  void StatusUI::render(sf::RenderWindow &window) {
    $gui.render(window);
  }

  void StatusUI::log(string msg) {
    $messages.push_front(msg);
    if($messages.size() > MAX_LOG_MESSAGES) {
      $messages.pop_back();
    }
  }

  void StatusUI::update_level(GameLevel &level) {
    $level = level;
    init();
  }
}
