//================================================================
// E3D library
//
// - Animation
//
// AUTHOR:  Gabor Nagy
// DATE:    2024-Aug-10 00:33:14
//
// Copyright (C) 1996-2026 by Gabor Nagy. All rights reserved.
//================================================================
#ifndef _E3D_Animation_h
#define _E3D_Animation_h


#include <Image/Time.h>

#ifndef _E3D_Spline_h
#include <E3D/Spline.h>
#endif

#if defined (__cplusplus) && (!(defined (__CPLUSLIBS)))
extern "C" {
#endif

// Animation types
//
typedef struct E3dAnimation_S        E3dAnimation;
typedef struct E3dAnimation_S*       E3dAnimationPtr;
EListA_t(E3dAnimationPtr);

typedef struct E3dAnimationClass_S   E3dAnimationClass;
typedef struct E3dAnimationClass_S*  E3dAnimationClassPtr;
EList_t(E3dAnimationClassPtr);

typedef struct
{
	ECoreObject*  Object;      // E.g. Node, E3dObject
	ECoreObject*  SubObject;   // E.g. NodeComponent, E3dObjectComponent
	EChangeFlags  ChangeFlags;
} E3dChangeListEntry;


typedef struct
{
	EListA(, E3dChangeListEntry, Entries);

	EListA_s(, EObjectAndComponents, ComponentsToUpdate);

	EChangeFlags  SceneChangedFlags;
} E3dChangeList;


typedef EChangeFlags  (*E3dAnimationSetFrameFunc)(E3dChangeList* PChangeList, E3dAnimation* PAnimation, EtTimeCallbackStruct* PTimeCBStruct);
typedef EBool         (*E3dAnimationIsActiveFunc)(E3dAnimation* PAnimation);
typedef EBool         (*E3dAnimationTargetSelectedFunc)(const E3dAnimation* PAnimation);



struct E3dAnimationClass_S
{
	EBaseClassCore(E3dAnimation);

	E3dAnimationSetFrameFunc        SetFrame;            // Called when the current frame changes
	E3dAnimationIsActiveFunc        IsActive;
	E3dAnimationTargetSelectedFunc  TargetSelected;
	unsigned int                    ChannelStructSize;
	EResourceList                   ChannelResources;    // Resource list for the Channels
};



#define E3dAnimationChannelCore\
	EObjectCore(EObject);\
	ECoreObject*      TargetObject;     /* Targetted "main" Object (e.g. a Model)              */\
	ECoreObject*      TargetSubObject;  /* Targetted SubObject (e.g., if a Model has an arbitrary Transform list, this will point to the specific Transform). If a Resource is targetted directly */\
                                      /* inside TargetObject, then this should point to TargetObject. This field must not be NULL, because it's needed by the EResource serializer. */\
\
	const EResource*  TargetMember;     /* Targetted quantity inside TargetSubObject.                        */\
	EIndex            TargetIndex;      /* Targetted element inside TargetMember if TargetMember is an array */\
	double            LastFrame;        /* The last frame when the target was updated                        */\
	EChangeFlags      ChangeFlags       /* What this channel changes                                         */

typedef struct
{
	E3dAnimationChannelCore;
} E3dAnimationChannel;


#define E3dAnimationCore\
	EObjectCore(E3dAnimation);\
\
	EListA(, E3dAnimationChannel*, Channels);\
\
/* Resources  */\
	uint8_t  Selection;\
	EBool    Active;\
\
/* Internals	*/\
	EBool    Removable,  /* Non-removable Animations are added/removed by plugins etc. and they are NOT SAVED to files.    */\
	         Absolute    /* If TRUE, animation can be evaluated at any point in time. Most physics simulations can not be, so they need "pre-roll" when rendering a sequence (e.g. run simulation from frame 0, but only render images in the given range.  */\



// Generic structure for Animation
//
struct E3dAnimation_S
{
	E3dAnimationCore;
};



// Key-frame animation
//

// Channel
//
typedef struct
{
	E3dAnimationChannelCore;

	E3dSpline*     Curve;
	EcRGBAp8Color  Color;  // Color used for drawing the FCurve
} E3dKeyFrameAnimationChannel;

// Animation
//
typedef struct
{
	E3dAnimationCore;
} E3dKeyFrameAnimation;


typedef struct
{
	ECoreObject*  Object;
	EListA(, E3dAnimation*, Animations);
} E3dAnimatedObject;



typedef struct
{
	EObjectCore(EObject);

	EBool  Active;
	EListA(, E3dAnimation*, Animations);
} E3dAnimStack, *E3dAnimStackPtr;
EListA_t(E3dAnimStackPtr);

typedef struct
{
	E3dObjectComponentCore;

// E3dShapeAnimObjectComponent part
//
	EList(, E3dCoord3, NeutralShapePoints);

// For serialization
//
	EListA(, E3dGeometry*, Geometries);
	double*  TimeStamps;
	EIndex   CurrentFrame;
} E3dShapeAnimObjectComponent;


//----------------------------------------
// Animation.c
//----------------------------------------
extern int  E3d_FCurveLengthSegmentsForEval;
extern int  E3d_FCurveLengthSegmentsForDraw;

EList_s(extern, E3dAnimationClassPtr, E3dAnimation_Classes);

extern E3dAnimationClass     E3dAnimation_class[];
extern EObjectClass          E3dAnimationChannel_class[];
extern EObjectClass          E3dAnimStack_class[];


extern E3dAnimation*         E3dAnimation_ByClass(E3dAnimation** PAnimations, const EIndex PCount,  E3dAnimationClass* PClass);
extern E3dAnimationChannel*  E3dAnimation_AddChannel(E3dAnimation* PAnimation);
extern int                   E3dAnimation_RemoveChannel(E3dAnimation* PAnimation, E3dAnimationChannel* PChannel);
extern void                  E3dAnimation_RemoveChannels(E3dAnimation* PAnimation);


extern E3dAnimationClass*  E3dKeyFrameAnimation_class;
extern EObjectClass        E3dKeyFrameAnimationChannel_class[];
extern E3dKeyFrameAnimationChannel*  E3dKeyframeAmination_AddChannel(E3dKeyFrameAnimation* PAnimation, const char* PName, const EcRGBAp8Color PColor, EObject* PTargetObject, EObject* PTargetSubObject, const EResource* PTargetMember, const EIndex PTargetIndex, const EIndex PNCurvePoints);

extern EIndex  E3dKeyFrameAnimation_AddKey(E3dKeyFrameAnimation* PAnimation, E3dKeyFrameAnimationChannel** PChannelRet,
 const char* PName, const EcRGBAp8Color PColor,
 const double PTime, const double PTimeEpsilon, const double PValue, const int PInterpolation, const E3dCoordinate PEaseInOut,
 EObject* PTargetObject, EObject* PTargetSubObject, const EResource* PTargetMember, const EIndex PTargetIndex,
 const EBool PUpdateSplines);


extern void                E3dAnimStack_AnimationAppend(E3dAnimStack* PStack, E3dAnimation* PAnimation);
extern E3dAnimation*       E3dAnimStack_AnimationAdd(E3dAnimStack* PStack, E3dAnimationClass* PClass);
extern EBool               E3dAnimStack_AnimationRemove(E3dAnimStack* PStack, E3dAnimation* PAnimation, const EBool PRemoveResidentAnim);


extern EIndex              E3dAnimationChannel_AddKeyFrame(E3dKeyFrameAnimationChannel* PAnimation,  const double PTime, const double PTimeEpsilon, const double PValue,  const int PInterpolation, const E3dCoordinate PEaseInOut, const EBool PTesselateCurves);
extern EBool               E3dAnimationChannel_TargetSelected(E3dAnimationChannel* PChannel);
extern EIndex              E3dAnimationChannel_SampleFrames(E3dAnimationChannel* PChannel,  const double PStartTime, const double PEndTime, const double PFramesPerSecond,  float** PValuesP, EIndex* PValues_CountAllocatedP);


extern E3dAnimationClass*   E3dAnimation_ClassRegister(const E3dAnimationClass* PDesc);
extern E3dAnimationClass*   E3dAnimation_ClassRegisterInactive(const char* PName);
extern E3dAnimationClass*   E3dAnimation_ClassByName(const char* PName);
extern void                 E3dAnimation_ClassDeactivate(E3dAnimationClass* PClass);
extern void                 E3dAnimation_ClassesFree(void);

extern void                 E3d_AddBuiltInAnimationClasses(void);

extern void                 E3dChangeList_Init(E3dChangeList* PChangeList);
extern void                 E3dChangeList_Clear(E3dChangeList* PChangeList, const EBool PFreeList);
extern E3dChangeListEntry*  E3dChangeList_EntryGetOrAdd(E3dChangeList* PChangeList, ECoreObject* PObject, ECoreObject* PSubObject);

extern EChangeFlags         E3dObjectsAndComponents_CallUpdate(EObjectAndComponentsListA* PList, const EBool PCallAllComponents);


#if defined (__cplusplus) && (!(defined (__CPLUSLIBS)))
}	// Close scope of 'extern "C"' declaration
#endif

#endif	// _E3D_Animation_h
