// Project Lab - NHTV Igad

#include "UnrealProject.h"
#include "DefaultGameInstance.h"
#include "PlayerControllerBase.h"
#include "ScreenOverlay.h"
#include "Prefs.h"
#include "TransitionScreen.h"
#include "SessionManager.h"
#include "Engine.h"

#if PLATFORM_SPECIFIC_WIN == 0
#include "InputManager.hpp"
using namespace Input;
#endif

UClass* overlayWidgetClass;
UClass* transitionWidgetClass;
APlayerControllerBase::APlayerControllerBase(const FObjectInitializer& init)
	: Super(init)
{
	overlayWidgetClass = ConstructorHelpers::FClassFinder<UScreenOverlay>(TEXT("/Game/Assets/GUI/WEEGEE_Overlay")).Class;
	transitionWidgetClass = ConstructorHelpers::FClassFinder<UTransitionScreen>(TEXT("/Game/Assets/GUI/WEEGEE_Transition")).Class;
}

void APlayerControllerBase::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);
	DOREPLIFETIME(APlayerControllerBase, setupState);
}

void APlayerControllerBase::BeginPlay()
{
	UDefaultGameInstance* game = Cast<UDefaultGameInstance>(GetGameInstance());

	if(IsLocalController())
	{
		transitionScreen = CreateWidget<UTransitionScreen>(GetWorld(), transitionWidgetClass);
		transitionScreen->AddToViewport(800);

		overlay = CreateWidget<UScreenOverlay>(GetWorld(), overlayWidgetClass);
		overlay->AddToViewport(1000);
		AddMenuInputItem(overlay, 10);
		check(overlay);

		UWorld* world = GetWorld();
		AGameMode* gameMode = nullptr;
		if(world)
			gameMode = GetWorld()->GetAuthGameMode();
		//if(!game->errorMessage.IsEmpty())
		//{
		//	game->DestroySession(false);
		//	game->ShowNetworkError(FString("Disconnected from game, ") + game->errorMessage);
		//	game->errorMessage.Empty();
		//}
		//else if(gameMode && UGameplayStatics::HasOption(gameMode->OptionsString, "closed"))
		//{
		//	game->DestroySession(false);
		//	game->ShowNetworkError(FString("Disconnected from game"));
		//}
	}

	Super::BeginPlay();
}
void APlayerControllerBase::SetupInputComponent()
{
	if (GetWorld()->WorldType == EWorldType::Preview)
		return;

	Super::SetupInputComponent();


#if PLATFORM_SPECIFIC_WIN == 0
	GPRINT("Binding Menu Input manager bindings on " + GetName());
	InputManager* inputManager = InputManager::GetInstance();

	UPrefs* prefs = Cast<UDefaultGameInstance>(GetGameInstance())->GetPrefs();

	// Connect initial controller
	inputManager->SearchForJoystick();
	ScanForJoysticks();
	int32 connect = 0;
	for(int32 i = 0; i < m_recentJoystickScan.Num(); i++)
	{
		if(m_recentJoystickScan[i] == prefs->useControllerName)
		{
			connect = i;
			break;
		}
	}
	SelectJoystick(connect);

	inputManager->RegisterCallback(IJP_FACEDOWN, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuConfirm);
	inputManager->RegisterCallback(IJP_FACERIGHT,IME_PRESSED, this, &APlayerControllerBase::m_OnMenuBack);
	inputManager->RegisterCallback(IJP_START,	 IME_PRESSED, this, &APlayerControllerBase::m_OnMenuStart);
	inputManager->RegisterCallback(IJP_DPADUP,	 IME_PRESSED, this, &APlayerControllerBase::m_OnMenuUp);
	inputManager->RegisterCallback(IJP_DPADDOWN, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuDown);
	inputManager->RegisterCallback(IJP_DPADLEFT, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuLeft);
	inputManager->RegisterCallback(IJP_DPADRIGHT, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuRight);
	inputManager->RegisterCallback(IJP_TRIGGERLEFT, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuLeftPressed);
	inputManager->RegisterCallback(IJP_TRIGGERRIGHT, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuRightPressed);
	inputManager->RegisterCallback(IJP_TRIGGERLEFT, IME_RELEASED, this, &APlayerControllerBase::m_OnMenuLeftReleased);
	inputManager->RegisterCallback(IJP_TRIGGERRIGHT, IME_RELEASED, this, &APlayerControllerBase::m_OnMenuRightReleased);
	inputManager->RegisterCallback(IJP_SHOULDERLEFT, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuShoulderLeft);
	inputManager->RegisterCallback(IJP_SHOULDERRIGHT, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuShoulderRight);
	inputManager->RegisterCallback(IJP_START, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuOptionsButton);
	inputManager->RegisterCallback(IJP_FACELEFT, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuOpt1);
	inputManager->RegisterCallback(IJP_FACEUP, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuOpt2);
#endif

	InputComponent->BindAction("MenuSelect", EInputEvent::IE_Pressed, this, &APlayerControllerBase::m_OnMenuConfirm);
	InputComponent->BindAction("MenuBack", EInputEvent::IE_Pressed, this, &APlayerControllerBase::m_OnMenuBack);
	InputComponent->BindAction("MenuStart", EInputEvent::IE_Pressed, this, &APlayerControllerBase::m_OnMenuStart);
	InputComponent->BindAction("MenuUp", EInputEvent::IE_Pressed, this, &APlayerControllerBase::m_OnMenuUp);
	InputComponent->BindAction("MenuDown", EInputEvent::IE_Pressed, this, &APlayerControllerBase::m_OnMenuDown);
	InputComponent->BindAction("MenuLeft", EInputEvent::IE_Pressed, this, &APlayerControllerBase::m_OnMenuLeftPressed);
	InputComponent->BindAction("MenuRight", EInputEvent::IE_Pressed, this, &APlayerControllerBase::m_OnMenuRightPressed);
	InputComponent->BindAction("MenuLeftUp", EInputEvent::IE_Released, this, &APlayerControllerBase::m_OnMenuLeftReleased);
	InputComponent->BindAction("MenuRightUp", EInputEvent::IE_Released, this, &APlayerControllerBase::m_OnMenuRightReleased);
	InputComponent->BindAction("MenuOpt1", EInputEvent::IE_Pressed, this, &APlayerControllerBase::m_OnMenuOpt1);
	InputComponent->BindAction("MenuOpt2", EInputEvent::IE_Pressed, this, &APlayerControllerBase::m_OnMenuOpt2);
	InputComponent->BindAction("MenuShoulderLeft", EInputEvent::IE_Pressed, this, &APlayerControllerBase::m_OnMenuShoulderLeft);
	InputComponent->BindAction("MenuShoulderRight", EInputEvent::IE_Pressed, this, &APlayerControllerBase::m_OnMenuShoulderRight);
}
void APlayerControllerBase::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
	if(IsLocalController())
	{
		if(transitionScreen)
		{
			transitionScreen->OnHide();
			transitionScreen->RemoveFromViewport();
		}
		if(overlay)
		{
			overlay->RemoveFromViewport();
		}
	}
	Super::EndPlay(EndPlayReason);
}

