#include "GasaCharacter.h"

#include "AbilitySystemComponent.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "Kismet/KismetSystemLibrary.h"

#include "AbilitySystem/GasaAbilitySystemComponent.h"
#include "AbilitySystem/GasaAttributeSet.h"
#include "Components/SkeletalMeshComponent.h"
#include "Engine/PostProcessVolume.h"
#include "Game/GasaGameInstance.h"
#include "Game/GasaLevelScriptActor.h"
#include "Game/GasaPlayerController.h"
#include "Materials/MaterialInstanceDynamic.h"
#include "Networking/GasaNetLibrary_Inlines.h"
using namespace Gasa;

AGasaCharacter::AGasaCharacter()
{
	PrimaryActorTick.bCanEverTick = false;

	HighlightColor = FLinearColor(0.8, 0.32, 0.05f, 1.f);

	UCharacterMovementComponent*
	Movement = GetCharacterMovement();
	Movement->bOrientRotationToMovement = true;
	Movement->bConstrainToPlane         = true;
	Movement->bSnapToPlaneAtStart       = true;
	Movement->RotationRate              = FRotator(0.0, 400.f, 0.0);

	bUseControllerRotationPitch = false;
	bUseControllerRotationRoll  = false;
	bUseControllerRotationYaw   = false;

	USceneComponent*        root_component = GetRootComponent();
	USkeletalMeshComponent* mesh           = GetMesh();

	Weapon = CreateDefaultSubobject<USkeletalMeshComponent>("Weapon");
	Weapon->SetupAttachment(mesh, FName("WeaponAttach"));
	Weapon->SetCollisionEnabled(ECollisionEnabled::NoCollision);

	if (bAutoAbilitySystem)
	{
		AbilitySystem = CreateDefaultSubobject<UGasaAbilitySystemComp>("Ability System");
		AbilitySystem->SetIsReplicated(true);
		AbilitySystem->SetReplicationMode(EGameplayEffectReplicationMode::Minimal);
		
		Attributes = CreateDefaultSubobject<UGasaAttributeSet>("Attributes");
	}

	// Replication
	
	bReplicates            = false;
	bNetLoadOnClient       = true;
	NetDormancy            = DORM_Awake;
	NetCullDistanceSquared = NetCullDist_Medium;
	NetUpdateFrequency     = 30.0f;
	MinNetUpdateFrequency  = 5.0f;
	NetPriority            = 2.0f;

	ACharacter::SetReplicateMovement(true);
}

#pragma region GameFramework
void AGasaCharacter::Controller_OnPawnPossessed()
{
	NetLog("Controller confirmed possession.");

	// Do stuff here that you needed to wait for the player controller be aware of you for.
	BP_Controller_OnPawnPossessed();

	if (Event_OnPawnReady.IsBound())
		Event_OnPawnReady.Broadcast();	
}

void AGasaCharacter::ServerRPC_R_NotifyClientPawnReady_Implementation()
{
	Event_OnPawnReady.Broadcast();
}
#pragma endregion GameFramework

#pragma region Highlight
void AGasaCharacter::SetHighlight(EHighlight Desired)
{
	HighlightState = Desired;
}
#pragma endregion Highlight

#pragma region Pawn
void AGasaCharacter::OnRep_PlayerState()
{
	Super::OnRep_PlayerState();
}

