// Fill out your copyright notice in the Description page of Project Settings.

#include "ld35.h"
#include "UcustomAnimInstance.h"
#include "mainCharacter.h"
#include "Enemy.h"


// Sets default values
AmainCharacter::AmainCharacter() {

 	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

}

// Called when the game starts or when spawned
void AmainCharacter::BeginPlay() {

	Super::BeginPlay();
	
	camera			= (UCameraComponent *)			GetComponentByClass(UCameraComponent::StaticClass());
	humanMesh		= (USkeletalMeshComponent *)	GetComponentByName("Human");
	birdMesh		= (UStaticMeshComponent *)		GetComponentByName("Bird");

	if(humanMesh)	animationInstance = Cast<UcustomAnimInstance>( humanMesh->GetAnimInstance() );
	if(birdMesh)	birdSize = birdMesh->GetComponentScale();

	characterRotation = 0.0f;
	attackTimer = 100.0f;

	transformed			= false;
	attacking			= false;
	attackButtonDown	= false;
	
}

// Called every frame
void AmainCharacter::Tick( float DeltaTime ) {

	Super::Tick( DeltaTime );
	
	static const float transformSpeed = 4.0f;
	static const float attackTime = 0.7f;


	if(animationInstance) {
			
		animationInstance->Speed = FMath::Lerp(animationInstance->Speed, movementDirection.Size(), 0.04f);
		
	}
	
	if(humanMesh) {
	
		humanMesh->SetRelativeScale3D(FVector(1.0 - transformValue));
		if(transformValue > 0.999f)		humanMesh->SetVisibility(false);
		else							humanMesh->SetVisibility(true);
	
	}

	if(birdMesh) {
	
		birdMesh->SetRelativeScale3D(birdSize * transformValue);
		if(transformValue < 0.001f)		birdMesh->SetVisibility(false);
		else							birdMesh->SetVisibility(true);
	
	}
		
	if(transformed) movementDirection.Z += 1.0f;

	attackTimer += DeltaTime;
	if(attackTimer > attackTime) {
	
		if(animationInstance) animationInstance->Hit = false;
		attacking = false;

	}


	// Cap movement length to one
	float movementLength = movementDirection.Size();
	if(movementLength > 1.0f) movementLength /= movementLength;


	// Rotation
	float wantedRotation = lookRotation.Yaw + 90.0f + (180.0f / (float) PI) * atan2(movementDirection.X, movementDirection.Z);
	if(movementLength > 0.1f && attackTimer > attackTime) {
		
		// Keep in range
		while(wantedRotation	< 0.0f)			wantedRotation += 360.0f;
		while(wantedRotation	> 360.0f)		wantedRotation -= 360.0f;
		while(characterRotation	< 0.0f)			characterRotation += 360.0f;
		while(characterRotation	> 360.0f)		characterRotation -= 360.0f;
		

		// Get closest
		if(fabs(characterRotation - wantedRotation) > fabs(characterRotation - (wantedRotation + 360.0f))) wantedRotation += 360.0f;
		if(fabs(characterRotation - wantedRotation) > fabs(characterRotation - (wantedRotation - 360.0f))) wantedRotation -= 360.0f;


		// Rotate
		characterRotation = FMath::Lerp(characterRotation, wantedRotation, 0.06f);
	}

	
	if(!transformed) {
		
		FVector forward	= lookRotation.RotateVector(FVector::ForwardVector);
		FVector right	= lookRotation.RotateVector(FVector(0.0f, 1.0f, 0.0f));
		humanMesh->SetWorldRotation(FRotator(0.0f, characterRotation, 0.0f));
		
		if(attackTimer < attackTime) {
			
			AddMovementInput(FVector(cos((characterRotation - 90.0f) * PI / 180.0f), sin((characterRotation - 90.0f) * PI / 180.0f), 0.0f),	40.0f * DeltaTime);

		} else {

			float movementSpeed = 40.0f * DeltaTime;
			AddMovementInput(forward,	movementSpeed * movementDirection.Z);
			AddMovementInput(right,		movementSpeed * movementDirection.X);

		}

		transformValue -= transformSpeed * DeltaTime;
		if(transformValue < 0.0f) transformValue = 0.0f;

	} else {

		FQuat MeshRotation;
		MeshRotation.MakeFromEuler(FVector(0.0f, 0.0f, 90.0f));
		birdMesh->SetWorldRotation(lookRotation.Quaternion() * MeshRotation);
		AddMovementInput(lookRotation.RotateVector(FVector::ForwardVector),		480.0f * DeltaTime);
		
		transformValue += transformSpeed * DeltaTime;
		if(transformValue > 1.0f) transformValue = 1.0f;

	}

}

// Called to bind functionality to input
void AmainCharacter::SetupPlayerInputComponent(class UInputComponent* InputComponent) {

	Super::SetupPlayerInputComponent(InputComponent);
	
	InputComponent->BindAxis("MoveForward",	this, &AmainCharacter::MoveForward);
	InputComponent->BindAxis("MoveRight",	this, &AmainCharacter::MoveRight);
	
	InputComponent->BindAxis("LookSide",	this, &AmainCharacter::LookSide);
	InputComponent->BindAxis("LookUp",		this, &AmainCharacter::LookUp);
	
	InputComponent->BindAxis("Transform",	this, &AmainCharacter::Transform);
	InputComponent->BindAxis("Attack",		this, &AmainCharacter::Attack);

}


void AmainCharacter::MoveForward(float Value) {

	movementDirection.Z = Value;

}


void AmainCharacter::MoveRight(float Value) {

	movementDirection.X = Value;
	
}


void AmainCharacter::LookSide(float Value) {
	
	lookRotation.Yaw += Value;
	if(camera) {
	
		camera->SetWorldRotation(lookRotation);
		camera->SetWorldLocation(GetActorLocation() + FVector(0.0f, 0.0f, 50.0f) + camera->GetForwardVector() * -160.0f);

	}

}


void AmainCharacter::LookUp(float Value) {

	lookRotation.Pitch += Value;
	if(camera) {
	
		camera->SetWorldRotation(lookRotation);
		camera->SetWorldLocation(GetActorLocation() + FVector(0.0f, 0.0f, 50.0f) + camera->GetForwardVector() * -160.0f);

	}

}


void AmainCharacter::Transform(float Value) {

	bool lastTransformed = transformed;

	if(Value > 0.5f)	transformed = true;
	else				transformed = false;

	if(lastTransformed != transformed) {
	
		if(transformed)		ClientCheatFly_Implementation();
		else				ClientCheatWalk_Implementation();

	}

}


void AmainCharacter::Attack(float Value) {

	if(Value > 0.5f) {
	
		if(!attackButtonDown && !attacking) {
			
			if(animationInstance) {
				
				animationInstance->Hit = true;
				attackTimer = 0.0f;

			}

			for(TActorIterator<AEnemy> ActorItr(GetWorld()); ActorItr; ++ActorItr) {

				auto enemy = *ActorItr;
				enemy->hit();

			}

			attacking = true;

		}

		attackButtonDown = true;

	} else attackButtonDown = false;

}


bool AmainCharacter::isTransformed() {

	return transformed;
	   
}


UActorComponent *AmainCharacter::GetComponentByName(const char *pName) {
	
	auto components =  GetComponents();
	for(auto component : components) {
		
		if(component->GetName() == pName) return component;

	}

	return nullptr;

}

