/*======================================================================*/
/* 3DLib								*/
/*									*/
/* Animation-related functions						*/
/*									*/
/* AUTHOR:	Gabor Nagy						*/
/* DATE:	1996-Nov-14 23:58:23					*/
/*									*/
/* 3DLib(TM) Copyright (C) 1995 by Gabor Nagy. All rights reserved.	*/
/*======================================================================*/
#include <stdio.h>
#include <float.h>
#include <math.h>

#include <EMalloc.h>
#include <EStrings.h>

#include <E3D/Animation.h>


E3dAnimationClass**	E3d_AnimationClasses=NULL;
int			E3d_NumOfAnimationClasses=0;


/*======================================================================================*/
/* Allocate and initialize an Animation structure					*/
/*											*/
/* Description										*/
/*  Allocates and initializes a E3dAnimation structure of a given Class.		*/
/*											*/
/* Return value										*/
/*  Pointer to the newly allocated E3dAnimation structure or NULL in case of an error.	*/
/*======================================================================================*/
E3dAnimation* E3d_AnimationAllocate(E3dAnimationClass* LClass)
{
 E3dAnimation*	LAnimation=(E3dAnimation*)EMalloc(LClass->StructSize);

 if(LAnimation)
 {
  LAnimation->Class=LClass;
  LAnimation->RefCnt=0;
  LAnimation->LastFrame=-99999999.0;
  LAnimation->Active=TRUE;
 }
 return(LAnimation);
}


/*======================================================================================*/
/* Free an Animation structure								*/
/*											*/
/* Description										*/
/*  This function first decrements the reference count (RefCnt) of the given Animation	*/
/*  structure (if it's not already zero).						*/
/*  After that, if RefCnt is still greater than zero, it means that something other	*/
/*  than the function calling CDs_AnimationFree() is still referring to this Animation,	*/
/*  so CDs_AnimationFree() will simply return.						*/
/*  If RefCnt is zero, CDs_AnimationFree() will call the 'FreeProc' method of the	*/
/*  animation and finally free the Animation structure.					*/
/*======================================================================================*/
void E3d_AnimationFree(E3dAnimation* LAnimation)
{
 E3dAnimationClass*	LClass=LAnimation->Class;


 if(LAnimation->RefCnt>0)
 {
  LAnimation->RefCnt-=1;
  if(LAnimation->RefCnt>0) return;
 }


 if(LClass->FreeProc) LClass->FreeProc(LAnimation);


 EFree(LAnimation);
}


/*==============================================*/
/* Find a AnimationClass by its name		*/
/*==============================================*/
E3dAnimationClass* E3d_AnimationClassFindByName(char* LName)
{
 E3dAnimationClass**	LClasses=E3d_AnimationClasses;

 if(LClasses)
 {
  E3dAnimationClass*	LClass;
  unsigned int		LC, LN;

  for(LC=0, LN=E3d_NumOfAnimationClasses;LC<LN;LC++)
  {
   LClass=LClasses[LC];
   if(EStr_StringsEqual(LClass->Name, LName)) return(LClass);
  }
 }
 return(NULL);
}


/*==============================================*/
/* Register a new Animation class		*/
/*==============================================*/
E3dAnimationClass* E3d_AnimationClassRegister(E3dAnimationClass* LClassTemplate)
{
 E3dAnimationClass**	LClasses=E3d_AnimationClasses;
 E3dAnimationClass*	LClass;
 unsigned int		LC, LN;


 if(LClasses)
 {
  for(LC=0, LN=E3d_NumOfAnimationClasses;LC<LN;LC++)
  {
   LClass=LClasses[LC];
   if(EStr_StringsEqual(LClass->Name, LClassTemplate->Name))
   {
    LClass->StructSize=LClassTemplate->StructSize;
    LClass->SetFrameProc=LClassTemplate->SetFrameProc;
    LClass->FreeProc=LClassTemplate->FreeProc;
    LClass->Resources=LClassTemplate->Resources;

    return(LClass);
   }
  }
 }

 if(LClasses==NULL) E3d_AnimationClasses=LClasses=(E3dAnimationClass**)EMalloc(sizeof(E3dAnimationClass));
 else
 {
  if((LClasses=(E3dAnimationClass**)ERealloc(E3d_AnimationClasses, sizeof(E3dAnimationClass*)*(E3d_NumOfAnimationClasses+1)))!=NULL) E3d_AnimationClasses=LClasses;
 }

 if(LClasses)
 {
  if((LClass=(E3dAnimationClass*)EMalloc(sizeof(E3dAnimationClass)))!=NULL)
  {
   LClasses[E3d_NumOfAnimationClasses]=LClass;

   LClass->Name=EStrDup(LClassTemplate->Name);
   LClass->StructSize=LClassTemplate->StructSize;
   LClass->SetFrameProc=LClassTemplate->SetFrameProc;
   LClass->FreeProc=LClassTemplate->FreeProc;
   LClass->Resources=LClassTemplate->Resources;
   E3d_NumOfAnimationClasses++;
  }
  return(LClass);
 }
 else return(NULL);
}


/*==============================================*/
/* Deactivate an AnimationClass			*/
/*==============================================*/
void E3d_AnimationClassDeactivate(E3dAnimationClass* LClass)
{
 E3dAnimationClass**	LClasses=E3d_AnimationClasses;

 if(LClasses)
 {
  unsigned int	LC, LN;

  for(LC=0, LN=E3d_NumOfAnimationClasses;LC<LN;LC++)
  {
   if(LClasses[LC]==LClass)
   {
    LClass->SetFrameProc=NULL;
    LClass->FreeProc=NULL;
    LClass->Resources=NULL;
   }
  }
 }
}


/*==============================================*/
/* Remove an AnimationClass			*/
/*==============================================*/
void E3d_AnimationClassRemove(E3dAnimationClass* LClass)
{
 E3dAnimationClass**	LClasses=E3d_AnimationClasses;

 if(LClasses)
 {
  unsigned int	LC, LN;

  for(LC=0, LN=E3d_NumOfAnimationClasses;LC<LN;LC++)
  {
   if(LClasses[LC]==LClass)
   {
    if(LClass->Name) EFree(LClass->Name);
    EFree(LClass);
    if(LC<(LN-1)) memmove(LClass, LClass+1, sizeof(E3dAnimationClass*)*(LN-LC-1));
    E3d_NumOfAnimationClasses-=1;
    if(E3d_NumOfAnimationClasses==0) { EFree(E3d_AnimationClasses);E3d_AnimationClasses=NULL; }
    else E3d_AnimationClasses=(E3dAnimationClass**)ERealloc(E3d_AnimationClasses, sizeof(E3dAnimationClass*)*E3d_NumOfAnimationClasses);
    return;
   }
  }
 }
}


/*==============================================*/
/* Find Animation by its Class			*/
/*==============================================*/
E3dAnimation* E3d_AnimationByClass(E3dAnimation** LAnimations, unsigned int LNumOfAnimations, E3dAnimationClass* LClass)
{
 if(LAnimations)
 {
  unsigned int		LC;

  for(LC=0;LC<LNumOfAnimations;LC++)
  {
   if(LAnimations[LC]->Class==LClass) return(LAnimations[LC]);
  }
 }
 return(NULL);
}
