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

#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "UObject/SoftObjectPath.h"
#include "Animation/AnimCurveTypes.h"
#include "Engine/World.h"
#include "GameFramework/Actor.h"
#include "RootMotionRecorderComponent.generated.h"

DECLARE_LOG_CATEGORY_EXTERN(LogRootRecorder, Log, All);

UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class ROOTMOTIONRECORDER_API URootMotionRecorderComponent : public UActorComponent
{
	GENERATED_BODY()

public:	
	// Sets default values for this component's properties
	URootMotionRecorderComponent();

protected:
	// Called when the game starts
	virtual void BeginPlay() override;

public:
	/** 
	* Frame rate to bake the root motion down to after recording. 
	* The plugin records at the game's frame rate, then down/upsamples to this rate on export.
	* IMPORTANT: If uploading trajectories directly to Motorica, do not change this value from 30 FPS!!
	*/
	UPROPERTY(EditAnywhere, Category = "Root Motion Recorder")
	int32 ExportFrameRate = 30;

	/**
	* Sets every data point in the trajectory to the ground plane. If you have recorded on a slope, this will change the speed of your root motion.
	* (Currently, elevation is not supported in Motorica, so this is recommended for all exports.)
	*/
	UPROPERTY(EditAnywhere, Category = "Root Motion Recorder")
	bool bFlattenTrajectory = true;
	
	/** Directory to save recorded root motion data to. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Root Motion Recorder")
	FDirectoryPath OutputDirectory;

	UFUNCTION(BlueprintCallable, Category = "Root Motion Recorder")
	void StartRecording();

	UFUNCTION(BlueprintCallable, Category = "Root Motion Recorder")
	void EndRecording();

	/**
	* End recording and write the file using a caller-supplied stem so the consumer
	* can correlate output with an input. Resulting filename:
	*   <BaseName>_recorded_<timestamp>.json
	* Empty BaseName falls through to the default "RecordedPath_<timestamp>.json".
	*/
	UFUNCTION(BlueprintCallable, Category = "Root Motion Recorder")
	void EndRecordingWithName(const FString& BaseName);

	UFUNCTION(BlueprintPure, Category = "Root Motion Recorder")
	bool IsRecording() const { return bIsRecording; }
	
protected:
	/** DEBUG CONTROLS */
	UPROPERTY(EditAnywhere, Category = "Root Motion Recorder|Debug")
	bool bShouldDebugDraw = true;

	UPROPERTY(EditAnywhere, Category = "Root Motion Recorder|Debug", meta = (EditCondition = "bShouldDrawDebug"))
	bool bDebugDrawRotations = false;

	UPROPERTY(EditAnywhere, Category = "Root Motion Recorder|Debug", meta = (EditCondition = "bShouldDrawDebug"))
	FVector DebugDrawOffset = FVector(0.0f, 0.0f, -85.0f);

private:
	// JSON utils
	void SaveFloatArrayToJson(const TArray<float>& FloatArray, const FString& FilePath);

	bool bIsRecording = false;

	TArray<float> RecordedData;
	void RecordTick();
	void ProcessRecordedData();

	float RecordStartTime;
	float RecordCurrentTime;
	FFloatCurve TimeIndexCurve;
	FVectorCurve RecordedPositionCurve;
	TArray<FQuat> RecordedRotations;
	int32 SampleIndex = 0;

	FVector StartPosition;
	FVector LastDebugPosition;

	/** 
	* Build the basis change matrix:
	* Unreal axes to Motirica axes:
	* Motorica X = -Y, Motorica Y = Z, Motorica Z = X
	*/
	const FQuat BasisChangeQuat = FQuat(
		FMatrix(
			FVector(0, -1, 0),// X axis -> -Y
			FVector(0, 0, 1),// Y axis -> Z
			FVector(1, 0, 0),// Z axis -> X
			FVector::ZeroVector // origin
		)
	);
};
