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


#include "NpcPawn.h"

#include "StateTree.h"
#include "SUDSDialogue.h"
#include "Components/StateTreeComponent.h"
#include "Components/StatForgeComponent.h"
#include "Faye/Data/OrderSettingsDataAsset.h"
#include "Faye/Inventory/ItemDatabase.h"
#include "Faye/Data/Filters/ItemFilters.h"
#include "Faye/System/FayeDeveloperSettings.h"
#include "Faye/Structs/CommonStructs.h"

ANpcPawn::ANpcPawn()
{
	PrimaryActorTick.bCanEverTick = true;
	
	CapsuleComponent = CreateDefaultSubobject<UCapsuleComponent>("Capsule Component");
	RootComponent = CapsuleComponent;
	
	MovementComponent = CreateDefaultSubobject<UNpcPawnMovement>("Movement Component");
	MovementComponent->UpdatedComponent = CapsuleComponent;
	
	StateTreeComponent = CreateDefaultSubobject<UStateTreeComponent>("StateTreeComponent");
	StateTreeComponent->SetStartLogicAutomatically(false);
	
	StatForgeComponent = CreateDefaultSubobject<UStatForgeComponent>("Stat Forge Component");
	
	AutoPossessAI = EAutoPossessAI::PlacedInWorldOrSpawned;
}

void ANpcPawn::OnDialogueStarting_Implementation(USUDSDialogue* Dialogue, FName AtLabel)
{
	ISUDSParticipant::OnDialogueStarting_Implementation(Dialogue, AtLabel);
	
	if (Dialogue)
	{
		Dialogue->SetVariableText(FName("SpeakerName.NPCName"), GetNpcBaseData().NpcDisplayName);
	}
}

void ANpcPawn::GenerateBookFilter()
{
	const UFayeDeveloperSettings* DeveloperSettings = GetDefault<UFayeDeveloperSettings>();
	FBookFilterFlagsSet FlagSet = DeveloperSettings->BookFilterVariations[FMath::RandRange(0, DeveloperSettings->BookFilterVariations.Num() - 1)];
	int32 Flag = FlagSet.Flags;
	
	UItemDatabase* ItemDatabase = UItemDatabase::Get(this);
	UBookFilter* BookFilter = NewObject<UBookFilter>(this);
	TArray<UItem*> AllBooks = UItemDatabase::GetAllBooks(this);
	bool bFilterPasses = false;
	
	if (Flag & (1 << static_cast<uint8>(EBookFilterFlags::Author)))
	{
		const TArray<FText>& AuthorList = UItemDatabase::GetAuthorList(this);
		while (!bFilterPasses)
		{
			BookFilter->Author = AuthorList[FMath::RandRange(0, AuthorList.Num() - 1)];
			TArray<UItem*> TempBooks = AllBooks;
			UItemDatabase::FilterItemArray(BookFilter, TempBooks);
			if (!TempBooks.IsEmpty())
			{
				bFilterPasses = true;
				AllBooks = TempBooks;
			}
		}
		BookFilter->Author = FText();
	}
	
	if (Flag & (1 << static_cast<uint8>(EBookFilterFlags::PublicationYear)))
	{
		TMap<int32, FObjectContainer>  BookByDecadesMap;
		for (auto Book : AllBooks)
		{
			if (UBookItem* BookItem = Cast<UBookItem>(Book))
			{
				int32 Decade = FMath::Floor((float)BookItem->PublicationDate / 10.f);
				if (auto FoundDecade = BookByDecadesMap.Find(Decade))
				{
					FoundDecade->Objects.Add(BookItem);
				}
				else
				{
					FObjectContainer Container;
					Container.Objects.Add(BookItem);
					BookByDecadesMap.Add(Decade, Container);
				}
			}
		}
		
		TArray<int32> Keys;
		BookByDecadesMap.GetKeys(Keys);
		bFilterPasses = false;

		for (int32 i = 0; i < 10 && !bFilterPasses; i++)
		{
			int32 RandIndex = FMath::RandRange(0, Keys.Num() - 1);
			BookFilter->PublicationDateStart = Keys[RandIndex] * 10;
			BookFilter->PublicationDateEnd = (Keys[RandIndex] * 10) + 9;
			
			TArray<UItem*> TempBooks = AllBooks;
			UItemDatabase::FilterItemArray(BookFilter, TempBooks);
			
			if (!TempBooks.IsEmpty())
			{
				bFilterPasses = true;
				AllBooks = TempBooks;
			}	
		}
		BookFilter->PublicationDateStart = INDEX_NONE;
		BookFilter->PublicationDateEnd = INDEX_NONE;
	}
	
	if (Flag & (1 << static_cast<int32>(EBookFilterFlags::PageNumber)))
	{
		bFilterPasses = false;
		TArray<EPageNumberCategorisation> Keys;
		DeveloperSettings->BookLengthLimits.GetKeys(Keys);
		
		for (int32 i = 0; i < 10 && !bFilterPasses; i++)
		{
			int32 RandIndex = FMath::RandRange(0, Keys.Num() - 1);
			BookFilter->PageNumberStart = 0;
			BookFilter->PageNumberEnd = DeveloperSettings->BookLengthLimits[Keys[RandIndex]];
			
			TArray<UItem*> TempBooks = AllBooks;
			UItemDatabase::FilterItemArray(BookFilter, TempBooks);
			
			if (!TempBooks.IsEmpty())
			{
				bFilterPasses = true;
				AllBooks = TempBooks;
			}
		}
		BookFilter->PageNumberStart = INDEX_NONE;
		BookFilter->PageNumberEnd = INDEX_NONE;
	}
	
	if (Flag & (1 << static_cast<uint8>(EBookFilterFlags::BookCategory)))
	{
		bFilterPasses = false;
		for (int32 i = 0; i < 10 && !bFilterPasses; i++)
		{
			EBookCategory BookCategory = static_cast<EBookCategory>(FMath::RandRange(1, static_cast<int32>(EBookCategory::ChildrenAndYoungAdult)));
			BookFilter->BookCategory = BookCategory;
			
			TArray<UItem*> TempBooks = AllBooks;
			UItemDatabase::FilterItemArray(BookFilter, TempBooks);
			
			if (!TempBooks.IsEmpty())
			{
				bFilterPasses = true;
				AllBooks = TempBooks;
			}
		}
		BookFilter->BookCategory = EBookCategory::None;
	}
	
	if (Flag & (1 << static_cast<uint8>(EBookFilterFlags::BookSubcategory)))
	{
		bFilterPasses = false;
		for (int32 i = 0; i < 10 && !bFilterPasses; i++)
		{
			EBookSubCategory BookSubCategory = static_cast<EBookSubCategory>(FMath::RandRange(1, static_cast<int32>(EBookSubCategory::YoungAdult)));
			BookFilter->BookSubCategory = BookSubCategory;
			
			TArray<UItem*> TempBooks = AllBooks;
			UItemDatabase::FilterItemArray(BookFilter, TempBooks);
			
			if (!TempBooks.IsEmpty())
			{
				bFilterPasses = true;
				AllBooks = TempBooks;
			}
		}
		BookFilter->BookSubCategory = EBookSubCategory::None;
	}
	
	int32 Num = AllBooks.Num();
	UE_LOG(LogTemp, Warning, TEXT("Found books with filter: %d, bitmask: %d"), Num, Flag);
	
	ChosenBookFilter = BookFilter;
}

