// Project Lab - NHTV Igad

#include "UnrealProject.h"
#include "SubMenu.h"
#include "PlayerControllerBase.h"
#include "MenuButton.h"
#include "DefaultGameInstance.h"
#include "MenuScreenBase.h"
#include "EffectFunctionLibrary.h"
#include "ScreenOverlay.h"

USoundBase* confirmAudioClass;
USoundBase* backAudioClass;
USoundBase* selectionChangedAudioClass;
USoundBase* clickAudioClass;

USubMenu::USubMenu(const FObjectInitializer& init)
{
	selectionChangedAudioClass = ConstructorHelpers::FObjectFinder<USoundBase>(TEXT("/Game/Assets/GUI/Components/InterfaceSounds/Select2")).Object;
	backAudioClass = ConstructorHelpers::FObjectFinder<USoundBase>(TEXT("/Game/Assets/GUI/Components/InterfaceSounds/Select0")).Object;
	confirmAudioClass = ConstructorHelpers::FObjectFinder<USoundBase>(TEXT("/Game/Assets/GUI/Components/InterfaceSounds/Select1")).Object;
	clickAudioClass = ConstructorHelpers::FObjectFinder<USoundBase>(TEXT("/Game/Assets/Sound/T_SkillDrop")).Object;
}

void USubMenu::NativeConstruct()
{
	m_focus = false;
	m_layoutDirection = 0;
	container = Cast<UPanelWidget>(WidgetTree->FindWidget("SubMenuContainer"));
	if(!container)
	{
		container = Cast<UPanelWidget>(GetRootWidget());
		if(!container)
		{
			GERROR("No \"Container\" found in widget " + GetName() + ", root widget was not a PanelWidget");
		}
	}
	Super::NativeConstruct();

	m_selectedItem = 0;
	RescanItems();
}
void USubMenu::NativeDestruct()
{
	Super::NativeDestruct();
}

void USubMenu::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
	Super::NativeTick(MyGeometry, InDeltaTime);
	if(HasFocus())
	{
		for(UMenuItemBase* item : m_items)
		{
			item->FocusTick(InDeltaTime);
		}
	}
}

void USubMenu::m_ScanContainer(UWidget* widget, int32 depth /*= 0*/)
{
	// Loop through panel widgets
	UPanelWidget* panel = Cast<UPanelWidget>(widget);
	if(panel)
	{
		for(int32 i = 0; i < panel->GetChildrenCount(); i++)
		{
			m_ScanContainer(panel->GetChildAt(i), depth+1);
		}
		return;
	}

	// Process Menu items
	UMenuItemBase* item = Cast<UMenuItemBase>(widget);
	if(item)
	{
		bool hidden = item->GetVisibility() == ESlateVisibility::Hidden || item->GetVisibility() == ESlateVisibility::Collapsed;
		if(!hidden)
		{
			m_items.Add(item);

			// Assign item to a submenu
			item->m_subMenu = this;
		}
		return;
	}

	UPanelUserWidget* puw = Cast<UPanelUserWidget>(widget);
	if(puw)
	{
		m_ScanContainer(puw->GetRootWidget());
		return;
	}
}

void USubMenu::RescanItems()
{
	m_items.SetNum(0);
	if(container)
	{
		if(Cast<UHorizontalBox>(container))
		{
			m_layoutDirection = 1;
		}
		else
		{
			m_layoutDirection = 0;
		}

		// Scan for buttons
		m_ScanContainer(container);

		int32 childCount = m_items.Num();

		// Clamp selection
		m_selectedItem = FMath::Clamp(m_selectedItem, 0, childCount - 1);

		// Set active selection
		for(int32 i = 0; i < childCount; i++)
		{
			UMenuItemBase* item = m_items[i];
			item->index = i;

			if(i == m_selectedItem)
			{
				item->NativeOnSelectionChanged(true, true);
			}
			else
			{
				item->NativeOnSelectionChanged(false, true);
			}
		}

		// Reselect single items
		if(childCount == 1)
		{
			m_items[0]->NativeOnSelectionChanged(false, true);
			m_items[0]->NativeOnSelectionChanged(true, true);
		}

		if(childCount > 0)
		{
			// Selection event
			onItemSelected.Broadcast(m_items[m_selectedItem]);
		}
	}
	else
	{
		GWWARNING(L"SubMenu Container not set");
		return;
	}
}

void USubMenu::CloseSubMenu()
{
	if(m_screen)
		m_screen->CloseSubMenu(this);
}

void USubMenu::OnEnter(class UMenuScreenBase* screen)
{
	check(screen);
	m_screen = screen;
	SetButtonHints();
	onOpened.Broadcast();
	m_SetFocus(true);
}
void USubMenu::OnLeave()
{
	onClosed.Broadcast();
	m_screen = nullptr;
	m_SetFocus(false);
}

