#pragma once
#include <vector>
#include "glm/glm.hpp"
#include "Box2D/Dynamics/b2Body.h"
#include "Box2D/Dynamics/b2Fixture.h"
#include "Component.hpp"
#include "Event.hpp"

enum ForceType{ Force, Impulse };
enum CollisionPhase { Enter, Exit };

class PhysicsBody final : public Component {
public:
	PhysicsBody(GameObject*);
	// Adds the specified amount and type of force.
	void AddForce(glm::vec2, ForceType);
	// Provides selective 'friend' access for LevelBuilder
	// (Attorney-Client idiom)
	class Mutator final {
	private:
		inline static void SetPhysicsStepEvent(PhysicsBody& target, Event<void()>* event) {
			target.physicsStepEvent = event;
		}
		inline static void SetPhysicsWorld(PhysicsBody& target, std::shared_ptr<b2World> world) {
			target.physicsWorld = world;
		}
		inline static void DefineBody(PhysicsBody& target, b2BodyDef definition) {
			target.bodyDef = definition;
		}
		inline static void AddFixture(PhysicsBody& target, b2FixtureDef fixture) {
			target.fixtureDefs.push_back(fixture);
		}
		friend class LevelBuilder;
	};

	glm::vec2 GetLinearVelocity();
	b2Body * GetBody();
	void SetLinearVelocity(glm::vec2);

	void Collision(GameObject&, CollisionPhase);
	Event<void(GameObject&)> OnCollisionEnter;
	Event<void(GameObject&)> OnCollisionExit;
	
private:
	Event<void()>* physicsStepEvent = nullptr;

	b2BodyType type;
	b2BodyDef bodyDef;
	std::vector<b2FixtureDef> fixtureDefs;
	b2Body * body = nullptr;
	std::shared_ptr<b2World> physicsWorld;

	bool fireCollisionEvents;
	
	void PhysicsUpdate();
	void registerPhysicsUpdate();
	void deregisterPhysicsUpdate();

	void buildBody();

	b2Vec2 startingPosition;

	friend class Mutator;	
};
