//================================================================
// E3D library
//
// - Nodes
//
// AUTHOR:  Gabor Nagy
// DATE:    2026-Aug-05 03:36:43
//
// Copyright (C) 1996-2026 by Gabor Nagy. All rights reserved.
//================================================================
#ifndef _E3D_Node_h
#define _E3D_Node_h


#ifndef _E3D_Types_h
#include <E3D/Types.h>
#endif

#ifndef _E3D_Animation_h
#include <E3D/Animation.h>
#endif

#ifndef _E3D_Geometry_h
#include <E3D/Geometry.h>
#endif


//========================================================================
// Collect a non-repeating list of branches that have affected Nodes
//========================================================================
#define E3dM_NodeAddToBranchRoots(pNode, pBranchRoots, pBranchRoots_Count) do\
{\
	E3dNode*  lNode = (E3dNode*)(pNode);\
\
/* Add pNode only if none of its ancestors are in the list yet	*/\
\
	EIndex  lI = 0;for(;lI < pBranchRoots_Count;lI++)\
	{\
		if(E3dNode_IsDescendant(lNode, pBranchRoots[lI])) break;\
	}\
	if(lI == pBranchRoots_Count) ELst_PointerAddChk(&pBranchRoots, &pBranchRoots_Count, lNode);\
} while(0)


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


//--------------------------------
// Node types
//--------------------------------
enum
{
	E3dMDL_TEXTURE_PROJECTOR = 1  // This is not an actual Node type, just for E3dMesh_Draw
};



#define E3dMDL_BEFORE    0x01		// For pre-calculated matrix PUSHs and POPs
#define E3dMDL_AFTER     0x02

// Node selection types
//
#define	E3dNodeSelNONE         0x0000
#define	E3dNodeSelOBJECT       0x0001  // One or more of the Geometries of the Node is selected or a sub-geometry (e.g. a PoyGroup) is selected in those Geometries
#define	E3dNodeSelNODE         0x0002  // Node is selected independently of it's relatives
#define	E3dNodeSelBRANCH       0x0004  // This Node is in a selected branch
#define	E3dNodeSelBRANCH_ROOT  0x0008  // This Node is in a selected branch and this is the root of it
#define	E3dNodeSelANY          0x000F  // Any selection type (mask for E3dScene_GetSelectedNodes() etc.)



#ifndef _E3dNode
#define _E3dNode
typedef struct E3dNode_S   E3dNode;     // Forward-define E3dNode
typedef struct E3dNode_S*  E3dNodePtr;
#endif

// Node array
//
EList_t(E3dNodePtr);
EListA_t(E3dNodePtr);




typedef struct E3dNodeClass_S  E3dNodeClass;


typedef void          (*E3dNodeProc)(E3dNode*);
typedef void          (*E3dNodeGUIProc)(E3dNode*, void*, void*, int);
typedef unsigned int  (*E3dNodeUpdateProc)(E3dNode*, unsigned int);

struct E3dNodeClass_S
{
	EBaseClassCore(E3dNode);

	E3dNodeProc          Edit,
	                     Remove;    // This gets called when the Node is removed from the Scene
	E3dNodeUpdateProc    Update;
	E3dNodeGUIProc       GUI;

	E3dSchematicsIcons*  SchematicsIcons;
};


typedef struct  E3dNodeComponent_S        E3dNodeComponent;
typedef struct  E3dNodeComponentClass_S   E3dNodeComponentClass;
typedef struct  E3dNodeComponentClass_S*  E3dNodeComponentClassPtr;
EList_t(E3dNodeComponentClassPtr);

