/*======================================================================*/
/* 3DLib								*/
/*									*/
/* 2DTexture-related functions						*/
/*									*/
/* AUTHOR:	Gabor Nagy						*/
/* DATE:	1996-Dec-24 23:52:10					*/
/*									*/
/* 3DLib(TM) Copyright (C) 1995 by Gabor Nagy. All rights reserved.	*/
/*======================================================================*/
#include <stdio.h>

#include <EMalloc.h>

#include <EGUI/Dialog.h>

#include <Image/Image.h>

#include <E3D/E3D.h>

#include <E3D/Material.h>

#include <E3D/Matrix.h>

#include <E3D/Polygon.h>

#include <E3D/Scene.h>


// Default texture for files couldn't be loaded
//
EpImage*	E3d_NotFound2DTextureImage=NULL;

#ifdef USEOpenGL
EpImage*	E3d_NotFound2DTextureGLImage=NULL;
GLuint		E3d_NotFound2DTextureGLIndex=E3dGL2DTxtNONE;
#endif	// USEOpenGL


E3d2DTextureShaderClass**	E3d_2DTextureShaderClasses=NULL;
int				E3d_NumOf2DTextureShaderClasses=0;


//================================================================================
// Print out the attributes of a 2DTexture
//
// Argument
//  E3d2DTexture*  L2DTexture      The E3d2DTexture to dump out
//
// Description
//  Prints the attributes of the given 2DTexture to the standard output.
//================================================================================
void E3d_Print2DTexture(E3d2DTexture* L2DTexture)
{
 printf("   Name: %s [%08x] RefCnt: %d\n", L2DTexture->Name, (unsigned int)L2DTexture, L2DTexture->RefCnt);
 printf("   File name: %s\n", L2DTexture->FileName);

 printf("   Components:  %d\n", L2DTexture->NumOfComponents);
 printf("   Size:        %dx%d\n", L2DTexture->XSize, L2DTexture->YSize);
 printf("   Cropping:    %d-%d, %d-%d\n", L2DTexture->SCropMin, L2DTexture->SCropMax, L2DTexture->TCropMin, L2DTexture->TCropMax);

 printf("   Repeats:     S: %d, T: %d\n", L2DTexture->SCount, L2DTexture->TCount);
 printf("   Scaling:     S: %f, T: %f\n", L2DTexture->SScaling, L2DTexture->TScaling);
 printf("   Offsets:     S: %f, T: %f\n", L2DTexture->SOffset, L2DTexture->TOffset);

 printf("   Rotation:    %f, %f, %f\n", L2DTexture->Rotation.X, L2DTexture->Rotation.Y, L2DTexture->Rotation.Z);

 printf("   Mapping:     ");
 switch(L2DTexture->MappingMethod)
 {
  case E3dTXT_XY:		printf("XY\n");break;
  case E3dTXT_XZ:		printf("XZ\n");break;
  case E3dTXT_YZ:		printf("YZ\n");break;
  case E3dTXT_UV:		printf("UV\n");break;
  case E3dTXT_CYLINDRICAL:	printf("CYLINDRICAL\n");break;
  case E3dTXT_SPHERICAL:	printf("SPHERICAL\n");break;
 }

 fflush(stdout);
}



// Texture "border color" for clamping
// (same as the shadow-map background color: white)
//
// FIXME: This should be set to the Material's Diffuse color...
//
float	Sh_TxBorder[4]= { 1.0, 1.0, 1.0, 1.0};


#ifdef USEOpenGL
//========================================================
// Set up Texture mapping for OpenGL
//========================================================
void E3dGL_2DTextureSetMapping(E3d2DTexture* L2DTexture)
{
 if(L2DTexture->GLIndex)
 {
  glBindTexture(GL_TEXTURE_2D, L2DTexture->GLIndex);

  if(L2DTexture->SCount>1) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  else glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);

  if(L2DTexture->TCount>1) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  else glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

//
//printf("L2DTexture [%s] %d %d\n", L2DTexture->FileName, L2DTexture->SCount, L2DTexture->TCount);fflush(stdout);

  glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, Sh_TxBorder);

  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

