// Project Lab - NHTV Igad

#include "UnrealProject.h"
#include "LobbyMenu.h"
#include "LobbySlot.h"
#include "DefaultPlayer.h"
#include "PlayerStateBase.h"
#include "MenuController.h"
#include "DefaultGameInstance.h"
#include "MenuGameMode.h"
#include "GameStateBase.h"
#include "SubMenu.h"
#include "SessionManager.h"
#include "Engine.h"
#include "ScreenOverlay.h"
#include "CharacterSettings.h"

ULobbyMenu::ULobbyMenu(const FObjectInitializer& init)
	: Super(init)
{
}

void ULobbyMenu::NativeConstruct()
{
	m_teamContainer = Cast<USubMenu>(WidgetTree->FindWidget("Container"));
	Super::NativeConstruct();
}
void ULobbyMenu::NativeDestruct()
{
	Super::NativeDestruct();
}
void ULobbyMenu::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
	Super::NativeTick(MyGeometry, InDeltaTime);

	if(!m_myState)
	{
		// Try to aquire my local player state, this is in Tick because player states are not guaranteed during BeginPlay
		UWorld* const world = GetWorld();
		check(world);
		APlayerControllerBase* player = Cast<APlayerControllerBase>(world->GetGameInstance()->GetFirstLocalPlayerController());
		check(player);
		m_myState = Cast<APlayerStateBase>(player->PlayerState);
	}

	UWorld* world = GetWorld();
	check(world);
	AGameStateBase* gameState = Cast<AGameStateBase>(world->GetGameState());
	check(gameState);
	TArray<int32> teamSizes = gameState->GetTeamSizes();
	TArray<TArray<APlayerStateBase*>> playersPerTeam = gameState->GetPlayersByTeam();

	// Update team slots with their respective player arrays
	for (int32 i = 0; i < (int32)m_numTeams; i++)
	{
		if((i + 1) < playersPerTeam.Num())
		{
			m_lobbySlots[i]->UpdatePlayers(playersPerTeam[i+1]);
		}
		else
			m_lobbySlots[i]->UpdatePlayers(TArray<APlayerStateBase*>());

		if (i > teamSizes.Num())
		{
			m_lobbySlots[i]->SetState(false);
		}
		else
		{
			m_lobbySlots[i]->SetState(true);
		}
	}
}
void ULobbyMenu::Init()
{
	// Generate the selectable builds
	UWorld* world = GetWorld();
	check(world);
	UDefaultGameInstance* instance = Cast<UDefaultGameInstance>(world->GetGameInstance());
	check(instance);
	UCharacterSettings* settings = instance->GetCharacterSettings();
	check(settings);
	m_characters = settings->characterSaves;


	// Remove existing lobby slots
	for(ULobbySlot* s : m_lobbySlots)
	{
		s->RemoveFromParent();
	}
	m_lobbySlots.SetNum(0);

	if(!m_teamContainer)
	{
		GWERROR("Container not set, can't initialize lobby menu");
		return;
	}

	UPanelWidget* panel = Cast<UPanelWidget>(m_teamContainer->WidgetTree->RootWidget);
	if (!panel)
	{
		GWERROR(L"No UPanelWidget found");
		return;
	}

	// Create team slots in this menu for the amount of selected teams in the game setup screen
	AGameStateBase* gameState = Cast<AGameStateBase>(world->GetGameState());
	check(gameState);
	m_numTeams = gameState->GetMapTeamCount();
	for (uint32 i = 0; i < m_numTeams; i++)
	{
		ULobbySlot* slot = CreateWidget<ULobbySlot>(GetWorld(), lobbySlotClass);
		check(slot);

		panel->AddChild(slot);
		slot->SetTeamIndex(i+1);
		m_lobbySlots.Add(slot);
	}

	m_teamContainer->RescanItems();
}

void ULobbyMenu::ToggleReadyState()
{
	// Toggle my player state's ready state
	if(!m_myState)
	{
		UWorld* const world = GetWorld();
		check(world);
		APlayerControllerBase* player = Cast<APlayerControllerBase>(world->GetGameInstance()->GetFirstLocalPlayerController());
		check(player);
		m_myState = Cast<APlayerStateBase>(player->PlayerState);
	}

	if(m_myState)
		m_myState->SetReadyState(!m_myState->GetReadyState());
}
void ULobbyMenu::StartGame()
{
	UWorld* world = GetWorld();
	check(world);
	AGameStateBase* gameState = Cast<AGameStateBase>(world->GetGameState());
	check(gameState);
	TArray<APlayerStateBase*> players = gameState->GetPlayers<APlayerStateBase>();

	// Check if we're the host
	AMenuGameMode* gameMode = world->GetAuthGameMode<AMenuGameMode>();
	if(gameMode)
	{
		// Check if everyone's ready
		for (size_t i = 0; i < players.Num(); i++)
		{
			if (!players[i]->GetReadyState())
				return;
		}

		// Tell the game to start
		GPRINT("Starting game");
		if(!gameMode->StartGame())
		{
			GERROR("Failed to start game");
			TArray<FString> options;
			options.Add("OK");
			Cast<APlayerControllerBase>(GetOwningPlayer())->overlay->ShowMessageBox("Error", "Failed to start game", options);
		}
	}
}

void ULobbyMenu::GotoTeamSelector()
{
	if (m_teamContainer)
	{
		OpenSubMenu(m_teamContainer);
	}
}
void ULobbyMenu::BackToMenu()
{
	// Close active session
	UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(GetOwningPlayer()->GetGameInstance());
	inst->sessionManager->DestroySession();
	inst->sessionManager->CloseNetConnections();

	// Goto menu
	AMenuController* mc = Cast<AMenuController>(GetOwningPlayer());
	mc->SwitchToMenu();
}
bool ULobbyMenu::IsHost() const
{
	if (!GetOwningPlayer())
		return false;
	return GetOwningPlayer()->Role == ROLE_Authority;
}

void ULobbyMenu::OnLobbyEnter()
{
	// Register session callbacks
	UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(GetOwningPlayer()->GetGameInstance());
	Init();
}


TArray<FCharacterSave> ULobbyMenu::GetCharacterSaves()
{
	return m_characters;
}