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


#include "BountyHunterCharacter.h"
#include "Kismet/GameplayStatics.h"
#include "EnhancedInputComponent.h"

void ABountyHunterCharacter::SpawnMainBounty(UEVGameInstance* GI)
{
	CurrentMainBountyIndex = GI->MainSaveGameInstanceRef->LastMainBountyIndexInOpenWorld;

	if (GI->MainSaveGameInstanceRef->CurrentMainBountyIndexSave > CurrentMainBountyIndex) CompleteCurrentMainBounty(GI);
		
	if (MainBountyClasses.IsEmpty() || MainBountyClasses.Num() <= CurrentMainBountyIndex|| !IsValid(MainBountyClasses[CurrentMainBountyIndex]))
	{
		AllBountiesCompleted();
		return;
	}

	CurrentMainBounty = GetWorld()->SpawnActor<AMainBountyClass>(MainBountyClasses[CurrentMainBountyIndex]);
	const FAttachmentTransformRules AttachmentTransformRules(EAttachmentRule::SnapToTarget, true);
	CurrentMainBounty->AttachToComponent(GetRootComponent(), AttachmentTransformRules);

	MainBountyStruct = CurrentMainBounty->MainBountyStruct;
	if (UGameplayStatics::GetCurrentLevelName(GetWorld()) != OpenWorldLevelName)
	{
		CurrentMainBounty->SpawnCheckpoints();
		return;
	}
	CurrentMainBounty->SpawnOpenWorldCheckpoint();
	CurrentSideBounties = CurrentMainBounty->SpawnAndReturnSideBounties();

	SideBountyStructs.Empty();
	for (auto SideBounty : CurrentSideBounties)
	{
		SideBountyStructs.Add(SideBounty->SideBountyStruct);
		SideBounty->CompletedSideBounty.AddDynamic(this, &ABountyHunterCharacter::CompletedASideBounty);
	}
}

void ABountyHunterCharacter::CompleteCurrentMainBounty(UEVGameInstance* GI)
{
	EarnMoney(MainBountyClasses[CurrentMainBountyIndex]->GetDefaultObject<AMainBountyClass>()->GetRewardMoney());
	if (IsValid(PauseMenu)) PauseMenu->UpdatePlayerStatistics(Money, Favours);
	UE_LOG(LogTemp, Warning, TEXT("Would be collecting reward for completing bounty %d"), CurrentMainBountyIndex);

	// Saves players spawn now as players location is set after spawn main bounty finishes running in begin play
	GI->MainSaveGameInstanceRef->PlayerOpenWorldSpawnTransformSave = MainBountyClasses[CurrentMainBountyIndex]->GetDefaultObject<AMainBountyClass>()->GetPlayerSpawnTransform();
	CurrentMainBountyIndex = GI->MainSaveGameInstanceRef->CurrentMainBountyIndexSave;
	GI->MainSaveGameInstanceRef->LastMainBountyIndexInOpenWorld = CurrentMainBountyIndex;
	UGameplayStatics::SaveGameToSlot(GI->MainSaveGameInstanceRef, "MainSave", 0);
}

void ABountyHunterCharacter::CompletedASideBounty(int CompletedSideBountiesUID)
{
	for (auto SBStruct : SideBountyStructs)
	{
		if (SBStruct.SideBountyUID != CompletedSideBountiesUID) continue;
		EarnMoney(SBStruct.MoneyEarnedForCompletion);
		EarnFavours(SBStruct.FavoursEarnedForCompletion);
		if (IsValid(PauseMenu)) PauseMenu->UpdatePlayerStatistics(Money, Favours);
	}
}

void ABountyHunterCharacter::AllBountiesCompleted()
{
	UE_LOG(LogTemp, Warning, TEXT("Game Completed!!!! No more Bounties left... or you forgot to set a bounty at one of the indexes oof"));
}

