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


#include "InventoryScreenWidget.h"

#include "Components/Button.h"
#include "Components/TextBlock.h"
#include "Components/WidgetSwitcher.h"
#include "CraftingWidget.h"
#include "FpsPlayer.h"
#include "GameFramework/PlayerController.h"
#include "InventoryComponent.h"
#include "InventoryDragDropOperation.h"
#include "InventoryWidget.h"
#include "StorageContainer.h"

void UInventoryScreenWidget::NativeConstruct()
{
	Super::NativeConstruct();

	if (InventoryTabButton)
	{
		InventoryTabButton->OnClicked.AddDynamic(this, &UInventoryScreenWidget::HandleInventoryTabClicked);
	}

	if (CraftingTabButton)
	{
		CraftingTabButton->OnClicked.AddDynamic(this, &UInventoryScreenWidget::HandleCraftingTabClicked);
	}

	if (StoreAllButton)
	{
		StoreAllButton->OnClicked.AddDynamic(this, &UInventoryScreenWidget::HandleStoreAllClicked);
	}

	if (TakeAllButton)
	{
		TakeAllButton->OnClicked.AddDynamic(this, &UInventoryScreenWidget::HandleTakeAllClicked);
	}

	// La grille de coffre n'affiche jamais de detail : c'est le meme
	// WBP_Inventory que le sac, elle en porte donc un exemplaire qu'on coupe une
	// fois pour toutes ici plutot que de compter sur une case a cocher.
	if (StorageGrid)
	{
		StorageGrid->SetItemDetailsEnabled(false);
	}

	// Le panneau de coffre ne se montre qu'a l'ouverture d'un coffre. On le
	// masque en C++ plutot que de dependre de ce qui a ete regle dans la
	// Designer : un panneau laisse Visible par erreur afficherait une grille
	// vide en permanence a cote du sac.
	HideStorage();

	ShowInventoryTab();
}

void UInventoryScreenWidget::BindToOwningPawn()
{
	if (InventoryPage)
	{
		InventoryPage->BindToOwningPawn();
	}

	if (CraftingPage)
	{
		CraftingPage->BindToOwningPawn();
	}
}

bool UInventoryScreenWidget::IsInventoryTabActive() const
{
	return TabSwitcher && InventoryPage && TabSwitcher->GetActiveWidget() == InventoryPage;
}

int32 UInventoryScreenWidget::GetHoveredSlotIndex() const
{
	if (!InventoryPage || !IsInventoryTabActive())
	{
		return INDEX_NONE;
	}

	return InventoryPage->GetHoveredSlotIndex();
}

bool UInventoryScreenWidget::GetHoveredSlot(UInventoryComponent*& OutInventory, int32& OutIndex) const
{
	// Le coffre en premier : c'est le panneau le plus a droite, donc celui que
	// le curseur survole quand les deux sont affiches.
	if (bStorageVisible && StorageGrid && StorageGrid->GetHoveredSlot(OutInventory, OutIndex))
	{
		return true;
	}

	if (InventoryPage && IsInventoryTabActive() && InventoryPage->GetHoveredSlot(OutInventory, OutIndex))
	{
		return true;
	}

	OutInventory = nullptr;
	OutIndex = INDEX_NONE;
	return false;
}

void UInventoryScreenWidget::NotifyExternalSlotHovered(UInventoryComponent* Inventory, int32 SlotIndex, bool bHovered)
{
	// Rien a faire sous l'onglet Fabrication : le panneau est parti avec la
	// page, et la barre rapide y est de toute facon masquee.
	if (InventoryPage && IsInventoryTabActive())
	{
		InventoryPage->NotifyExternalSlotHovered(Inventory, SlotIndex, bHovered);
	}
}

UInventoryComponent* UInventoryScreenWidget::GetPlayerInventory() const
{
	const APlayerController* OwningController = GetOwningPlayer();
	AFpsPlayer* PlayerPawn = OwningController ? Cast<AFpsPlayer>(OwningController->GetPawn()) : nullptr;
	return PlayerPawn ? PlayerPawn->GetInventoryComponent() : nullptr;
}

void UInventoryScreenWidget::ShowStorage(AStorageContainer* Container)
{
	UInventoryComponent* Contents = Container ? Container->GetStorage() : nullptr;
	if (!Contents)
	{
		UE_LOG(LogTemp, Warning, TEXT("UInventoryScreenWidget::ShowStorage : coffre nul ou sans inventaire."));
		return;
	}

	if (!StoragePanel || !StorageGrid)
	{
		UE_LOG(LogTemp, Warning, TEXT("UInventoryScreenWidget : 'StoragePanel' ou 'StorageGrid' manque dans WBP_InventoryScreen, le coffre ne s'affichera pas."));
		return;
	}

	BoundStorage = Contents;
	bStorageVisible = true;

	StorageGrid->SetColumnCount(Container->GetColumnCount());
	StorageGrid->BindToInventory(Contents);

	// Le clic droit envoie d'un cote a l'autre. Le pawn a pu changer depuis la
	// derniere ouverture (respawn), d'ou la relecture plutot qu'une valeur
	// gardee en cache.
	StorageGrid->SetQuickTransferTarget(GetPlayerInventory());
	if (InventoryPage)
	{
		InventoryPage->SetQuickTransferTarget(Contents);

		// Le detail disparait en mode coffre : l'ecran passe deja a deux grilles
		// cote a cote, une troisieme colonne le rendrait illisible.
		InventoryPage->SetItemDetailsEnabled(false);
	}

	if (StorageTitle)
	{
		StorageTitle->SetText(Container->GetDisplayName());
	}

	StoragePanel->SetVisibility(ESlateVisibility::Visible);

	// Un coffre ouvert sous l'onglet Fabrication n'aurait aucun sens : on force
	// l'onglet Inventaire, et ShowPage grise le bouton Fabrication tant que
	// bStorageVisible est vrai.
	ShowInventoryTab();
}