// Texture-coordinate generation method
//
  switch(L2DTexture->MappingMethod)
  {
   case E3dTXT_ENVIRONMENT_MAP:
    glEnable(GL_TEXTURE_GEN_S);\
    glEnable(GL_TEXTURE_GEN_T);\
    glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
    glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
   break;

   default:
   break;
  }
  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
 }
}
#endif	// USEOpenGL


//========================================
// Set a 2DTexture to default
//========================================
void E3d_2DTextureDefault(E3d2DTexture* L2DTexture)
{
 L2DTexture->Name=NULL;
 L2DTexture->FileName=NULL;
 L2DTexture->Flags=E3dTXTF_PIXELINTERPOLATION|E3dTXTF_TRANSPARENCYBW;

#ifdef USEOpenGL
 L2DTexture->GLIndex=E3dGL2DTxtNONE;
 L2DTexture->GLImage=NULL;
 L2DTexture->GLAlphaReference=0.0;
 L2DTexture->GLAlphaFunction=GL_GREATER;
#endif // USEOpenGL

 L2DTexture->NumOfComponents=3;						// RGB

 L2DTexture->Image=NULL;
 L2DTexture->XSize=0;L2DTexture->YSize=0;
 E3d_3DPositionInit(&(L2DTexture->Translation), 0.0, 0.0, 0.0);
 E3d_3DPositionInit(&(L2DTexture->Scaling), 1.0, 1.0, 1.0);
 E3d_RotationInit(&(L2DTexture->Rotation), 0.0, 0.0, 0.0);

 L2DTexture->Animation=E3dTXT_STATIC;
 L2DTexture->MappingMethod=E3dTXT_XY;
 L2DTexture->SCropMin=0;L2DTexture->SCropMax=0;L2DTexture->TCropMin=0;L2DTexture->TCropMax=0;
 L2DTexture->SScaling=1.0;
 L2DTexture->SOffset=0.0;
 L2DTexture->SCount=1;
 L2DTexture->TScaling=1.0;
 L2DTexture->TOffset=0.0;
 L2DTexture->TCount=1;

 L2DTexture->BlendingMask=E3dTXTB_INTENSITY;
 L2DTexture->BlendingFactor=1.0;
 L2DTexture->AmbientFactor=0.0;
 L2DTexture->DiffuseFactor=1.0;
 L2DTexture->SpecularFactor=0.0;
 L2DTexture->ReflectivityFactor=0.0;
 L2DTexture->TransparencyFactor=0.0;
 L2DTexture->RoughnessFactor=0.0;
 L2DTexture->ReflectionMapFactor=0.0;
 E3d_3DPositionInit(&(L2DTexture->SchemPosition), 0.0, 0.0, 0.0);
}


//========================================
// Allocate a 2DTexture
//========================================
E3d2DTexture* E3d_2DTextureAllocate()
{
 E3d2DTexture*	L2DTexture;

#ifdef E_MEMDEBUG
// assert(0);
 E_MemName="2DTexture";
#endif // E_MEMDEBUG

 if((L2DTexture=(E3d2DTexture*)EMalloc(sizeof(E3d2DTexture)))!=NULL) E3d_2DTextureDefault(L2DTexture);

 L2DTexture->RefCnt=0;
 return(L2DTexture);
}


