#ifndef _H_EVENTS_H
#define _H_EVENTS_H

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

#include "utility/direction.h"

class Bullet;
struct PhysicsComponent;

class Event {
public:
	virtual ~Event() {  };
	virtual std::string getType() const = 0;
};

class BulletFiredEvent : public Event {
public:
	BulletFiredEvent(const std::shared_ptr<Bullet>& bullet) : bullet(bullet) {};
	std::string getType() const override { return "OnBulletFired"; }
	std::shared_ptr<Bullet> bullet;
};

class BulletDiedEvent : public Event {
public:
	BulletDiedEvent(const std::shared_ptr<PhysicsComponent>& physObj) : physObj(physObj) {};
	std::string getType() const override { return "OnBulletDied"; }
	std::shared_ptr<PhysicsComponent> physObj;
};

class BulletCollideEvent : public Event {
public:
	BulletCollideEvent(const unsigned int ownerID, const unsigned int victimID, const std::shared_ptr<PhysicsComponent>& bullet, const glm::vec2& normal) 
		: ownerID(ownerID), victimID(victimID), bullet(bullet), normal(normal){}
	std::string getType() const override { return "OnBulletCollide"; }
	unsigned int ownerID;
	unsigned int victimID;
	std::shared_ptr<PhysicsComponent> bullet;
	glm::vec2 normal;
};

class DirectionChangeEvent : public Event {
public:
	DirectionChangeEvent(const int entityid, Direction direction) : entityid(entityid), direction(direction) {}
	std::string getType() const override { return "OnDirectionChange"; }
	Direction direction;
	int entityid;
};

class EntityMoveEvent : public Event {
public:
	EntityMoveEvent(const int entityid) : entityid(entityid) {}
	std::string getType() const override { return "OnEntityMove"; }
	int entityid;
};

class EntityStopEvent : public Event {
public:
	EntityStopEvent(const int entityid) : entityid(entityid) {}
	std::string getType() const override { return "OnEntityStop"; }
	int entityid;
};

class EntityReloadEvent : public Event {
public:
	EntityReloadEvent(const int entityid) : entityid(entityid) {}
	std::string getType() const override { return "OnEntityReload"; }
	int entityid;
};

class EntityFinishReloadEvent : public Event {
public:
	EntityFinishReloadEvent(const int entityid) : entityid(entityid) {}
	std::string getType() const override { return "OnEntityFinishReload"; }
	int entityid;
};

class AnimationFinishedEvent : public Event {
public:
	AnimationFinishedEvent(const int entityid, const std::string animType) : entityid(entityid), animType(animType) {}
	std::string getType() const override { return "OnAnimationFinished"; }
	std::string animType;
	int entityid;
};

class EventManager {
public:
	using EventCallback = std::function<void(std::shared_ptr<Event>)>;

	void subscribe(std::string eventType, EventCallback callback) {
		listeners[eventType].push_back(callback);
	}
	void notify(const std::shared_ptr<Event>& event) {
		auto iterator = listeners.find(event->getType());
		if (iterator != listeners.end())
		{
			for (auto& callback : iterator->second)
			{
				callback(event);
			}
		}
	}
private:
	std::unordered_map <std::string, std::vector <EventCallback>> listeners;
};

#endif //_H_EVENTS_H