#pragma once
#include "game/components.hpp"
#include <SFML/Graphics/RenderTexture.hpp>
#include "game/map.hpp"
#include "algos/spatialmap.hpp"
#include "game/level.hpp"
#include "events.hpp"
#include "game/registry.hpp"

namespace System {

  using namespace components;
  using namespace DinkyECS;
  using std::string, matrix::Matrix;

  void init(Registry& reg);
  void restart();

  void lighting();
  void motion();
  void collision();
  void death();
  void generate_paths();
  void enemy_pathing();
  void enemy_ai_initialize();

  void device(World &world, Entity actor, Entity item);
  void move_player(Position move_to);
  Entity spawn_item(World& world, const string& name);
  void drop_item(Entity item);

  void enemy_ai();
  void combat(int attack_id);

  std::shared_ptr<sf::Shader> sprite_effect(Entity entity);
  void distribute_loot(Position target_pos);

  void pickup();

  // BUG: these might need to go somewhere else....
  bool place_in_container(Entity cont_id, const string& name, Entity world_entity);
  void remove_from_container(Entity cont_id, const std::string& name);
  void remove_from_world(Entity entity);
  void inventory_swap(Entity container_id, const std::string& a_name, const std::string &b_name);
  bool inventory_occupied(Entity container_id, const std::string& name);

  void set_position(DinkyECS::World& world, SpatialMap& collision, Entity entity, Position pos);
  void use_item(const std::string& slot_name);

  game::Event shortest_rotate(Point player_at, Point aiming_at, Point turning_to);

  template <typename T>
  void multi_path(GameDB::Level& level, Pathing& paths, Matrix& walls) {
    // first, put everything of this type as a target
    level.world->query<Position, T>(
      [&](const auto ent, auto& position, auto&) {
        if(ent != level.player) {
          paths.set_target(position.location);
        }
    });

    level.world->query<Collision>(
      [&](const auto ent, auto& collision) {
        if(collision.has && ent != level.player) {
          auto& pos = level.world->get<Position>(ent);
          walls[pos.location.y][pos.location.x] = WALL_VALUE;
        }
    });

    paths.compute_paths(walls);
  }

  void clear_attack();
  void spawn_attack(World& world, int attack_id, DinkyECS::Entity enemy);

}