#ifdef USEOpenGL
//========================================
// Free the GLImage of a 2DTexture
//========================================
void E3d_2DTextureFreeGLImage(EpImage* LGLImage, GLuint LGLIndex)
{
 EBool	LGoOn;

 if(LGLImage==E3d_NotFound2DTextureGLImage)
 {
  printf("E3d_2DTextureFreeGLImage  LGLImage (E3d_NotFound2DTextureGLImage) GLIndex %d ", LGLIndex);
 }
 else
 {
  printf("E3d_2DTextureFreeGLImage  LGLImage %08x [%s] GLIndex %d\n", (unsigned int)LGLImage, LGLImage->FileName, LGLIndex);
 }


 if(LGLImage->RefCnt>1) LGoOn=TRUE;
 else LGoOn=FALSE;

 if(LGLImage->RefCnt<2)
 {
  if(LGLIndex!=E3dGL2DTxtNONE) glDeleteTextures(1, &LGLIndex);
 }
 Ep_ImageFree(LGLImage, TRUE);


 if(LGoOn) printf("LGLImage->RefCnt %d\n", LGLImage->RefCnt);
 else printf("FREED\n");
 fflush(stdout);
}
#endif // USEOpenGL


//========================================
// Free a 2DTexture
//========================================
void E3d_2DTextureFree(E3d2DTexture* L2DTexture)
{
 if(L2DTexture==&E3d_Default2DTexture) return;

printf("E3d_2DTextureFree() %08x [%s] %d\n", (unsigned int)L2DTexture, L2DTexture->FileName, L2DTexture->RefCnt);fflush(stdout);

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

printf("Free2DTxt [%s] %d\n", L2DTexture->FileName, L2DTexture->GLIndex);fflush(stdout);

 if(L2DTexture->Name) EFree(L2DTexture->Name);

 if((L2DTexture->Image!=NULL)&&(L2DTexture->Image!=E3d_NotFound2DTextureImage)) { Ep_ImageFree(L2DTexture->Image, TRUE);L2DTexture->Image=NULL; }

#ifdef USEOpenGL
 if((L2DTexture->GLImage!=NULL)&&(L2DTexture->GLImage!=E3d_NotFound2DTextureGLImage)) E3d_2DTextureFreeGLImage(L2DTexture->GLImage, L2DTexture->GLIndex);
#endif // USEOpenGL

 EFree(L2DTexture);
}


