#include "rituals.hpp"
#include "ai_debug.hpp"
#include "ai.hpp"

namespace combat {

  void BattleEngine::add_enemy(DinkyECS::Entity enemy_id, ai::EntityAI& enemy) {
    combatants.insert_or_assign(enemy_id, enemy);
  }

  bool BattleEngine::plan() {
    int active = 0;

    for(auto& [entity, enemy_ai] : combatants) {
      fmt::println("\n\n==== ENTITY {} has AI:", entity);
      enemy_ai.dump();
      enemy_ai.set_state("enemy_found", true);
      enemy_ai.set_state("in_combat", true);
      enemy_ai.update();

      fmt::println("\n\n---- AFTER UPDATE:");
      enemy_ai.dump();

      active += enemy_ai.active();
    }

    return active > 0;
  }

  void BattleEngine::fight(std::function<void(DinkyECS::Entity, ai::EntityAI &)> cb) {
    for(auto& [entity, enemy_ai] : combatants) {
      if(enemy_ai.wants_to("kill_enemy")) {
        cb(entity, enemy_ai);
      } else if(!enemy_ai.active()) {
        enemy_ai.dump();
        dbc::sentinel("enemy AI ended early, fix your ai.json");
      } else {
        dbc::log("enemy doesn't want to fight");
        enemy_ai.dump();
      }
    }
  }

  void BattleEngine::dump() {
    for(auto& [entity, enemy_ai] : combatants) {
      fmt::println("\n\n###### ENTITY #{}", entity);
      enemy_ai.dump();
    }
  }


  RitualEngine::RitualEngine(std::string config_path) :
    $config(config_path)
  {
    $profile = $config["profile"];

    auto& actions = $config["actions"];

    for(auto& ac : actions) {
      auto action = ai::config_action($profile, ac);
      $actions.insert_or_assign(action.name, action);
    }

    for(auto& [name, sc] : $config["states"].items()) {
      auto state = ai::config_state($profile, sc);
      $states.insert_or_assign(name, state);
    }

    auto& scripts = $config["scripts"];
    for(auto& [script_name, action_names] : scripts.items()) {
      std::vector<ai::Action> the_script;
      for(auto name : action_names) {
        the_script.push_back($actions.at(name));
      }

      $scripts.insert_or_assign(script_name, the_script);
    }
  }

  ai::State RitualEngine::load_state(std::string name) {
    return $states.at(name);
  }

  ai::Action RitualEngine::load_action(std::string name) {
    return $actions.at(name);
  }

  RitualAI RitualEngine::start() {
    auto start = load_state("initial");
    auto goal = load_state("final");
    return {"actions", start, goal};
  }

  void RitualEngine::set_state(RitualAI& ritual, std::string name, bool setting) {
    ritual.start.set($profile.at(name), setting);
  }

  void RitualEngine::reset(RitualAI& ritual) {
    ritual.start = ritual.original;
  }

  void RitualEngine::plan(RitualAI& ritual) {
    ritual.plan = ai::plan_actions($scripts.at(ritual.script), ritual.start, ritual.goal);
  }

  bool RitualAI::will_do(std::string name) {
    if(plan.script.size() == 0) return false;

    return plan.script[0].name == name;
  }

  ai::Action RitualAI::pop() {
    auto result = plan.script.front();
    plan.script.pop_front();
    return result;
  }

  void RitualAI::dump() {
    ai::dump_script(script, start, plan.script);
  }
}