#include "levelmanager.hpp"
#include "worldbuilder.hpp"
#include "constants.hpp"
#include "save.hpp"
#include "systems.hpp"
#include "components.hpp"
#include "rituals.hpp"

using lighting::LightRender;
using std::shared_ptr, std::make_shared;
using namespace components;

LevelManager::LevelManager() {
  create_level();
}

LevelScaling LevelManager::scale_level() {
  return {
    INITIAL_MAP_W + int($current_level * 2),
    INITIAL_MAP_H + int($current_level * 2)
  };
}


inline shared_ptr<DinkyECS::World> clone_load_world(shared_ptr<DinkyECS::World> prev_world)
{
  auto world = make_shared<DinkyECS::World>();

  if(prev_world != nullptr) {
    fmt::println("############### NEW WORLD #################");
    prev_world->clone_into(*world);

    fmt::println("new world entity_count={}, prev={}", world->entity_count, prev_world->entity_count);

    for(auto [ent, is_set] : prev_world->$constants) {
      if(world->has<components::Sprite>(ent)) {
        fmt::println("#### Sprite {} copied to new world.", ent);
      }
    }
  } else {
    save::load_configs(*world);
  }

  return world;
}

shared_ptr<gui::BossFightUI> LevelManager::create_bossfight(shared_ptr<DinkyECS::World> prev_world) {
  dbc::check(prev_world != nullptr, "Starter world for boss fights can't be null.");
  auto world = clone_load_world(prev_world);
  auto& config = prev_world->get_the<GameConfig>();

  // BUG: the jank is too strong here
  auto boss_names = config.bosses.keys();
  auto& level_name = boss_names[$current_level % boss_names.size()];
  auto& boss_data = config.bosses[level_name];

  auto boss_id = world->entity();
  components::configure_entity(*world, boss_id, boss_data["components"]);

  return make_shared<gui::BossFightUI>(world, boss_id);
}

DinkyECS::Entity LevelManager::spawn_enemy(const std::string& named) {
  (void)named;
  dbc::log("THIS FUNCTION NEEDS A REWRITE");
  return 0;
}

size_t LevelManager::create_level(shared_ptr<DinkyECS::World> prev_world) {
  auto world = clone_load_world(prev_world);

  auto scaling = scale_level();

  auto map = make_shared<Map>(scaling.map_width, scaling.map_height);
  auto collision = std::make_shared<SpatialMap>();

  WorldBuilder builder(*map, *collision);
  builder.generate(*world);

  size_t index = $levels.size();

  auto player = world->get_the<Player>();

  $levels.emplace_back(index, player.entity, map, world,
      make_shared<LightRender>(map->tiles()), collision);

  dbc::check(index == $levels.size() - 1, "Level index is not the same as $levels.size() - 1, off by one error");
  return index;
}

GameLevel &LevelManager::next() {
  dbc::check($current_level < $levels.size(), "attempt to get next level when at end");
  $current_level++;
  return $levels.at($current_level);
}

GameLevel &LevelManager::previous() {
  dbc::check($current_level > 0, "attempt to go to previous level when at 0");
  $current_level--;
  return $levels.at($current_level);
}

GameLevel &LevelManager::current() {
  return $levels.at($current_level);
}

GameLevel &LevelManager::get(size_t index) {
  return $levels.at(index);
}
