#ifndef _H_EVENTS_H
#define _H_EVENTS_H

#include <functional>
#include <memory>
#include <string>
#include <typeindex>
#include <unordered_map>

#include "utility/direction.h"
#include <glm/glm.hpp>

class Bullet;
struct PhysicsComponent;

struct BulletFiredEvent {
  std::weak_ptr<Bullet> bullet;
};

struct BulletDiedEvent {
  std::shared_ptr<PhysicsComponent> physObj;
};

struct BulletCollideEvent {
  unsigned int ownerID;
  unsigned int victimID;
  std::shared_ptr<PhysicsComponent> bullet;
  glm::vec2 normal;
};

struct DirectionChangeEvent {
  int entityid;
  Direction direction;
};

struct EntityMoveEvent {
  int entityid;
};

struct EntityStopEvent {
  int entityid;
};

struct EntityReloadEvent {
  int entityid;
  glm::vec3 position;
  std::string weaponType;
};

struct EntityFinishReloadEvent {
  int entityid;
};

struct EntityFireEvent {
  int entityid;
  float fireDelay;
  glm::vec3 firePosition;
  std::string weaponType;
};

struct AnimationFinishedEvent {
  int entityid;
  std::string animType;
};

struct ScreenShakeEvent {
  float intensity;
  float duration;
};

struct ScreenBlurEvent {
  float intensity;
  float duration;
};

struct WindowResizeEvent {
  size_t width;
  size_t height;
};

class EventManager {
public:
  template <typename T> using EventCallback = std::function<void(const T &)>;

  template <typename T> void subscribe(EventCallback<T> cb) {
    auto wrapper = [cb](void *ev) { cb(*static_cast<T *>(ev)); };
    callbacks[typeid(T)].push_back(wrapper);
  }
  template <typename T> void notify(const T &event) {
    auto iterator = callbacks.find(typeid(T));
    if (iterator != callbacks.end()) {
      for (auto &cb : iterator->second) {
        cb((void *)&event);
      }
    }
  }

private:
  std::unordered_map<std::type_index, std::vector<std::function<void(void *)>>>
      callbacks;
};

#endif //_H_EVENTS_H