void APlayerControllerBase::PlayerTick(float DeltaTime)
{
	Super::PlayerTick(DeltaTime);
	if(m_repeatLeft)
		OnMenuAction(EMenuActionBinding::Repeat_Left);
	if(m_repeatRight)
		OnMenuAction(EMenuActionBinding::Repeat_Right);
}

void APlayerControllerBase::Destroyed()
{
	Super::Destroyed();
#if PLATFORM_SPECIFIC_WIN == 0
	GPRINT("Unbinding Menu Input manager bindings on " + GetName());
	InputManager* inputManager = InputManager::GetInstance();

	inputManager->DeregisterCallback(IJP_FACEDOWN, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuConfirm);
	inputManager->DeregisterCallback(IJP_FACERIGHT, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuBack);
	inputManager->DeregisterCallback(IJP_START, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuStart);
	inputManager->DeregisterCallback(IJP_DPADUP, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuUp);
	inputManager->DeregisterCallback(IJP_DPADDOWN, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuDown);
	inputManager->DeregisterCallback(IJP_DPADLEFT, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuLeft);
	inputManager->DeregisterCallback(IJP_DPADRIGHT, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuRight);
	inputManager->DeregisterCallback(IJP_TRIGGERLEFT, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuLeftPressed);
	inputManager->DeregisterCallback(IJP_TRIGGERRIGHT, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuRightPressed);
	inputManager->DeregisterCallback(IJP_TRIGGERLEFT, IME_RELEASED, this, &APlayerControllerBase::m_OnMenuLeftReleased);
	inputManager->DeregisterCallback(IJP_TRIGGERRIGHT, IME_RELEASED, this, &APlayerControllerBase::m_OnMenuRightReleased);
	inputManager->DeregisterCallback(IJP_SHOULDERLEFT, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuShoulderLeft);
	inputManager->DeregisterCallback(IJP_SHOULDERRIGHT, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuShoulderRight);
	inputManager->DeregisterCallback(IJP_START, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuOptionsButton);
	inputManager->DeregisterCallback(IJP_FACELEFT, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuOpt1);
	inputManager->DeregisterCallback(IJP_FACEUP, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuOpt2);
#endif
}