void ANpcPawn::GenerateCoffeeFilter()
{
	const UFayeDeveloperSettings* DeveloperSettings = GetDefault<UFayeDeveloperSettings>();
	FCoffeeFilterFlagsSet FlagSet;
	
	if (CustomOrderSettings)
	{
		FlagSet = CustomOrderSettings->FilterSettings[FMath::RandRange(0,  CustomOrderSettings->FilterSettings.Num() - 1)];
	}
	else
	{
		FlagSet = DeveloperSettings->GetOrderSettingDataAsset()->FilterSettings[FMath::RandRange(0,  DeveloperSettings->GetOrderSettingDataAsset()->FilterSettings.Num() - 1)];
	}
	
	int32 Flag = FlagSet.Flags;
	
	UCoffeeFilter* CoffeeFilter = NewObject<UCoffeeFilter>(this);
	
	if (Flag & (1 << static_cast<uint8>(ECoffeeFilterFlags::SizeAndType)))
	{
		CoffeeFilter->CoffeeSize = static_cast<ECoffeeSize>(FMath::RandRange(static_cast<int32>(ECoffeeSize::Small), static_cast<int32>(ECoffeeSize::Large)));
		CoffeeFilter->CoffeeType = static_cast<ECoffeeType>(FMath::RandRange(static_cast<int32>(ECoffeeType::Water), static_cast<int32>(ECoffeeType::Max) - 1));
	}
	
	if (Flag & (1 << static_cast<uint8>(ECoffeeFilterFlags::Iced)))
	{
		CoffeeFilter->bIced = false; //FMath::RandBool();
	}
	
	if (Flag & (1 << static_cast<uint8>(ECoffeeFilterFlags::CoffeeAddition)))
	{
		CoffeeFilter->CoffeeAddition = ECoffeeAddition::None; //static_cast<ECoffeeAddition>(FMath::RandRange(static_cast<int32>(ECoffeeAddition::Muffin), static_cast<int32>(ECoffeeAddition::Max) - 1));
	}
	
	if (Flag & (1 << static_cast<uint8>(ECoffeeFilterFlags::WhippedCream)))
	{
		CoffeeFilter->bWhippedCream = false; //FMath::RandBool();
	}
	
	ChosenCoffeeFilter = CoffeeFilter;
}

void ANpcPawn::BeginPlay()
{
	Super::BeginPlay();
	
	if (!StateTree.IsNull())
	{
		StateTree.LoadAsync(FLoadSoftObjectPathAsyncDelegate::CreateWeakLambda(this, [this](const FSoftObjectPath& Path, UObject* Object)
		{
			if (!StateTreeComponent) return;
			
			if (UStateTree* LoadedStateTree = Cast<UStateTree>(Object))
			{
				StateTreeComponent->SetStateTree(LoadedStateTree);
				StateTreeComponent->StartLogic();
			}
		}));
	}
}

void ANpcPawn::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
}

UPawnMovementComponent* ANpcPawn::GetMovementComponent() const
{
	return MovementComponent;
}

