// Gravity Stomp Copyright Kevin Poretti

#include "GSCharacter.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"
#include "Components/InputComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "GameFramework/Controller.h"
#include "GameFramework/SpringArmComponent.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "GSCharacterMovementComponent.h"
#include "GameFramework/PlayerState.h"
#include "Kismet/GameplayStatics.h"
#include "Kismet/KismetMathLibrary.h"
#include "Player/GSPlayerState.h"


//////////////////////////////////////////////////////////////////////////
// AGravityStompCharacter

class AGSPlayerState;

AGSCharacter::AGSCharacter(const FObjectInitializer& ObjectInitializer)
    : Super(ObjectInitializer.SetDefaultSubobjectClass<UGSCharacterMovementComponent>(ACharacter::CharacterMovementComponentName))
{
	// Set size for collision capsule
	GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);
		
	// Don't rotate when the controller rotates. Let that just affect the camera.
	bUseControllerRotationPitch = false;
	bUseControllerRotationYaw = false;
	bUseControllerRotationRoll = false;

	// Configure character movement
	GetCharacterMovement()->bOrientRotationToMovement = true; // Character moves in the direction of input...	
	GetCharacterMovement()->RotationRate = FRotator(0.0f, 500.0f, 0.0f); // ...at this rotation rate

	// Note: For faster iteration times these variables, and many more, can be tweaked in the Character Blueprint
	// instead of recompiling to adjust them
	GetCharacterMovement()->JumpZVelocity = 700.f;
	GetCharacterMovement()->AirControl = 0.35f;
	GetCharacterMovement()->MaxWalkSpeed = 500.f;
	GetCharacterMovement()->MinAnalogWalkSpeed = 20.f;
	GetCharacterMovement()->BrakingDecelerationWalking = 2000.f;

	// Note: The skeletal mesh and anim blueprint references on the Mesh component (inherited from Character) 
	// are set in the derived blueprint asset named ThirdPersonCharacter (to avoid direct content references in C++)
}


void AGSCharacter::ChangeTeamColor(bool bIsPlayerFriendly)
{
	int32 TeamNum = GetPlayerState<AGSPlayerState>()->GetTeamNum();

	// invalid team num
    if(TeamNum < 0 || TeamNum >= PlayerMaterials.Num())
	{
	    return;
	}

	GetMesh()->SetMaterial(0, PlayerMaterials[TeamNum].TorsoMaterial);
	GetMesh()->SetMaterial(1, PlayerMaterials[TeamNum].HeadLegsMaterial);
}


void AGSCharacter::BeginPlay()
{
	// Call the base class  
	Super::BeginPlay();

	//Add Input Mapping Context
	if (APlayerController* PlayerController = Cast<APlayerController>(Controller))
	{
		if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
		{
			Subsystem->AddMappingContext(DefaultMappingContext, 0);
		}
	}
}

//////////////////////////////////////////////////////////////////////////
// Input

void AGSCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
{
	// Set up action bindings
	if (UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent)) 
	{
		//Moving
		EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AGSCharacter::Move);
		EnhancedInputComponent->BindAction(ChangeGravityAction, ETriggerEvent::Triggered, this, &AGSCharacter::ChangeGravityDirection);
	}

}


void AGSCharacter::Move(const FInputActionValue& Value)
{
    if(UGameplayStatics::GetGlobalTimeDilation(this) < 1.0f || IsMoveInputIgnored())
    {
        return;
    }

	// input is a Vector2D
	FVector2D MovementVector = Value.Get<FVector2D>();

	// add movement 
	AddMovementInput(FVector::UpVector, MovementVector.Y);
	AddMovementInput(FVector::RightVector, MovementVector.X);
}


void AGSCharacter::ChangeGravityDirection(const FInputActionValue& Value)
{
    if(!bCanChangeGravity || UGameplayStatics::GetGlobalTimeDilation(this) < 1.0f || IsMoveInputIgnored())
    {
        return;
    }

	FVector2D GravityDirection = Value.Get<FVector2D>();
	FVector NewCharacterUpDirection(0.0f, -GravityDirection.X, -GravityDirection.Y);

	FRotator CharRot = GetActorRotation();
	if(GetCharacterMovement<UGSCharacterMovementComponent>()->IsCharacterUpAlignedToWorldUp())
	{
	    CharRot = FRotator(0.0f, CharRot.Yaw, 0.0f);
	}
	else
	{
	    CharRot = FRotator(CharRot.Pitch, 0.0f, 0.0f);
	}

	float NewRoll = GetCharacterMovement<UGSCharacterMovementComponent>()->GetRollFromCharacterUpDir(NewCharacterUpDirection);
	FRotator RollRot = UKismetMathLibrary::RotatorFromAxisAndAngle(FVector::ForwardVector, NewRoll);

	SetActorRotation(UKismetMathLibrary::ComposeRotators(RollRot, CharRot));

	GetCharacterMovement<UGSCharacterMovementComponent>()->SetCharacterUpDirection(NewCharacterUpDirection);
}

void AGSCharacter::PawnClientRestart()
{
    Super::PawnClientRestart();

	// Make sure that we have a valid PlayerController.
    if (APlayerController* PC = Cast<APlayerController>(GetController()))
    {
        // Get the Enhanced Input Local Player Subsystem from the Local Player related to our Player Controller.
        if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PC->GetLocalPlayer()))
        {
            // PawnClientRestart can run more than once in an Actor's lifetime, so start by clearing out any leftover mappings.
            Subsystem->ClearAllMappings();

            // Add each mapping context, along with their priority values. Higher values outprioritize lower values.
            Subsystem->AddMappingContext(DefaultMappingContext, 0);
        }
    }
}

void AGSCharacter::PossessedBy(AController* NewController)
{
    Super::PossessedBy(NewController);
}