typedef int             (*E3dNodeComponentFunc)(E3dNode* PNode, E3dNodeComponent* PInfo);
typedef int             (*E3dNodeComponentCopyFunc)(const E3dNode* PSNode, const E3dNodeComponent* PSInfo,  E3dNode* PDNode, E3dNodeComponent* PDInfo);
typedef void            (*E3dNodeComponentGUIFunc)(E3dNode* PNode, E3dNodeComponent* PComponent, XeInfoPanel* PPanel, void* PDrawer, const int PAction);
typedef EChangeFlags    (*E3dNodeComponentUpdateFunc)(E3dNode* PChangedBranchRootNode, E3dNode* PNode, E3dNodeComponent* PInfo, const EChangeFlags PChangeFlags, const EBool PCheckOnly);
typedef E3dHWDrawFlags  (*E3dNodeComponentGLDrawFunc)(void* P3DWin,  const E3dDrawContext* PDrawContext,  E3dNode* PNode, E3dNodeComponent* PInfo);


struct E3dNodeComponentClass_S
{
	ECoreClassCore(E3dNodeComponent);

	E3dNodeComponentFunc        Init;
	E3dNodeComponentCopyFunc    Copy;
	E3dNodeComponentFunc        Free;

	E3dNodeComponentFunc        Edit,
	                            Remove;          // This is called when the Node is removed from the Scene
	E3dNodeComponentUpdateFunc  Update;
	E3dNodeComponentGUIFunc     GUI;
	uint16_t                    InternalFlags;   // Or-ed flags of EComponentCOPY, EComponentSAVE, EComponentREMOVABLE etc. E.g. some components are generated internally and are not supposed to be saved to files etc.
	                                             // This may be overwritten per instance. This field is used for creating the GUI etc. when there's no instance available.
	                                             // If the EComponentSET is 0, it means this field was not initialized when registering the class, so it will be defaulted by ..ClassRegister()
	E3dNodeComponentGLDrawFunc  GLDraw;          // Custom drawing proc for Node (e.g. continous LOD terrain)
	uint16_t                    GLDrawFlags;     // E3dDrawLAST etc. (e.g. GLDraw() must be done after everything else had been drawn (e.g. requiers no Z-test overdraw, transparency etc.). If PDisplayFlags has E3dDF_PICK set, it's called for picking (no need to set colors / shaders etc.)

	E3dNodeComponentGLDrawFunc  GLDrawControls;  // Custom drawing proc for Node Component's controls (3D GUI) (e.g. IK joint). Unlike the GLDraw method, this is called, even if E3dDF_CONTROLS is TRUE in DisplayFlags. This separation allows for distinguishing between things that must be drawn, even in ray-tracing mode, vs. GL-specific ObjectComponent visualizations.
	uint16_t                    GLDrawControlsFlags;
	EpImage*                    Icon;

	E3dSchematicsIcons*         SchematicsIcons;  // If not NULL, this overrides the icons in the baseclass
};


#define E3dNodeComponentCore\
	EObjectCore(E3dNodeComponent);\
/* Or-ed flags of EComponentCOPY, EComponentSAVE, EComponentREMOVABLE etc. E.g. some components are generated internally and are not supposed to be saved to files etc.  */\
/* These flags are set by code, in the class' constructor, etc. and are not saved into files.  */\
	uint16_t                 InternalFlags;\
/* Or-ed flags of EComponentANIMATABLE, EComponentRESPOND_TO_INPUT_CHANGE. These flags may be user-settable (depending on the class) and are saved to files, etc. */\
	uint16_t                 Flags;\
	EIndex  AttributeChangeCount, InputChangeCount

struct E3dNodeComponent_S
{
	E3dNodeComponentCore;
};


// Nodes in hierarchies are reference-counted / tracked downward, via the Child and NextSibling links.
// Just like root Nodes are reference-counted / tracked from the Scene's RootNodes array.
// The rest of the links anre not reference-counted, as they are only used for faster traversal and they are updated by E3dNodeHrc_UpdateConnections()
//
#define E3dNodeCore\
	E3dBaseObjectCore(E3dNode);\
\
	char*               FileName;\
\
	EList(, E3dNodeComponent*, Components);             /* Runtime-typed object-components        */\