void AGasaCharacter::PossessedBy(AController* NewController)
{
	NetLog("Pawn possessed.");

	AController* OldController;
	
	// APawn::PossessedBy
	{
		SetOwner(NewController);
		OldController = Controller;
		Controller = NewController;

		ForceNetUpdate();

	#if UE_WITH_IRIS
		// The owning connection depends on the Controller having the new value.
		UpdateOwningNetConnection();
	#endif
		
		if (Controller->PlayerState != nullptr)
			SetPlayerState(Controller->PlayerState);

		if (APlayerController* PlayerController = Cast<APlayerController>(Controller))
		{
			if (GetNetMode() != NM_Standalone)
			{
				SetReplicates(true);
				SetAutonomousProxy(true);
			}
		}
		else
			CopyRemoteRoleFrom(GetDefault<APawn>());
	}

	if (AGasaPlayerController* PC = Cast<AGasaPlayerController>(NewController))
	{
		PC->Event_OnPawnPossessed.AddUniqueDynamic(this, & ThisClass::Controller_OnPawnPossessed);
	}
	else
	{
		NetLog("Controller assigned to GasaCharacter is not derived from GasaPlayerController.", ELogV::Warning);
		NetLog("Controller: Name: " + NewController->GetName() + " Class: " + NewController->GetClass()->GetName(), ELogV::Warning);
	}

	// cont. APawn::PossessedBy
	{
		// Dispatch Blueprint event if necessary
		if (OldController != NewController)
		{
			ReceivePossessed(Controller);
			NotifyControllerChanged();
		}
	}

	// ACharacter::PossessedBy
	{
		// If we are controlled remotely, set animation timing to be driven by client's network updates. So timing and events remain in sync.
		if (GetMesh() && IsReplicatingMovement() && (GetRemoteRole() == ROLE_AutonomousProxy && GetNetConnection() != nullptr))
			GetMesh()->bOnlyAllowAutonomousTickPose = true;
	}

#if 0
	if (bAutoAbilitySystem)
	{
		AbilitySystem->InitAbilityActorInfo(this, this);
	}
#endif
}

void AGasaCharacter::SetPlayerDefaults()
{
	Super::SetPlayerDefaults();
}

void AGasaCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);
}
#pragma endregion Pawn

#pragma region Actor
void AGasaCharacter::BeginPlay()
{
	Super::BeginPlay();

	// TODO(Ed): Find out if this is the best spot todo this
	// There is also OnPossessed, PostInitializeComponents, etc...
	if (bAutoAbilitySystem)
	{
		AbilitySystem->InitAbilityActorInfo(this, this);
		Cast<UGasaAbilitySystemComp>(AbilitySystem)->OnAbilityActorInfoSet();
	}
}

void AGasaCharacter::Tick(float DeltaSeconds)
{
	Super::Tick(DeltaSeconds);
	
	USkeletalMeshComponent* mesh = GetMesh();
	switch (HighlightState)
	{
		case EHighlight::Disabled:
		{
			mesh->SetRenderCustomDepth(false);
			mesh->SetCustomDepthStencilValue(0.f);
			Weapon->SetRenderCustomDepth(false);
			Weapon->SetCustomDepthStencilValue(0.f);
		}	
		break;
		case EHighlight::Enabled:
		{
			mesh->SetRenderCustomDepth(true);
			mesh->SetCustomDepthStencilValue(HighlightStencilDepth);
			Weapon->SetRenderCustomDepth(true);
			Weapon->SetCustomDepthStencilValue(HighlightStencilDepth);

			APostProcessVolume* PPV     = Gasa::GetLevelActor(this)->GlobalPPV;
			TObjectPtr<UObject> Blendable = PPV->Settings.WeightedBlendables.Array[0].Object;
			UMaterialInstanceDynamic*
			MID = Cast<UMaterialInstanceDynamic>(Blendable);
			MID->SetVectorParameterValue("Depth Highlight Color", HighlightColor);
#if 0
			UCapsuleComponent* Capsule = GetCapsuleComponent();
			UKismetSystemLibrary::DrawDebugCapsule(this
				, Capsule->GetComponentLocation()
				, Capsule->GetScaledCapsuleHalfHeight()^
				, Capsule->GetScaledCapsuleRadius()
				, Capsule->GetComponentRotation()
				, HighlightColor
				, 0.f
				, 1.f
			);
#endif
		}
		break;
	}
}
#pragma endregion Actor