void ABountyHunterCharacter::SavePlayerMoneyAndFavours()
{
	UEVGameInstance* GI = Cast<UEVGameInstance>(GetGameInstance());
	if (!IsValid(GI->MainSaveGameInstanceRef)) return;
	GI->MainSaveGameInstanceRef->MoneySave = Money;
	GI->MainSaveGameInstanceRef->FavourSave = Favours;
	UGameplayStatics::SaveGameToSlot(GI->MainSaveGameInstanceRef, "MainSave", 0);
}

void ABountyHunterCharacter::BeginPlay()
{
	UEVGameInstance* GI = Cast<UEVGameInstance>(GetGameInstance());
	if (IsValid(GI->MainSaveGameInstanceRef))
	{
		Money = GI->MainSaveGameInstanceRef->MoneySave;
		Favours = GI->MainSaveGameInstanceRef->FavourSave;
		SpawnMainBounty(GI);
		FTransform SpawnLoc = GI->MainSaveGameInstanceRef->PlayerOpenWorldSpawnTransformSave;
		if (SpawnLoc.GetLocation() != FVector(0, 0, 0) && UGameplayStatics::GetCurrentLevelName(GetWorld()) == OpenWorldLevelName)
		{
			SetActorTransform(GI->MainSaveGameInstanceRef->PlayerOpenWorldSpawnTransformSave);
			Cast<APlayerController>(GetController())->SetControlRotation(GI->MainSaveGameInstanceRef->PlayerOpenWorldSpawnTransformSave.GetRotation().Rotator());
		}
	}
	CreatePauseMenuTabs();
	Super::BeginPlay();
}

void ABountyHunterCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	if (UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent))
	{
		EnhancedInputComponent->BindAction(PauseMenuAction, ETriggerEvent::Started, this, &ABountyHunterCharacter::TogglePauseMenu);
	}
	
	UE_LOG(LogTemp, Warning, TEXT("Setup player input on bounty hunter character"));

	Super::SetupPlayerInputComponent(PlayerInputComponent);
}

void ABountyHunterCharacter::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
	Super::EndPlay(EndPlayReason);
	if (UGameplayStatics::GetCurrentLevelName(GetWorld()) != OpenWorldLevelName) return;
	UEVGameInstance* GI = Cast<UEVGameInstance>(GetGameInstance());
	if (!IsValid(GI->MainSaveGameInstanceRef)) return;
	TArray<AActor*> LandingZones;
	ALandingZone* ClosestLandingZone = nullptr;
	double DistToClosestLandingZone;
	UGameplayStatics::GetAllActorsOfClass(GetWorld(), ALandingZone::StaticClass(), LandingZones);
	for (AActor* LZ_Actor : LandingZones)
	{
		ALandingZone* LZ = Cast<ALandingZone>(LZ_Actor);
     	double DistToLZ = FVector::Dist(LZ->GetExitTransform().GetLocation(), GetActorLocation());
		if (!IsValid(ClosestLandingZone))
		{
			ClosestLandingZone = LZ;
			DistToClosestLandingZone = DistToLZ;
			continue;
		}
		ClosestLandingZone = DistToClosestLandingZone >= DistToLZ ? LZ : ClosestLandingZone;
		DistToClosestLandingZone = DistToClosestLandingZone >= DistToLZ ? DistToLZ : DistToClosestLandingZone;
	}
	if (!IsValid(ClosestLandingZone)) return;
	GI->MainSaveGameInstanceRef->PlayerOpenWorldSpawnTransformSave = ClosestLandingZone->GetExitTransform();
	UGameplayStatics::SaveGameToSlot(GI->MainSaveGameInstanceRef, "MainSave", 0);
}