\
/* Hierarchy links                        */\
	E3dNode*            Parent;\
	E3dNode*            PrevSibling;\
	E3dNode*            NextSibling;                    /* Reference-counted / tracked            */\
	E3dNode*            Child;                          /* Reference-counted / tracked            */\
	E3dNode*            Prev;                           /* Links for hierarchy traversal (depth-first) */\
	E3dNode*            Next;\
	char                PopN, PushN;                    /* For pre-computed Matrix/pos PUSHs and POPs  */\
\
	E3dMatrix           LocalToWorldMatrix;             /* Local->World Matrix                    */\
	E3dCoordinate       LocalToWorldMatrixDeterminant;\
	E3dMatrix           LocalToWorldUnitVectorMatrix;   /* Local->World Matrix for transforming unit vectors, like normals, tangents, etc. (the transpose-adjoint of LocalToWorldMatrix)  */\
	ETime               Matrix_TimeStamp;               /* The last time the Matrix changed       */\
\
	EBool               NeedsRenormalizing;             /* Scaling is not 1.0 (+- SCALE_EPSILON)	*/\
\
	E3dBBox             BBox;                           /* Bounding box    */\
\
	int                 Selection;\
	EBool               Visible, Highlighted;\
	EBool               BranchOutputsEnabled;\
	EIndex              GlobalIndex;                    /* For picking     */\
	uint32_t            SubCameras;                     /* Specifies which sub-cameras this Node is visible to (E3dCameraLEFT etc.)*/\
	EChangeFlags        ChangedFlags


//----------------------------------------
// Node: the base of Model etc.
//----------------------------------------
struct E3dNode_S  // For forward declaration
{
	E3dNodeCore;
};


typedef struct
{
	E3dNode*  Node;
	E3dNode*  Parent;
	EIndex    Index;
} E3dBranch;

//================================================================================================================
// Tell whether a Node is selected
//
// Arguments
//  PNode    Pointer to the Node
//
// Description
//  Returns TRUE if PNode->Selection is E3dNodeSelNODE, E3dNodeSelBRANCH or E3dNodeSelBRANCH_ROOT.
//  Otherwise it returns FALSE, even if PNode->Selection is E3dNodeSelOBJECT.
//  This function is used for distingishing whether an entire Node is selected or only one of its Objects (e.g. Mesh) or SubObjectsw (e.g. a PolyGroup).
//
// Return value
//  TRUE if PNode->Selection is E3dNodeSelNODE, E3dNodeSelBRANCH or E3dNodeSelBRANCH_ROOT, otherwise FALSE.
//================================================================================================================
static inline EBool E3dNode_IsSelected(const E3dNode* PNode)
{
	switch(PNode->Selection)
	{
		case E3dNodeSelNODE:
		case E3dNodeSelBRANCH:
		case E3dNodeSelBRANCH_ROOT:
		return(TRUE);

		default:
		return(FALSE);
	}
}


extern E3dNodeClass  E3dNode_class[];

extern E3dNodeComponentClass   E3dNodeComponent_class[];
extern E3dNodeComponentClass*  E3dAimConstraintNodeComponent_class;
extern E3dNodeComponentClass*  E3dJointNodeInfo_class;
extern E3dNodeComponentClass*  E3dIKEffectorNodeInfo_class;


extern void                    (*E3dNodeComponentClass_GUIFreeFunc)(E3dNodeComponentClass* PClass);

#define E3dConstraintDRIVING  0x01
#define E3dConstraintDRIVEN   0x02

#define E3dConstraintNodeComponentCore\
	E3dNodeComponentCore;\
	E3dNode*  Node;                     /* Point back to the Node that has this NodeComponent (for _Update's sake) */\
	EListA(, E3dNode*, AffectedNodes);  /* The Nodes affected by this constraint (e.g., if it's an AimConstraint NodeComponent on the Aim Node, these will point to the Nodes that are oriented toward this Node).  */\
	                                    /* This will be an emptly list, if this E3dConstraintNodeComponent is on the affected Node, in which case E3dConstraintDRIVEN will be set in ConstraintFlags.               */\
	uint8_t   ConstraintFlags           /* Flags E3dConstraintDRIVING E3dConstraintDRIVEN */