//================================================================================================
// Clone a 2DTexture
//
// Arguments
//  E3d2DTexture*  L2DTexture         The 2DTexture to clone
//  E3d2DTexture** L2DTextures        List of 2DTextures for naming
//  unsigned int   LNumOf2DTextures   Number of entries in the 2DTextures array
//
// Description
//  Creates a duplicate (clone) of the given 2DTexture.<BR>
//  If the LNumOf2DTextures argument is not 0, this function will make sure that the name of
//  the new 2DTexture will be unique among the 2DTextures in the L2DTextures array.<BR>
//  For example: if the name of L2DTexture is "Iron" and the L2DTextures array already has
//  a 2DTexture called "Iron", the clone will be called "Iron-1".
//
// Return value	
//  Pointer to the new 2DTexture, or NULL in case of an error.
//
// See also
//  E3d_2DTextureFree, E3d_2DTextureDefault
//================================================================================================
E3d2DTexture* E3d_2DTextureClone(E3d2DTexture* L2DTexture, E3d2DTexture** L2DTextures, unsigned int LNumOf2DTextures)
{
 E3d2DTexture*	LNew2DTexture=NULL;

 if(L2DTexture)
 {
#ifdef E_MEMDEBUG
  E_MemName="2DTexture by E3d_2DTextureClone()";
#endif // E_MEMDEBUG

  if((LNew2DTexture=E3d_2DTextureAllocate())!=NULL)
  {
   unsigned int	LC;
   char*	LName;

   if(LNumOf2DTextures)
   {
    char**	LNames=(char**)EMalloc(sizeof(char*)*LNumOf2DTextures);

    if(LNames)
    {
     for(LC=0;LC<LNumOf2DTextures;LC++)
     {
      LNames[LC]=L2DTextures[LC]->Name;
     }
     LName=EStr_StringGetUnique(L2DTexture->Name, LNames, LNumOf2DTextures);
     EFree(LNames);
    }
    else LName=EStrDup(L2DTexture->Name);
   }
   else LName=EStrDup(L2DTexture->Name);

   LNew2DTexture->Name=LName;

   LNew2DTexture->FileName=EStrDup(L2DTexture->FileName);
   LNew2DTexture->Flags=L2DTexture->Flags;

#ifdef USEOpenGL
   LNew2DTexture->GLIndex=L2DTexture->GLIndex;
   LNew2DTexture->GLImage=L2DTexture->GLImage;if(LNew2DTexture->GLImage) LNew2DTexture->GLImage->RefCnt+=1;
   LNew2DTexture->GLAlphaFunction=L2DTexture->GLAlphaFunction;
   LNew2DTexture->GLAlphaReference=L2DTexture->GLAlphaReference;
#endif // USEOpenGL

   LNew2DTexture->Image=L2DTexture->Image;if(LNew2DTexture->Image) LNew2DTexture->Image->RefCnt+=1;
   LNew2DTexture->XSize=L2DTexture->XSize;
   LNew2DTexture->YSize=L2DTexture->YSize;
   LNew2DTexture->Animation=L2DTexture->Animation;
   LNew2DTexture->MappingMethod=L2DTexture->MappingMethod;
   LNew2DTexture->Translation=L2DTexture->Translation;
   LNew2DTexture->Scaling=L2DTexture->Scaling;
   LNew2DTexture->Rotation=L2DTexture->Rotation;
   LNew2DTexture->SScaling=L2DTexture->SScaling;
   LNew2DTexture->TScaling=L2DTexture->TScaling;
   LNew2DTexture->SOffset=L2DTexture->SOffset;
   LNew2DTexture->TOffset=L2DTexture->TOffset;
   LNew2DTexture->SCropMin=L2DTexture->SCropMin;
   LNew2DTexture->SCropMax=L2DTexture->SCropMax;
   LNew2DTexture->TCropMin=L2DTexture->TCropMin;
   LNew2DTexture->SCount=L2DTexture->SCount;
   LNew2DTexture->TCount=L2DTexture->TCount;

   LNew2DTexture->BlendingMask=L2DTexture->BlendingMask;
   LNew2DTexture->BlendingFactor=L2DTexture->BlendingFactor;
   LNew2DTexture->AmbientFactor=L2DTexture->AmbientFactor;
   LNew2DTexture->DiffuseFactor=L2DTexture->DiffuseFactor;
   LNew2DTexture->SpecularFactor=L2DTexture->SpecularFactor;
   LNew2DTexture->ReflectivityFactor=L2DTexture->ReflectivityFactor;
   LNew2DTexture->TransparencyFactor=L2DTexture->TransparencyFactor;
   LNew2DTexture->RoughnessFactor=L2DTexture->RoughnessFactor;
   LNew2DTexture->ReflectionMapFactor=L2DTexture->ReflectionMapFactor;
  }
 }
 return(LNew2DTexture);
}


//========================================
// Set a 2DTextureMapper to default
//========================================
void E3d_2DTextureMapperDefault(E3d2DTextureMapper* L2DTextureMapper, E3d2DTexture* L2DTexture)
{
 L2DTextureMapper->T2DTexture=L2DTexture;
 if(L2DTexture) L2DTexture->RefCnt+=1;

 L2DTextureMapper->MapOn=E3dTXMAP_GEOMETRY;
 L2DTextureMapper->Geometry=NULL;
 E3d_3DPositionInit(&(L2DTextureMapper->ProjectorCenter), 0.0, 0.0, 0.0);
 E3d_3DPositionInit(&(L2DTextureMapper->CenterOffset), 0.0, 0.0, 0.0);

 E3d_MatrixLoadIdentity(L2DTextureMapper->ProjectorMatrix);
 E3d_MatrixLoadIdentity(L2DTextureMapper->VertexToPixelMatrix);
}