void APlayerControllerBase::OnLearnSkill(class UBaseSkillObject* object)
{
}
void APlayerControllerBase::OnUnlearnSkill(class UBaseSkillObject* object)
{
}

void APlayerControllerBase::AddMenuInputItem(class UMenuItemBase* item, int32 priority)
{
	if(!item)
		return;
	if(m_menuItemStack.Contains(item))
		return;
	item->priority = priority;

	int32 at = m_menuItemStack.Num();
	if(at == -1)
	{
		m_menuItemStack.Add(item);
		return;
	}

	while(at > 0 && m_menuItemStack[at-1]->priority > priority)
	{
		at--;
	}
	m_menuItemStack.Insert(item, at);
}
void APlayerControllerBase::RemoveMenuInputItem(class UMenuItemBase* item)
{
	m_menuItemStack.Remove(item);
}

EInputMethod APlayerControllerBase::GetInputMethod() const
{
#if PLATFORM_SPECIFIC_WIN == 0
	InputManager* inputManager = InputManager::GetInstance();
	switch(inputManager->GetConnectedType())
	{
	default:
	case InputDeviceType::IDT_NONE:
	case InputDeviceType::IDT_NOTDEFINED:
		return EInputMethod::PC;
		break;
	case InputDeviceType::IDT_XBOX:
		return EInputMethod::X360;
		break;
	case InputDeviceType::IDT_DS4:
		return EInputMethod::DS4;
		break;
	}
#else
	return EInputMethod::DS4;
#endif
}

TArray<FString> APlayerControllerBase::ScanForJoysticks()
{
#if PLATFORM_SPECIFIC_WIN == 0
	m_recentJoystickScan.SetNum(0);
	InputManager* inputManager = InputManager::GetInstance();
	inputManager->SearchForJoystick();
	for(std::wstring s : inputManager->GetJoystickNames())
	{
		m_recentJoystickScan.Add(FString(s.c_str()));
	}
#endif
	return m_recentJoystickScan;
}
void APlayerControllerBase::SelectJoystick(int32 joystick)
{
#if PLATFORM_SPECIFIC_WIN == 0
	if(joystick >= m_recentJoystickScan.Num())
		return;

	InputManager* inputManager = InputManager::GetInstance();
	GWPRINT(L"Connecting joystick [" + joystick + L"] " + m_recentJoystickScan[joystick]);
	inputManager->ConnectJoystickByIndex(joystick);
#endif
}

void APlayerControllerBase::PreClientTravel(const FString& PendingURL, ETravelType TravelType, bool bIsSeamlessTravel)
{
	GPRINT("PRE TRAVEL: " + PendingURL);
	Super::PreClientTravel(PendingURL, TravelType, bIsSeamlessTravel);
}

