#ifndef _H_WEAPON_H
#define _H_WEAPON_H

#include <memory>
#include <glm/glm.hpp>
#include <functional>

#include "util.h"

#include "gameplay/entity.h"
#include "utility/direction.h"

#include <utility/component.h>
#include <gameplay/weapons/bulletmanager.h>

// TODO: Put all weapons inside of this base, we don't need a class for every weapon, we only need one class!

class Shader;
class Camera;
class EventManager;
class ResourceManager;
class GameActor;
class WeaponScript;
struct PhysicsComponent;
struct WeaponData;

class Weapon : public Entity
{
public:
	Weapon(const WeaponData* data, const unsigned weaponShaderID, const unsigned bulletShaderID, ResourceManager* resourceManager);

	void setWielder(GameActor* wielder) { this->wielder = wielder; }
	void toggleInfiniteAmmo() { infiniteAmmo = !infiniteAmmo; }
	void wield() { wielded = true; }
	void putaway() { wielded = false; }

	void addComponent(std::unique_ptr<Component> component);
	void attachScript(std::unique_ptr<WeaponScript> script);
	void hookEventManager(std::weak_ptr<EventManager> eventManager);
	bool shoot();
	void reload();
	void update(double deltaTime);
	void draw();

	const std::string getType() const { return weaponType; } 

	struct BulletData {
		glm::vec3 origin;
		glm::vec2 direction;
		float mass		= 1.f;
		float sizeMod	= 1.f;
		float speedMod	= 1.f;
		float dropMod	= 1.f;
	};
	void onHitCallback(GameActor* target, PhysicsComponent* bullet, const glm::vec2& normal);

	glm::vec2					getWeaponSize()		const { return weaponSize; }
	// This is the offset from the center right point of the weapon to the barrel of the gun defined in 
	// the XML file for the weapon
	glm::vec2					getWeaponOffset()	const { return weaponOffset; }
	float						getBulletSpeed()	const { return bulletSpeed; }
	glm::vec2					getBulletSize()		const { return bulletSize; }
	GameActor*					getWielder()		const { return wielder; }
	const Uint32				getMagazine()		const { return weaponMag; }
	const Uint32				getAmmo()			const { return weaponAmmo; }

	Weapon::BulletData			genBulletData	();
	void						createBullet	(const BulletData& data);

	const BulletManager* 		getBulletManager() { return bulletManager.get(); }

	~Weapon();

private:
	void adjustWeapon();

	std::string weaponType;

	glm::vec2 weaponSize;
	glm::vec2 weaponOffset;
	// TODO: Add reloading
	// weaponMag will be unused for now, but we will do a reload animation and reload the weapon eventually
	Uint16 weaponMag, weaponMagSize, weaponAmmo;
	bool reloading = false;
	bool wasReloading = false;
	bool infiniteAmmo = false;

	float bulletSpeed;
	float bulletDrop;
	// in ms
	float fireSpeed;
	glm::vec2 bulletSize;
	unsigned bulletShaderID;
	std::unique_ptr<Component> bulletSprite;
	std::shared_ptr<BulletManager> bulletManager;
	std::weak_ptr<EventManager> eventManager;

	std::unique_ptr<UTIL::RandomGenerator> bulletSpread;
	std::unique_ptr<UTIL::RandomGenerator> bulletModifer;

	GameActor* wielder;
	// is the weapon currently active
	bool wielded = false;
	int lastFireTime = 0;

	Direction lastDir;

	std::vector <std::unique_ptr<Component>> components;
	std::unique_ptr<WeaponScript> weaponScript;
	
};

#endif // _H_WEAPON_H