void ABountyHunterCharacter::CreatePauseMenuTabs()
{
	if (CurrentMainBountyIndex <= 0)
	{
		BountiesTab = CreateWidget<UBountiesTab>(GetWorld(), TutorialBountyTabWidgetClass);
	}
	else
	{
		BountiesTab = CreateWidget<UBountiesTab>(GetWorld(), UGameplayStatics::GetCurrentLevelName(GetWorld()) == OpenWorldLevelName ? BountyTabWidgetClass : BountyInProgressTabWidgetClass);
		BountiesTab->ReturnToOpenWorldEarlyDelegate.AddDynamic(this, &ABountyHunterCharacter::ReturnToOpenWorldEarly);
		BountiesTab->TrackBountyDelegate.AddDynamic(this, &ABountyHunterCharacter::TrackMainBounty);
		BountiesTab->TrackSideBountyDelegate.AddDynamic(this, &ABountyHunterCharacter::TrackSideBounty);
	}	
	UpdateBountyTabInfo();
	FavoursTab = CreateWidget<UTabWidget>(GetWorld(), FavoursTabWidgetClass);
	NavigationTab = CreateWidget<UTabWidget>(GetWorld(), NavigationTabWidgetClass);
	SettingsTab = CreateWidget<UTabWidget>(GetWorld(), SettingsTabWidgetClass);
	QuitTab = CreateWidget<UQuitTab>(GetWorld(), QuitTabWidgetClass);
}

void ABountyHunterCharacter::TogglePauseMenu()
{
	const bool bOpenNewPauseMenu = !IsValid(PauseMenu);
	FInputModeGameAndUI UIAndGameOnly;
	FInputModeGameOnly GameOnly;
	APlayerController* PC = Cast<APlayerController>(GetController());
	
	InPauseMenu = bOpenNewPauseMenu;

	bOpenNewPauseMenu ? PC->SetInputMode(UIAndGameOnly) : PC->SetInputMode(GameOnly);
	PC->SetIgnoreMoveInput(bOpenNewPauseMenu);
	PC->SetIgnoreLookInput(bOpenNewPauseMenu);
	PC->SetShowMouseCursor(bOpenNewPauseMenu);
	
	if (!bOpenNewPauseMenu)
	{
		PauseMenu->RemoveFromParent();
		PauseMenu = nullptr;
		return;
	}
	PauseMenu = CreateWidget<UPauseMenuClass>(GetWorld(), PauseMenuWidgetClass, FName("PauseMenu"));
	PauseMenu->UpdatePlayerStatistics(Money, Favours);
	PauseMenu->AddToViewport(50);
	PauseMenu->OpenNewTab(BountiesTab);
	PauseMenu->OpenedBountiesTab.AddDynamic(this, &ABountyHunterCharacter::OpenBountiesTab);
	PauseMenu->OpenedFavoursTab.AddDynamic(this, &ABountyHunterCharacter::OpenFavoursTab);
	PauseMenu->OpenedNavigationTab.AddDynamic(this, &ABountyHunterCharacter::OpenNavigationTab);
	PauseMenu->OpenedSettingsTab.AddDynamic(this, &ABountyHunterCharacter::OpenSettingsTab);
	PauseMenu->OpenedQuitTab.AddDynamic(this, &ABountyHunterCharacter::OpenQuitTab);
	PauseMenu->SetVisibility(ESlateVisibility::Visible);
	PauseMenu->SetFocus();
}

void ABountyHunterCharacter::ReturnToOpenWorldEarly()
{
	UEVGameInstance* GI = Cast<UEVGameInstance>(GetGameInstance());
	if (!IsValid(GI->MainSaveGameInstanceRef)) return;
	FTransform SpawnTransform =  MainBountyClasses[CurrentMainBountyIndex]->GetDefaultObject<AMainBountyClass>()->GetPlayerSpawnTransform();	

	GI->MainSaveGameInstanceRef->PlayerOpenWorldSpawnTransformSave = SpawnTransform;
	UGameplayStatics::SaveGameToSlot(GI->MainSaveGameInstanceRef, "MainSave", 0);
	UGameplayStatics::OpenLevel(GetWorld(), FName(OpenWorldLevelName));
}