void APlayerControllerBase::OnEnterLobby()
{
	GWARNING("PC: Entering lobby " + GetName());
}
void APlayerControllerBase::ReturnToMenu()
{
	GWARNING("PC: Leaving game " + GetName());
	UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(GetGameInstance());
	inst->sessionManager->DestroySession();
	inst->sessionManager->CloseNetConnections();

	// Load menu level
	//FString error;
	//FWorldContext* worldContext = GEngine->GetWorldContextFromWorld(GetWorld());
	//FURL target = FURL(*inst->menuLevelPath);
	//int32 ret = GEngine->Browse(*worldContext, target, error);
	//if(ret == EBrowseReturnVal::Failure)
	//{
	//	GERROR("Failed to load menu level: " + error);
	//}
	GetWorld()->ServerTravel(inst->menuLevelPath, true);
}

void APlayerControllerBase::OnRep_PlayerState()
{
	Super::OnRep_PlayerState();
	GPRINT("[" + GetName() + "]Got player state");
}
void APlayerControllerBase::OnRep_Pawn()
{
	Super::OnRep_Pawn();
	if(GetPawn())
	{
		GPRINT("[" + GetName() + "]Got pawn: " + GetPawn()->GetName());
	}
	else
	{
		GPRINT("[" + GetName() + "]Unpossessed pawn");
	}
}

void APlayerControllerBase::EnterLobby_Implementation()
{
	OnEnterLobby();
}

void APlayerControllerBase::OnRep_Setup()
{
	GPRINT("GOT SETUP STATE ON " + GetName());
	m_onSetupStateSet = true;
}
void APlayerControllerBase::m_OnMenuConfirm()
{
	OnMenuAction(EMenuActionBinding::Confirm);
}
void APlayerControllerBase::m_OnMenuBack()
{
	OnMenuAction(EMenuActionBinding::Back);
}
void APlayerControllerBase::m_OnMenuStart()
{
	OnMenuAction(EMenuActionBinding::Start);
}
void APlayerControllerBase::m_OnMenuUp()
{
	OnMenuAction(EMenuActionBinding::Up);
}
void APlayerControllerBase::m_OnMenuDown()
{
	OnMenuAction(EMenuActionBinding::Down);
}
void APlayerControllerBase::m_OnMenuLeft()
{
	OnMenuAction(EMenuActionBinding::Left);
}
void APlayerControllerBase::m_OnMenuRight()
{
	OnMenuAction(EMenuActionBinding::Right);
}
void APlayerControllerBase::m_OnMenuOpt1()
{
	OnMenuAction(EMenuActionBinding::Opt1);
}
void APlayerControllerBase::m_OnMenuOpt2()
{
	OnMenuAction(EMenuActionBinding::Opt2);
}
void APlayerControllerBase::m_OnMenuLeftPressed()
{
	m_repeatLeft = true;
	m_OnMenuLeft();
	OnMenuAction(EMenuActionBinding::Repeat_Left);
	OnMenuAction(EMenuActionBinding::Trigger_Left);
}
void APlayerControllerBase::m_OnMenuRightPressed()
{
	m_repeatRight = true;
	m_OnMenuRight();
	OnMenuAction(EMenuActionBinding::Repeat_Right);
	OnMenuAction(EMenuActionBinding::Trigger_Right);
}
void APlayerControllerBase::m_OnMenuLeftReleased()
{
	m_repeatLeft = false;
}
void APlayerControllerBase::m_OnMenuRightReleased()
{
	m_repeatRight = false;
}
void APlayerControllerBase::m_OnMenuShoulderLeft()
{
	OnMenuAction(EMenuActionBinding::Shoulder_Left);
}
void APlayerControllerBase::m_OnMenuShoulderRight()
{
	OnMenuAction(EMenuActionBinding::Shoulder_Right);
}
void APlayerControllerBase::m_OnMenuOptionsButton()
{
	OnMenuAction(EMenuActionBinding::Options);
}


void APlayerControllerBase::OnMenuAction(EMenuActionBinding action)
{
	m_menuItemStack.Remove(nullptr);
	for(int32 i = m_menuItemStack.Num(); i > 0;)
	{
		i--;
		m_menuItemStack[i]->NativeOnMenuAction(action);
		if(m_menuItemStack[i]->blockInput)
			break;
	}
}