typedef struct
{
	E3dConstraintNodeComponentCore;
} E3dConstraintNodeComponent;

extern E3dNodeComponentClass   E3dConstraintNodeComponent_class[];

EList_s(extern, E3dNodeComponentClassPtr, E3dNodeComponent_Classes);


// EOLD
typedef struct
{
	E3dNodeComponentCore;

	E3dNode*  AffectedNode;
} E3dOldInterestPoint;


extern E3dNodeComponentClass  _E3dOldInterestPointNodeComponent_class[];

extern void  _E3dNode_ConvertOldInterestPoint(E3dNode* PNode);


static inline EBool E3dNode_IsDescendant(E3dNode* PNode, E3dNode* PAncestorNode)
{
	if(PNode == PAncestorNode) return(TRUE);
	while(PNode->Parent)
	{
		if(PNode->Parent == PAncestorNode) return(TRUE);
		PNode = PNode->Parent;
	}
	return(FALSE);
}

static inline E3dNode* E3d_RootNode(E3dNode* PNode)
{
	while(PNode->Parent) PNode = PNode->Parent;
	return(PNode);
}


//--------------------------------
// Node.c
//--------------------------------
EListA_s(extern, XeInfoPanelPtr, E3d_NodePanels);

extern EIndex                  E3dNode_GetChildIndex(const E3dNode* PNode);

extern EBool                   E3dNode_IsInHierarchy(E3dNode* PNode);

extern EIndex                  E3dNode_LockFind(const E3dNode* PNode, const E3dLock* PLock);
extern EChangeFlags            E3dNode_LockAdd(E3dNode* PNode, E3dLock* PLock);
extern EChangeFlags            E3dNode_LockDel(E3dNode* PNode, E3dLock* PLock);
extern EChangeFlags            E3dNodes_LockAdd(E3dNode** PNode, const EIndex PNumOfNodes, E3dLock* PLock);
extern EChangeFlags            E3dNodes_LockDel(E3dNode** PNode, const EIndex PNumOfNodes, E3dLock* PLock);

extern E3dNodeComponent*       E3dNodeComponent_New(E3dNodeComponentClass* PClass);
extern void                    E3dNodeComponent_Free(E3dNodeComponent* PComponent);

extern E3dNodeComponent*       E3dNodeComponent_Add(E3dNode* PNode, E3dNodeComponentClass* PClass);
extern E3dNodeComponent*       E3dNodeComponent_AddCopy(E3dNode* POldNode, E3dNode* PNode, E3dNodeComponent* POldComponent);
extern int                     E3dNodeComponent_Remove(E3dNode* PNode, E3dNodeComponent* PInfo);
extern int                     E3dNodeComponents_RemoveByClass(E3dNode* PNode, E3dNodeComponentClass* PClass);
extern void                    E3dNodeComponents_RemoveAll(E3dNode* PNode);

extern E3dNodeComponent*       E3dNodeComponent_ByClass(E3dNode* PNode, const E3dNodeComponentClass* PClass, const EBool PAdd);
extern E3dNodeComponent*       E3dNodeComponent_BySuperClass(const E3dNode* PNode, const E3dNodeComponentClass* PClass);

extern E3dNodeComponentClass*  E3dNodeComponent_ClassByName(const char* PName);
extern E3dNodeComponentClass*  E3dNodeComponent_ClassRegister(E3dNodeComponentClass* PClassTemplate);
extern void                    E3dNodeComponent_ClassDeactivate(E3dNodeComponentClass* PClass);
extern void                    E3dNodeComponent_ClassesFree(void);

extern EChangeFlags            E3dNode_CallRemoveProcs(E3dNode* PNode);

extern EChangeFlags            E3dNode_UpdateForDisplay(E3dNode* PNode, const EUpdateFlags PFlags);
extern E3dMaterial*            E3dNode_InheritMaterial(E3dNode* PNode, const EBool PFallBackToDefaultMaterial);

