#include "CharacterController.h"
#include <memory>
#include "PhysicsBody.hpp"
#include "GameObject.hpp"
#include "Transform.hpp"
#include "CharacterAnimator.hpp"
#include "Logger.hpp"
#include "glm/gtx/norm.hpp"

CharacterController::CharacterController(GameObject* gameObject) : Behaviour(gameObject){
	gameObject->OnEnable += Delegate<void()>::Create<CharacterController, &CharacterController::registerEvents>(this);
	gameObject->OnDisable += Delegate<void()>::Create<CharacterController, &CharacterController::deregisterEvents>(this);
}

void CharacterController::Update(float deltaTime) {
	Logger::Log("CharacterController::Update");
	if (right) {
		newDirectionalForce.x = directionalForceAmount;
		animator->SetState(CharacterAnimator::State::WalkRight);
	}
	else if (left) {
		newDirectionalForce.x = -directionalForceAmount;
		animator->SetState(CharacterAnimator::State::WalkLeft);
	}
	if (isGrounded && jump) {
		newDirectionalForce.y = jumpForceAmount;
		animator->SetState(CharacterAnimator::State::Jump);
		isGrounded = false;
	}
	if (glm::length2(newDirectionalForce) != 0.0f) {
		auto linearVelocity = physicsBody->GetLinearVelocity();
		float currentVelocity = linearVelocity.x;
		if (abs(currentVelocity) > maxVelocity) {
			linearVelocity.x = glm::sign(linearVelocity.x)*maxVelocity;
			physicsBody->SetLinearVelocity(linearVelocity);
		}
		physicsBody->AddForce(newDirectionalForce, Impulse);
		newDirectionalForce = glm::vec2(0.0f, 0.0f);
	}
	if (isGrounded) {
		if (physicsBody->GetLinearVelocity().x == 0)animator->SetState(CharacterAnimator::State::Idle);
	}
	else {
		if (physicsBody->GetLinearVelocity().y < 0)animator->SetState(CharacterAnimator::State::Fall);
	}
	//UnderLevel();
}

void CharacterController::InitialiseSelf() {
	physicsBody = gameObject.GetComponents<PhysicsBody>().front();
	animator = gameObject.GetComponents<CharacterAnimator>().front();
	playerSpawn = gameObject.GetTransform()->GetPosition();
}

void CharacterController::OnKey(SDL_Event& event) {
	if (event.key.keysym.sym == SDLK_LEFT)
	{
		left = event.type == SDL_KEYDOWN;
	}
	if (event.key.keysym.sym == SDLK_RIGHT)
	{
		right = event.type == SDL_KEYDOWN;
	}
	if (event.key.keysym.sym == SDLK_SPACE) { 
		jump = event.type == SDL_KEYDOWN;
	}
}

void CharacterController::OnCollisionEnter(GameObject& other) {
	Logger::Debug("Collision Enter");
	if (other.GetTransform()->GetPosition().y < gameObject.GetTransform()->GetPosition().y)isGrounded = true;
}
void CharacterController::OnCollisionExit(GameObject& other) {
	Logger::Debug("Collision Exit");
}

void CharacterController::registerEvents() {
	*keyEvent += Delegate<void(SDL_Event&)>::Create<CharacterController, &CharacterController::OnKey>(this);
	auto physicsBody = gameObject.GetComponents<PhysicsBody>().front();
	physicsBody->OnCollisionEnter += Delegate<void(GameObject&)>::Create<CharacterController, &CharacterController::OnCollisionEnter>(this);
	physicsBody->OnCollisionExit += Delegate<void(GameObject&)>::Create<CharacterController, &CharacterController::OnCollisionExit>(this);
}
void CharacterController::deregisterEvents() {
	*keyEvent -= Delegate<void(SDL_Event&)>::Create<CharacterController, &CharacterController::OnKey>(this);
	auto physicsBody = gameObject.GetComponents<PhysicsBody>().front();
	physicsBody->OnCollisionEnter -= Delegate<void(GameObject&)>::Create<CharacterController, &CharacterController::OnCollisionEnter>(this);
	physicsBody->OnCollisionExit -= Delegate<void(GameObject&)>::Create<CharacterController, &CharacterController::OnCollisionExit>(this);
}