//========================================
// Free an array of TextureMappers
//========================================
void E3d_2DTextureMappersFree(E3d2DTextureMapper* L2DTextureMappers, unsigned int LNumOfTextureMappers)
{
 unsigned int		LC;
 E3d2DTextureMapper*	L2DTextureMapper;

 L2DTextureMapper=L2DTextureMappers;
 for(LC=0;LC<LNumOfTextureMappers;LC++, L2DTextureMapper++)
 {
  E3d_2DTextureFree(L2DTextureMapper->T2DTexture);
  if(L2DTextureMapper->Geometry) E3d_GeometryFree(L2DTextureMapper->Geometry);
 }

 EFree(L2DTextureMappers);
}



//================================================
// Register a new 2DTextureShader class
//================================================
E3d2DTextureShaderClass* E3d_2DTextureShaderClassRegister(char* LName, int (*LEditProc)(E3d2DTexture*), int (*LDestroyProc)(E3d2DTexture*), int (*LGetImageProc)(E3d2DTexture*, double, int, int, int, int), EResource* LResources)
{
 E3d2DTextureShaderClass**	LClasses;
 E3d2DTextureShaderClass*	LClass;
 unsigned int			LC, LN;

 if((LClasses=E3d_2DTextureShaderClasses)!=NULL)
 {
  for(LC=0, LN=E3d_NumOf2DTextureShaderClasses;LC<LN;LC++)
  {
   LClass=LClasses[LC];
   if(EStr_StringsEqual(LClass->Name, LName))
   {
    LClass->EditProc=LEditProc;
    LClass->DestroyProc=LDestroyProc;
    LClass->GetImageProc=LGetImageProc;
    LClass->Resources=LResources;

    return(LClass);
   }
  }
 }

 if(LClasses==NULL) E3d_2DTextureShaderClasses=LClasses=(E3d2DTextureShaderClass**)EMalloc(sizeof(E3d2DTextureShaderClass));
 else
 {
  if((LClasses=(E3d2DTextureShaderClass**)ERealloc(E3d_2DTextureShaderClasses, sizeof(E3d2DTextureShaderClass*)*(E3d_NumOf2DTextureShaderClasses+1)))!=NULL) E3d_2DTextureShaderClasses=LClasses;
 }

 if(LClasses)
 {
  if((LClass=(E3d2DTextureShaderClass*)EMalloc(sizeof(E3d2DTextureShaderClass)))!=NULL)
  {
   LClasses[E3d_NumOf2DTextureShaderClasses]=LClass;

   LClass->Name=EStrDup(LName);
   LClass->EditProc=LEditProc;
   LClass->GetImageProc=LGetImageProc;
   LClass->Resources=LResources;
   E3d_NumOf2DTextureShaderClasses++;
  }
  return(LClass);
 }
 else return(NULL);
}


//================================================
// Deactivate a 2DTextureShader class
//================================================
void E3d_2DTextureShaderClassDeactivate(E3d2DTextureShaderClass* LClass)
{
 E3d2DTextureShaderClass**	LClasses=E3d_2DTextureShaderClasses;
 unsigned int			LC, LN;

 if(LClasses)
 {
  for(LC=0, LN=E3d_NumOf2DTextureShaderClasses;LC<LN;LC++)
  {
   if(LClasses[LC]==LClass)
   {
    LClass->EditProc=NULL;
    LClass->GetImageProc=NULL;
   }
  }
 }
}


//================================================
// Remove a 2DTextureShader class
//================================================
void E3d_2DTextureShaderClassRemove(E3d2DTextureShaderClass* LClass)
{
 E3d2DTextureShaderClass**	LClasses=E3d_2DTextureShaderClasses;
 unsigned int			LC, LN;

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



//========================================
// Free a 3DTexture
//========================================
void E3d_3DTextureFree(E3d3DTexture* L3DTexture)
{
 if(L3DTexture==&E3d_Default3DTexture) return;
 if(L3DTexture->RefCnt>0)
 {
  L3DTexture->RefCnt-=1;
  if(L3DTexture->RefCnt>0) return;
 }

 EFree(L3DTexture);
}
