#pragma once
#include "Box2D/Box2D.h"
#include "Box2D/Common/b2Math.h"
#include "PhysicsBody.hpp"
#include "GameObject.hpp"
#include "Transform.hpp"
#include "SceneManager.h"
#include "Event.hpp"

const float METERS_PER_PIXEL = 1.0f/100.0f;

PhysicsBody::PhysicsBody(GameObject* gameObject) : Component(gameObject) {
	gameObject->OnEnable += Delegate<void()>::Create<PhysicsBody, &PhysicsBody::registerPhysicsUpdate>(this);
	gameObject->OnDisable += Delegate<void()>::Create < PhysicsBody, &PhysicsBody::deregisterPhysicsUpdate>(this);
	OnCollisionEnter = Event<void(GameObject&)>();
	OnCollisionExit = Event<void(GameObject&)>();
}

void PhysicsBody::AddForce(glm::vec2 force, ForceType forceType) {
	b2Vec2 forceV{ force.x, force.y };
	if (forceType == Force) {
		body->ApplyForce(forceV, body->GetWorldCenter(), true);
	}
	else {
		body->ApplyLinearImpulse(forceV, body->GetWorldCenter(), true);
	}
}

void PhysicsBody::Collision(GameObject& other, CollisionPhase phase) {
	if (!fireCollisionEvents)return;
	if (phase == Enter && OnCollisionEnter.IsValid())OnCollisionEnter(other);
	else if (OnCollisionExit.IsValid())OnCollisionExit(other);
}

void PhysicsBody::PhysicsUpdate() {
	if (body->GetType() == b2_staticBody)return;
	glm::vec2 position = { body->GetPosition().x , body->GetPosition().y };
	if (position.y < SceneManager::GetInstance()->GetCurrentScene()->levelManager.currentLevel.deathLevelInY)
	{
		position = glm::vec2(startingPosition.x,startingPosition.y);
		body->SetTransform(startingPosition, 0.0f);
		
		body->SetLinearVelocity(b2Vec2(0.0f, -49.0f));
	}
	auto physicsScale = SceneManager::GetInstance()->GetCurrentScene()->GetPhysicsScale();
	//position = position * 0.5f;
	gameObject.GetTransform()->SetPosition(position);
	gameObject.GetTransform()->SetRotation(body->GetAngle());

}

void PhysicsBody::registerPhysicsUpdate() {
	buildBody();
	if (fixtureDefs.size() > 0) {
		for (b2FixtureDef fixtureDef : fixtureDefs) {
			body->CreateFixture(&fixtureDef);
		}
	}
	*physicsStepEvent += Delegate<void()>::Create<PhysicsBody, &PhysicsBody::PhysicsUpdate>(this);
	fireCollisionEvents = true;
	startingPosition = body->GetPosition();
}

void PhysicsBody::deregisterPhysicsUpdate() {
	if (physicsStepEvent == nullptr)return;
	*physicsStepEvent -= Delegate<void()>::Create<PhysicsBody, &PhysicsBody::PhysicsUpdate>(this);
	if (body == nullptr) return;
	physicsWorld->DestroyBody(body);
	body = nullptr;
	fireCollisionEvents = false;
}

void PhysicsBody::buildBody() {
	glm::vec2 position = gameObject.GetTransform()->GetPosition();
	bodyDef.position.Set(position.x, position.y);
	bodyDef.angle = gameObject.GetTransform()->GetRotation();
	body = physicsWorld->CreateBody(&bodyDef);
	body->SetFixedRotation(true);
	body->SetUserData(this);
	/*for (auto & fixture : fixtureDefs) {
		body->CreateFixture(&fixture);
	}*/
}

glm::vec2 PhysicsBody::GetLinearVelocity() {
	if (body == nullptr)
	{
		return glm::vec2(0, 0);
	}
	b2Vec2 v = body->GetLinearVelocity();
	return { v.x,v.y };
}
b2Body * PhysicsBody::GetBody()
{
	return body;
}
void PhysicsBody::SetLinearVelocity(glm::vec2 velocity) {
	b2Vec2 v{ velocity.x, velocity.y };
	if (velocity != glm::vec2(0, 0)) {
		body->SetAwake(true);
	}
	body->SetLinearVelocity(v);
}