bool USubMenu::HasFocus() const
{
	return m_focus;
}

void USubMenu::OnSuspend(USubMenu* newSubMenu, bool loseFocus)
{
	if(loseFocus)
		m_SetFocus(false);
	onSuspend.Broadcast();
}
void USubMenu::OnRestore(USubMenu* removedMenu)
{
	SetButtonHints();
	m_SetFocus(true);
	onRestore.Broadcast();
}

class UButtonHintBar* USubMenu::GetButtonHintBar() const
{
	return GetScreen()->GetButtonHintBar();
}

class UCharacterSettings* USubMenu::GetCharacterSettings() const
{
	return GetGameInstance()->GetCharacterSettings();
}
class UPrefs* USubMenu::GetPrefs() const
{
	return GetGameInstance()->GetPrefs();
}
class UDefaultGameInstance* USubMenu::GetGameInstance() const
{
	return Cast<UDefaultGameInstance>(GetWorld()->GetGameInstance());
}
void USubMenu::NativeOnMenuAction(EMenuActionBinding binding)
{
	if(m_items.Num() > 0)
		m_items[m_selectedItem]->NativeOnMenuAction(binding);

	switch(binding)
	{
	case EMenuActionBinding::Down:
		if(m_items.Num() > 0 && m_layoutDirection == 0)
			SelectNewItemByIndex(FMath::Clamp(m_selectedItem + 1, 0, GetNumItems()-1), true);
		break;
	case EMenuActionBinding::Up:
		if(m_items.Num() > 0  && m_layoutDirection == 0)
			SelectNewItemByIndex(FMath::Clamp(m_selectedItem - 1, 0, GetNumItems()-1), true);
		break;
	case EMenuActionBinding::Left:
		if(m_items.Num() > 0 && m_layoutDirection == 1)
			SelectNewItemByIndex(FMath::Clamp(m_selectedItem - 1, 0, GetNumItems() - 1), true);
		break;
	case EMenuActionBinding::Right:
		if(m_items.Num() > 0 && m_layoutDirection == 1)
			SelectNewItemByIndex(FMath::Clamp(m_selectedItem + 1, 0, GetNumItems() - 1), true);
		break;
	case EMenuActionBinding::Confirm:
		break;
	case EMenuActionBinding::Back:
		onBack.Broadcast();
		UEffectFunctionLibrary::PlaySoundEffect2D(GetWorld(), backAudioClass);
		break;
	}

	Super::NativeOnMenuAction(binding);
}

void USubMenu::NativeOnSelectionConfirmed(UMenuItemBase* item)
{
	UEffectFunctionLibrary::PlaySoundEffect2D(GetWorld(), clickAudioClass);
	onItemSelectionConfirmed.Broadcast(m_items[m_selectedItem]);
}

UMenuItemBase* USubMenu::GetItem(int32 index) const
{
	if(index >= m_items.Num() || index < 0)
	{
		GWWARNING(L"Item index out of range");
		return nullptr;
	}
	return m_items[index];
}
int32 USubMenu::GetNumItems() const
{
	return m_items.Num();
}
int32 USubMenu::GetSelectedItem() const
{
	return m_selectedItem;
}
void USubMenu::SelectNewItemByIndex(int32 idx)
{
	SelectNewItemByIndex(idx, false);
}
void USubMenu::SelectNewItemByIndex(int32 idx, bool controller)
{
	if(idx >= m_items.Num() || idx < 0)
	{
		GWWARNING(L"Item index out of range");
		return;
	}
	if(m_selectedItem != idx)
	{
		check(m_selectedItem < m_items.Num());
		m_items[m_selectedItem]->NativeOnSelectionChanged(false, controller);
		m_items[idx]->NativeOnSelectionChanged(true, controller);
		m_selectedItem = idx;
		onItemSelected.Broadcast(m_items[m_selectedItem]);
		SetButtonHints();
		UEffectFunctionLibrary::PlaySoundEffect2D(GetWorld(), selectionChangedAudioClass);
	}
}
void USubMenu::SelectNewItem(UMenuItemBase* item)
{
	int32 idx = 0;
	if (m_items.Find(item, idx))
	{
		SelectNewItemByIndex(idx);
	}
}
int32 USubMenu::GetItemIndex(UMenuItemBase* item)
{
	int32 idx = 0;
	if (m_items.Find(item, idx))
		return idx;
	return -1;
}
UMenuScreenBase* USubMenu::GetScreen() const
{
	return m_screen;
}

void USubMenu::m_SetFocus(bool focus)
{
	m_focus = focus;

	// Get Selected button
	UMenuItemBase* item = GetItem(GetSelectedItem());
	if (item)
	{
		item->NativeOnSelectionChanged(true, false);
	}
}