void UInventoryScreenWidget::HideStorage()
{
	BoundStorage.Reset();
	bStorageVisible = false;

	// On coupe les transferts rapides AVANT de debrancher : une case qui garde
	// un coffre en memoire y enverrait encore des objets au prochain clic droit,
	// panneau ferme.
	if (InventoryPage)
	{
		InventoryPage->SetQuickTransferTarget(nullptr);

		// Le sac retrouve son panneau. Repose a chaque fermeture, y compris a la
		// construction : c'est l'etat par defaut de l'ecran, il ne doit dependre
		// ni de la Designer ni de ce qu'a laisse la session precedente.
		InventoryPage->SetItemDetailsEnabled(true);
	}

	if (StorageGrid)
	{
		StorageGrid->SetQuickTransferTarget(nullptr);
		StorageGrid->BindToInventory(nullptr);
	}

	if (StoragePanel)
	{
		StoragePanel->SetVisibility(ESlateVisibility::Collapsed);
	}

	// Rend la main a l'onglet Fabrication, que ShowPage avait grise.
	if (CraftingTabButton)
	{
		CraftingTabButton->SetIsEnabled(IsInventoryTabActive());
	}
}

void UInventoryScreenWidget::HandleStoreAllClicked()
{
	UInventoryComponent* PlayerInventory = GetPlayerInventory();
	if (!PlayerInventory || !BoundStorage.IsValid())
	{
		return;
	}

	// A partir de la grille seulement : la barre rapide porte les outils qu'on
	// vient d'utiliser, et les ranger a chaque retour d'expedition serait une
	// punition deguisee en confort.
	PlayerInventory->TransferAllTo(BoundStorage.Get(), PlayerInventory->GetBackpackStartIndex());
}

void UInventoryScreenWidget::HandleTakeAllClicked()
{
	UInventoryComponent* PlayerInventory = GetPlayerInventory();
	if (!PlayerInventory || !BoundStorage.IsValid())
	{
		return;
	}

	// Depuis l'index 0 : un coffre n'a pas de barre rapide. Ce qui ne rentre
	// pas dans le sac reste dans le coffre.
	BoundStorage->TransferAllTo(PlayerInventory, 0);
}

void UInventoryScreenWidget::ShowInventoryTab()
{
	ShowPage(InventoryPage);
}

void UInventoryScreenWidget::ShowCraftingTab()
{
	ShowPage(CraftingPage);
}

void UInventoryScreenWidget::HandleInventoryTabClicked()
{
	ShowPage(InventoryPage);
}

void UInventoryScreenWidget::HandleCraftingTabClicked()
{
	ShowPage(CraftingPage);
}

void UInventoryScreenWidget::ShowPage(UWidget* Page)
{
	if (!TabSwitcher || !Page)
	{
		return;
	}

	TabSwitcher->SetActiveWidget(Page);

	// Le bouton de l'onglet courant est desactive : il sert d'indicateur de
	// position autant que de garde-fou contre un clic sans effet.
	const bool bInventoryActive = (Page == InventoryPage);

	if (InventoryTabButton)
	{
		InventoryTabButton->SetIsEnabled(!bInventoryActive);
	}

	if (CraftingTabButton)
	{
		// Grise aussi tant qu'un coffre est ouvert : ses ressources ne comptent
		// pas encore dans les recettes, et un clic dont le resultat serait
		// incomprehensible vaut mieux etre rendu impossible.
		CraftingTabButton->SetIsEnabled(bInventoryActive && !bStorageVisible);
	}

	OnTabChanged.Broadcast(bInventoryActive);
}

bool UInventoryScreenWidget::NativeOnDrop(const FGeometry& InGeometry, const FDragDropEvent& InDragDropEvent, UDragDropOperation* InOperation)
{
	Super::NativeOnDrop(InGeometry, InDragDropEvent, InOperation);

	// On n'arrive ici que si ni une case ni la page d'inventaire n'ont traite le
	// depot : les enfants sont consultes en premier et arretent la propagation.
	const UInventoryDragDropOperation* Payload = Cast<UInventoryDragDropOperation>(InOperation);
	if (!Payload || Payload->SourceSlotIndex == INDEX_NONE)
	{
		return true;
	}

	// Depuis l'inventaire SOURCE et non celui du joueur : sortir une pile du
	// coffre en la lachant dans le vide doit la retirer du coffre.
	if (const APlayerController* OwningController = GetOwningPlayer())
	{
		if (AFpsPlayer* PlayerPawn = Cast<AFpsPlayer>(OwningController->GetPawn()))
		{
			PlayerPawn->DropFromInventory(Payload->SourceInventory.Get(), Payload->SourceSlotIndex, Payload->Quantity);
		}
	}

	return true;
}