extern EBool                   E3dNode_IsEffector(E3dNode* PNode);
extern void                    E3dNode_CameraNodeCheckAimPoint(E3dScene* PScene, E3dNode* PNode);


//--------------------------------
// NodeHrc.c
//--------------------------------
extern E3dNode*      E3dNode_BranchNextNodeDepthFirst(E3dNode* PNode, E3dNode* PBranchRootNode);
extern E3dNode*      E3dNode_BranchGetLastNode(E3dNode* PBranchRootNode);
extern E3dNode*      E3dNode_BranchGetNodeAfter(const E3dNode* PBranchRootNode);

extern EIndex        E3dNode_HrcIsDescendantOf(const E3dNode* PNode, const E3dNode* PANode);

extern void          E3dNodeHrc_UpdateMaterialInheritance(E3dNode* PNode);
extern int           E3dNodeHrc_ReplaceTexturePaths(E3dNode* PNode, const char* PTexturePath);
extern EIndex        E3dNodeHrc_TextureMappingUpdate(E3dNode* PRootNode, const EBool PUpdateSTs);

extern void          E3dNodeHrc_UpdateChildAndNextSiblingLinks(E3dNode** PNodes, const EIndex PNNodes);
extern void          E3dNodeHrc_UpdateConnections(E3dNode* PRootNode);
extern void          E3dNodeHrc_UpdateMatrices(E3dNode* PBranchRootNode, const EBool PInheritTransformFromAbove);
extern void          E3dNodeHrc_UpdateMatricesNoScaling(E3dNode* PRootNode);

extern int           E3dNodeHrc_RemoveComponents(E3dNode* PRootNode);
extern int           E3dNodeHrc_FreezeRotation(E3dNode* PBranchRootNode);
extern int           E3dNodeHrc_FreezeScaling(E3dNode* PBranchRootNode);

extern EChangeFlags  E3dNodeHrc_CallNodeInfoUpdateProcs(E3dNode* PRootNode, const EChangeFlags PChangeFlags, const EBool PCheckOnly);
extern EChangeFlags  E3dNodeHrc_CallRemoveProcs(E3dNode* PRootNode);

extern EChangeFlags  E3dNodeHrc_CallNodeOutputs(E3dNode* PBranchRootNode,  EObjectAndComponentsListA* PObjectAndComponents);

extern void          E3dNode_ConnectChild(E3dNode* PParent, E3dNode* PChild, const EIndex PChildIndex, const EBool PCompensateTransform, const EBool PUpdate);
extern E3dNode*      E3dNode_DisconnectFromParent(E3dNode* PNode, const EBool PCompensateForTransforms);

extern void          E3dNode_AddChild(E3dNode* PParent, E3dNode* PChild);

extern void          E3dNodeHrc_Free(E3dNode* PRootNode);

extern void          E3dNodeHrc_CreateEdgesFromPolyData(E3dNode* PRootNode);
extern void          E3dNodeHrc_UpdateForDisplay(E3dNode* PNode, const EUpdateFlags PFlags);

extern void          E3dNodeHrc_CollectObjects(E3dNode* PNode, const uint32_t PObjectTypeFlags,  E3dObjectPtrListA* PObjects);
extern void          E3dNodeHrc_CollectMaterials(E3dNode* PRootNode, E3dMaterialPtrListA* PMaterials);

extern void          E3dNodeHrc_RemoveMappersFromTextures(E3dNode* PRootNode);

extern E3dNode*      E3dNodeHrc_NearestCommonAncestor(E3dNode** PNodes, const EIndex PCount);
extern EIndex        E3dNodeHrc_CollectIndependentBranches(E3dNodePtrListA* PNodes,  E3dNodePtrListA* PBranches);
extern void          E3dNodeHrc_BonesAutoscale(E3dNode* PRootNode, const E3dCoordinate PScale);

extern EIndex        E3dNodes_CreateUniqueBranchList(E3dNode*** PNodesPtr, const EIndex PNumOfNodes);


extern EBool         E3dNode_HasCamera(E3dNode* PNode);


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

#endif	// _E3D_Node_h
