// Project Sword & Gun Copyright © 2021 Kevin Poretti


#include "Components/SNGState.h"

USNGState::USNGState()
{
	Name = NAME_None;
	TimeInState = 0.0f;
	bCanTransitionToItself = false;
	DefaultSubStateIndex = 0;
}

void USNGState::RegisterTransition(FName DestState, FName TransitionRuleFunctionName)
{
	FStateTransitionRule StateTransitionRule;
	StateTransitionRule.BindUFunction(this, TransitionRuleFunctionName);
	
	if(!Transitions.Contains(DestState))
	{
		Transitions.Add(DestState, StateTransitionRule);
	}
	else
	{
		UE_LOG(LogTemp, Warning, TEXT("USNGState :: Transition dictionary already contains transition rule for destination state %s"), *DestState.ToString());
	}
}

void USNGState::RegisterEventHandler(FName DestState, ESNGEventType EventType, FName EventHandlerFunctionName)
{
	FEventHandler EventHandler;
	EventHandler.DestinationState = DestState;
	EventHandler.HandlerRule.BindUFunction(this, EventHandlerFunctionName);

	if(Handlers.Contains(EventType))
	{
		Handlers[EventType].Add(EventHandler);
	}
	else
	{
		Handlers.Add(EventType, TArray<FEventHandler>());
		Handlers[EventType].Add(EventHandler);
	}
}

FName USNGState::GetNextStateFromEvent(FSNGEvent& Event)
{
	if(Handlers.Contains(Event.EventType))
	{
		for (auto EventHandler : Handlers[Event.EventType])
		{
			if(EventHandler.HandlerRule.Execute(Event))
			{
				return EventHandler.DestinationState;
			}
		}
	}

	if(Parent)
	{
		return Parent->GetNextStateFromEvent(Event);
	}
	
	return NAME_None;
}

FName USNGState::GetNextState()
{
	// NOTE(kevin): Does this actually have to be a map?
	for(const TPair<FName, FStateTransitionRule>& pair : Transitions)
	{
		// FStateTransitionRule is a delegate that returns true if the transition rules are met
		// and false otherwise
		if(pair.Value.Execute())
		{
			return pair.Key;
		}
	}

	if(Parent)
	{
		return Parent->GetNextState();
	}
	
	return NAME_None;
}

AActor* USNGState::GetOwner()
{
	return Owner;
}

void USNGState::SetOwner(AActor* StateOwner)
{
	Owner = StateOwner;
}

void USNGState::CreateState()
{
	OnCreateState();
}

USNGState* USNGState::EnterState()
{
	OnEnterState();
	TimeInState = 0.0f;

	if(SubStates.Num() > 0 &&
		(DefaultSubStateIndex >= 0 && DefaultSubStateIndex < SubStates.Num()))
	{
		return SubStates[DefaultSubStateIndex]->EnterState();	
	}
	return this;
}

void USNGState::ExitState()
{
	OnExitState();

	// to do this correctly i think we need to find the shared parent of the current state and the next state
	// we only want to exit states until that point
	if(Parent)
	{
		Parent->ExitState();
	}
}

void USNGState::UpdateState(float DeltaTime)
{
	OnUpdateState(DeltaTime);
	TimeInState += DeltaTime;

	if(Parent)
	{
		Parent->UpdateState(DeltaTime);
	}
}

FName USNGState::GetName()
{
	return Name;
}

float USNGState::GetTimeInState()
{
	return TimeInState;
}

bool USNGState::CanTransitionToItself()
{
	return bCanTransitionToItself;
}

TArray<TSubclassOf<USNGState>> USNGState::GetSubStateClasses()
{
	return SubStateClasses;
}

void USNGState::AddSubState(USNGState* SubState)
{
	SubState->Parent = this;
	SubStates.Add(SubState);
}
