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

#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "AnimQueue.h"
#include "MovementComponentState.h"
#include "ScriptedAnimation.h"
#include "Tangible.generated.h"


class UlxTangibleManager;


/**
 *	
 * UlxTangible
 *
 * The Tangible stores all the data we need for a tangible,
 * such as the animation queue and so forth.
 *
 * From time to time, a tangible can change its blueprint class.
 * To do that, we have to delete and recreate the actor.  This
 * is all set up so that it is possible to do that.
 *
 * The tangible has a place to store an Actor pointer.  This
 * actor pointer is allowed to be nullptr, especially in the
 * case that the blueprint hasn't been set yet.
 *
 * This also serves as a repository for blueprint functions
 * that operate on tangible actors.
 *
 */

UCLASS()
class INTEGRATION_API UlxTangible : public UObject
{
	GENERATED_BODY()

public:
	UlxTangible();

	// My Tangible Manager.
	UPROPERTY()
	TObjectPtr<UlxTangibleManager> Manager;

	// The tangible ID.
	UPROPERTY()
	uint64 TangibleId = 0;

	UPROPERTY()
	TWeakObjectPtr<AActor> CurrentActor;

	// The name of the current blueprint.
	UPROPERTY()
	FString ActorBlueprintName;

	// Every tangible can store a set of scripted animations. 
	UPROPERTY()
	UlxScriptedAnimations *ScriptedAnimations = nullptr;

	// Every tangible can store a fake movement component state.
	UPROPERTY()
	FlxMovementComponentState FakeMovementComponentState;

	// Animation tracker
	FlxAnimTracker AnimTracker;

	// Animation that is waiting to be finished.
	uint64 PendingAnimationHash = 0;

	// When do we timeout the pending animation.
	double PendingAnimationTimeout = 0.0;
	
	// Current Plane.
	FName Plane;

	// True if luprex thinks this object is Near the player.
	bool NearAccordingToLuprex = false;

	// True if unreal thinks this object is Near the player.
	bool NearAccordingToUnreal = false;

	// Delete the current actor.
	//
	void DeleteCurrentActor();
	
public:
	// Initialize a new tangible.
	//
	// This links the tangible to its TangibleManager and
	// sets the tangible's ID.
	//
	void Init(UlxTangibleManager *tm, int64 id);

	// Destroy a tangible.
	//
	// Delete the actor associated with the tangible, if any,
	// and clean up everything else.
	//
	void Destroy();

	// Get the actor associated with this tangible.
	//
	// Note that this may return nullptr: it is valid for a
	// tangible to have no actor associated with it.  This
	// is most commonly the case if the blueprint class of
	// the tangible is set to nullptr.
	//
	// Also bear in mind that if a tangible changes its blueprint
	// class, then the actor will have to be deleted and
	// recreated.  In that case, GetActor will return a different
	// pointer than before the blueprint change.
	// 
	AActor* GetActor() const { return CurrentActor.Get(); }

	// Get the location of the tangible.
	//
	// Note that if the actor is nullptr, the location is always
	// at the origin.
	//
	FVector GetLocation() const;

	// Update the animation queue from Luprex.
	//
	// This reads the animation queue, and copies it
	// into the animtracker.
	//
	void UpdateAnimationQueue(std::string_view aq);

	// Execute AnimStateChanged, if the 'changed' bit is set.
	//
	void MaybeExecuteAnimStateChanged();

	// Convert an actor to a tangible.
	//
	// The logging version of this function will generate a log
	// message if the actor is not a tangible, and it will then return
	// nullptr.  The quiet version will just return nullptr.
	//
	static UlxTangible *GetActorTangibleQuiet(AActor *actor);
	static UlxTangible *GetActorTangibleOrLog(AActor *actor);	

	// Automatically update the tangible's XYZ, Plane, Facing.
	//
	void AutoUpdatePosition();

private:

	// Set the actor's blueprint, and recreate the actor if necessary.
	//
	// If the blueprint is the empty string, deletes the actor.
	// If the blueprint is not the empty string, creates an actor of the
	// correct class.  This is a no-op if the actor already exists and
	// is already of the correct blueprint class.
	//
	void SetActorBlueprint(const FString &name);

public:
	UFUNCTION(BlueprintCallable, Meta = (DefaultToSelf = "target"), Category = "Luprex|Animation Queue")
		static void GetCurrentAnimation(AActor *target, FlxAnimationStep &step);

	UFUNCTION(BlueprintCallable, Meta = (DefaultToSelf = "Target"), Category = "Luprex|Animation Queue")
		static void FinishedAnimation(AActor *Target, const FlxAnimationStep &Step, bool AutoUpdate = true);

	UFUNCTION(BlueprintCallable, Meta = (DefaultToSelf = "Target", ExpandBoolAsExecs="ReturnValue"), Category = "Luprex|Animation Queue")
		static bool AnimationStepIsFinished(AActor *Target, const FlxAnimationStep &Step);

	UFUNCTION(BlueprintPure, Meta = (DefaultToSelf = "Target"), Category = "Luprex|Scripted Animations")
		static UlxScriptedAnimations *GetScriptedAnimations(AActor *Target);

	UFUNCTION(BlueprintCallable, Meta = (DefaultToSelf = "target"), Category = "Luprex|Tangible")
		static FString GetTangiblePlane(AActor* target);

	UFUNCTION(BlueprintCallable, Meta = (DefaultToSelf = "target"), Category = "Luprex|Tangible")
		static void SetTangiblePlane(AActor* target, const FString& plane);

	UFUNCTION(BlueprintPure, Meta = (DefaultToSelf = "target"), Category = "Luprex|Tangible")
		static bool IsCurrentPlayer(AActor *target);

	UFUNCTION(BlueprintPure, Meta = (DefaultToSelf = "target"), Category = "Luprex|Tangible")
		static bool IsLuprexTangible(AActor *target);
};


/**
 *
 * UlxTangibleComponent
 *
 * The TangibleComponent holds a pointer to the Tangible.
 * This is the only purpose it serves: to make it possible to
 * have the Actor point to its corresponding Tangible.
 *
 * The TangibleComponent is procedurally inserted into the Actor.
 * The TangibleComponent is not visible to blueprints.
 *
 */

UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class INTEGRATION_API UlxTangibleComponent : public UActorComponent
{
	GENERATED_BODY()

public:	
	UlxTangibleComponent() : Tangible(nullptr) {}
	
	// The actor that we're a part of.
	UPROPERTY()
	TWeakObjectPtr<UlxTangible> Tangible;
};
