/*=========================================================================
*
* Borders.c	- Borders & Frontier management
*
* Version			1.0
* Revision date
*
*=======================================================================*/
#include <Windows.h>

#include "Borders.h"

#include "conventi.h"
#include "defines.h"
#include "print.h"

#include "MatStack.h"

#include "ModLib.h"
#include "SPOLoad.h"
#include "SctSave.h"		

#include "Levels.h"


//--------------------------------------------------------------------


/****************************************************************************
*		Description:	delete the border referece in hierarchy
*
*		Parameters:		hSprObj		: root of the hierarchy
*						sParam		: name of the current sector
*---------------------------------------------------------------------------
*		Revision date:                                      Author:         
*****************************************************************************/
void MLT_vEraseBorderInHierarchy (MLT_tdxHandleToSuperObject hSprObj, char *sParam)
{
  long		i, lNbChild;
  xString	sAction, sSectorName, sFile, sIdent;
  
  
  // matrix parameters
  lNbChild = 0;
  MLT_xPushMatrix(hSprObj->p_stMatrix);

  // check each child
  for (i=0; i<hSprObj->lNbChild; i++ )
  {
    // get super-object name
	SCR_fn_v_RdL0_SplitSectionName(hSprObj->sName, sFile, sAction, sIdent);
  
    // root super-object
	if(  (strcmp(sAction, M_SuperObject) == 0) 
      && (strcmp(sIdent, M_Root) == 0) )
    {
      // get each sector
	  SCR_fn_v_RdL0_SplitSectionName(hSprObj->d_hChild[i]->stSector.sName, sFile, sAction, sSectorName);
      if(strstr(sSectorName, M_SPO_) != NULL)
      {
        xString sTmp;

        sprintf(sTmp, "%s", sSectorName+strlen(M_SPO_)); // delete the string "SPO_"
        sprintf(sSectorName, "%s", sTmp);
      }
    }
    // sector super-object
	else
      sprintf(sSectorName, "%s", sParam);
    
    lNbChild++;
    
	// check if the object is a fronteer
	if(strstr(hSprObj->d_hChild[i]->sName, M_Frontier) != NULL) 
    {
		// compute border
		if (!g_bRaymanII)
			MLT_vComputeAbsoluteBorder(hSprObj->d_hChild[i], sSectorName);
		else
			MLT_vComputeAbsoluteValuesOfBorder(hSprObj, i, sSectorName);

		// remove from hierarchy
		MLT_vDeleteHierarchy(hSprObj->d_hChild[i]);
		hSprObj->d_hChild[i] = NULL;
		lNbChild--;
    }
    // else iterate on next level
	else
    {
      MLT_vEraseBorderInHierarchy(hSprObj->d_hChild[i], sSectorName);
    }
  }
  MLT_xPopMatrix();
  
  // clean the hierarchy for NULL children
  for(i=0; i<lNbChild; i++)
  {
    MLT_tdxHandleToSuperObject	hTemp;
    int							j;
    
    j=i;
    while(hSprObj->d_hChild[j] == NULL) j++;
    hTemp = hSprObj->d_hChild[j];
    hSprObj->d_hChild[j] = NULL;
    hSprObj->d_hChild[i] = hTemp;
  }
  
  // update the number of children
  hSprObj->lNbChild = lNbChild;
}


/****************************************************************************
*		Description:	merge all sector's border to build a single one
*
*		Parameters:		hSprObj		: root of the hierarchy
*---------------------------------------------------------------------------
*		Revision date:                                      Author:         
*****************************************************************************/
void MLT_vMakeGoThroughInLib (MLT_tdxHandleToSuperObject hSprObj)
{
  MLT_tdstGeometricObject	**h_stObj;
  xString					sAction, sIdent, sIdent2, sFile, sObjName;
  long						i, j, lNbObj;
  
  // check each sector
  for (i=0; i<hSprObj->lNbChild; i++)
  {
    // get sector name
	SCR_fn_v_RdL0_SplitSectionName(hSprObj->d_hChild[i]->stSector.sName, sFile, sAction, sIdent);
    sprintf(sIdent2,"%s_%s",M_NameGothrough, sIdent);
    sprintf(sFile,"%s.mod",g_sLevelFileIn);
    // compute corresponding section name
	SCR_fn_v_RdL0_ComputeSectionName(sObjName, sFile, M_Geometric, sIdent2);
    // find the coresponding object
	MLT_vRequestObjInLib(sObjName, &h_stObj, &lNbObj);
    
    // merge the object with the current border
	for(j=1; j<lNbObj; j++)
    {
      MLT_vMergeObjects(h_stObj[0], h_stObj[j]);
      MLT_vEraseMergeObjInLib(h_stObj[j]);
    }
    // free useless objects
	if(lNbObj != 0) 
		free(h_stObj);
  }
}




/****************************************************************************
*		Description:	compute the border in the global repere (with absolute coordinate)
*
*		Parameters:		hSprObj		: fronteer super object
*						sSector		: sector name
*---------------------------------------------------------------------------
*		Revision date:                                      Author:         
*****************************************************************************/
void MLT_vComputeAbsoluteBorder(MLT_tdxHandleToSuperObject hSprObj, char *sSector)
{
  MLT_tdstGeometricObject	*p_stObj;
  xString					sAction, sSectorDest, sTemp, sType, sIdent, sFile;
  char						*p_cString;
  int						i;
  
  // find corresponding object
  p_stObj = MLT_pFindInLib(hSprObj->sGeometric);
  if(p_stObj == NULL)
  {
	MLT_vOutput( C_ComRes_cErrorLine, "\nError : Can't load %s", hSprObj->sGeometric);
	return;
  }
  
  MLT_xPushMatrix(hSprObj->p_stMatrix);

  // get geometric name
  SCR_fn_v_RdL0_SplitSectionName(p_stObj->sName, sFile, sAction, sIdent);
  // separate type of border
  MLT_vSplitSectionInformation('_', sIdent, sType, sTemp); 
  // separate destination sector
  MLT_vSplitSectionInformation('_', sTemp, sSectorDest, sAction); 
  // remove "SPO_" prefix
  if(strstr(sSectorDest, M_SPO_) != NULL)
  {
    xString sTmp;

    sprintf(sTmp, "%s", sSectorDest+4);
    sprintf(sSectorDest, "%s", sTmp);
  }

  // check the destination sector
  if(!MLT_bIsGoodSector(sSectorDest))
  {
	MLT_vOutput( C_ComRes_cErrorLine, "\nError : Bad sector's name %s in border %s", sSectorDest, p_stObj->sName);
  }

  // create border name (gothrough)
  sprintf(sIdent, "%s_%s", M_NameGothrough, sSector); // nom du border
  SCR_fn_v_RdL0_ComputeSectionName(p_stObj->sName, sFile, M_Geometric, sIdent);
  sprintf(p_stObj->sComment, "");
  
  // compute absolute vertices
  for(i=0; i<p_stObj->xNbPoints; i++)
  {
    MLT_xMulMatrixVertex(p_stObj->d_stListOfPoints+i, g_p_stCurrentMatrix, p_stObj->d_stListOfPoints+i);
  }
  
  p_cString = strstr(sType, M_FronRever);

  // reversible fronteer -> do symmetric
  if ((p_cString != NULL) || ((p_cString = strstr(sType, M_NewFronRever))!=NULL)) 
  {
    MLT_tdstGeometricObject *p_stObjRev;
    
    *p_cString = 0;
    
    MLT_vInformSectorHasBorder(g_hRoot, sSectorDest, sSector);
    MLT_vMakeReverseObjInLib(p_stObj, &p_stObjRev, sSectorDest);
    if (strstr(sType, M_Fermeture) == NULL) 
      MLT_vPointedSectorOfBorder(p_stObjRev, sSector);
  }
  
  // update sector links form border
  MLT_vInformSectorHasBorder(g_hRoot, sSector, sSectorDest);
  // it's not a closed border
  if (strstr(sType, M_Fermeture) == NULL) 
    MLT_vPointedSectorOfBorder(p_stObj, sSectorDest);
  
  MLT_xPopMatrix(); 
}



/****************************************************************************
*		Description:	compute the border as a bounding box
*
*		Parameters:		hSprObj		: root super object
*						i			: sector index
*						sSector		: sector name
*---------------------------------------------------------------------------
*		Revision date:                                      Author:         
*****************************************************************************/
void MLT_vComputeAbsoluteValuesOfBorder(MLT_tdxHandleToSuperObject hSprObj, int i, char *sSector)
{
  MLT_tdstGeometricObject	*p_stObj;
  xString					sAction, sIdent, sFile;
  xString					sSectorDest;
  
  //get the geometrical object
  p_stObj = MLT_pFindInLib(hSprObj->d_hChild[i]->sGeometric);
  if(p_stObj == NULL)
  {	
	MLT_vOutput( C_ComRes_cErrorLine, "\nError : Can't load %s", hSprObj->d_hChild[i]->sGeometric);
	return;
  }
  
  MLT_xPushMatrix(hSprObj->d_hChild[i]->p_stMatrix);
  
  // get geometric name
  SCR_fn_v_RdL0_SplitSectionName(p_stObj->sName, sFile, sAction, sIdent);

  // create border name (gothrough)
  sprintf(sIdent, "%s_%s", M_NameGothrough, sSector); // nom du border
  SCR_fn_v_RdL0_ComputeSectionName(p_stObj->sName, sFile, M_Geometric, sIdent);
  sprintf(p_stObj->sComment, "");
  // compute absolute vertices
  for(i=0; i<p_stObj->xNbPoints; i++)
  {
    MLT_xMulMatrixVertex(p_stObj->d_stListOfPoints+i, g_p_stCurrentMatrix, p_stObj->d_stListOfPoints+i);
  }

  // compute the minimum and maximum of the border, after modified the absolute values
  MLT_vComputeMinMaxPointOfBorder (p_stObj, &hSprObj->stSector.stPointMin, &hSprObj->stSector.stPointMax);
  // update sector links form border
  MLT_vInformSectorHasBorder(g_hRoot, sSector, sSectorDest);

  MLT_xPopMatrix(); 
}


/****************************************************************************
*		Description:	update sector links according to border
*
*		Parameters:		hRoot		: root super object
*						sSectorDest	: destination sector
*						sSectorOrg	: origin sector
*---------------------------------------------------------------------------
*		Revision date:                                      Author:         
*****************************************************************************/
void MLT_vInformSectorHasBorder(MLT_tdxHandleToSuperObject hRoot, char *sSectorDest, char *sSectorOrg)
{
  long i;
  char *sTmp;
  
  for (i=0; i<hRoot->lNbChild; i++)
  {
    sTmp = strstr(hRoot->d_hChild[i]->sName, "_sct");
    if(sTmp && (stricmp(sTmp+1, sSectorDest) == 0) )
    {
	  // update border flag
	  hRoot->d_hChild[i]->stSector.bIsVirtual = FALSE;
      // update graphic link
      sprintf(hRoot->d_hChild[i]->stSector.a_sSecteurGraphic[hRoot->d_hChild[i]->stSector.lNbSecteurGraphic], "%s", sSectorOrg);
      hRoot->d_hChild[i]->stSector.lNbSecteurGraphic++;
      // update activity link
      sprintf(hRoot->d_hChild[i]->stSector.a_sSecteurActivity[hRoot->d_hChild[i]->stSector.lNbSecteurActivity], "%s", sSectorOrg);
      hRoot->d_hChild[i]->stSector.lNbSecteurActivity++;
      // update collision link
      sprintf(hRoot->d_hChild[i]->stSector.a_sSecteurCollision[hRoot->d_hChild[i]->stSector.lNbSecteurCollision], "%s", sSectorOrg);
      hRoot->d_hChild[i]->stSector.lNbSecteurCollision++;
    }
  }
}

/****************************************************************************
*		Description:	update sector pointer into the border
*
*		Parameters:		p_stObj		: fronteer object
*						sNameSector	: destination sector
*---------------------------------------------------------------------------
*		Revision date:                                      Author:         
*****************************************************************************/
void MLT_vPointedSectorOfBorder (MLT_tdstGeometricObject *p_stObj, char *sNameSector)
{
  long i;
  
  for(i=0; i<p_stObj->xNbElements; i++)
  {
    switch(p_stObj->d_xListOfElementsTypes[i])
    {
		// update sector name in indexed triangle
		case MLT_C_xElementIndexedTriangles:
		  {
			MLT_tdstElementIndexedTriangles *p_stLocal;
			xString  sFile;
        
			p_stLocal = (MLT_tdstElementIndexedTriangles*)(p_stObj->d_stListOfElements[i]);
        
			sprintf(p_stLocal->sMaterial,"");
			sprintf(sFile,"%s.sct", g_sLevelFileIn);
			SCR_fn_v_RdL0_ComputeSectionName(p_stLocal->sSectorName, sFile, M_Sector, sNameSector);
		  }
		  break;
      
		// update sector name in face map descriptor
		case MLT_C_xElementFaceMapDescriptors:
		  {
			long j;
			MLT_tdstElementFaceMapDescriptors *p_stLocal;
        
			p_stLocal = (MLT_tdstElementFaceMapDescriptors*)(p_stObj->d_stListOfElements[i]);
        
			for(j=0; j<p_stLocal->xNbFaces; j++)
			{
			  sprintf(p_stLocal->d_stListOfFacesQuadrupled[j].hFaceMapDescriptor->sMaterial,"");
			  SCR_fn_v_RdL0_ComputeSectionName(p_stLocal->d_stListOfFacesQuadrupled[j].hFaceMapDescriptor->sSprObjName, 
				"*", 
				M_SuperObject, 
				sNameSector);
			}
		  }
		  break;

		// other types : not implementes
		case MLT_C_xElementSprites:
		  break;

		case MLT_C_xElementTMeshes:
		  break;
      
		case MLT_C_xElementPoints:
		  break;
      
		case MLT_C_xElementLines:
		  break;
      
		case MLT_C_xElementSpheres:
		  break;
      
		case MLT_C_xElementAlignedBoxes:
		  break;
      
		case MLT_C_xElementCones:
		  break;
    }
  }
}



/****************************************************************************
*		Description:	compute minmax points of a border
*
*		Parameters:		p_stObj		: border object
*						p_stMin		: min point
*						p_stMax		: max point
*---------------------------------------------------------------------------
*		Revision date:                                      Author:         
*****************************************************************************/
void MLT_vComputeMinMaxPointOfBorder (MLT_tdstGeometricObject *p_stObj, MTH3D_tdstVector *p_stMin, MTH3D_tdstVector *p_stMax)
{
	int		i;
	
	// check coordinates of each point 
	for (i = 0; i < p_stObj->xNbPoints; i++)
	{
		// check x coordinate
		if (p_stObj->d_stListOfPoints[i].xX < p_stMin->xX) p_stMin->xX = p_stObj->d_stListOfPoints[i].xX;
		if (p_stObj->d_stListOfPoints[i].xX > p_stMax->xX) p_stMax->xX = p_stObj->d_stListOfPoints[i].xX;
		// check y coordinate
		if (p_stObj->d_stListOfPoints[i].xY < p_stMin->xY) p_stMin->xY = p_stObj->d_stListOfPoints[i].xY;
		if (p_stObj->d_stListOfPoints[i].xY > p_stMax->xY) p_stMax->xY = p_stObj->d_stListOfPoints[i].xY;
		// check z coordinate
		if (p_stObj->d_stListOfPoints[i].xZ < p_stMin->xZ) p_stMin->xZ = p_stObj->d_stListOfPoints[i].xZ;
		if (p_stObj->d_stListOfPoints[i].xZ > p_stMax->xZ) p_stMax->xZ = p_stObj->d_stListOfPoints[i].xZ;
	}

}


/****************************************************************************
*		Description:	compute minmax points of a border, updating with absolute position
*
*		Parameters:		p_stObj		: border object
*						p_stMin		: min point
*						p_stMax		: max point
*						p_stMatrix	: absolute matrix
*---------------------------------------------------------------------------
*		Revision date:                                      Author:         
*****************************************************************************/
void MLT_vComputeMinMaxPointWithMatrix (MLT_tdstGeometricObject *p_stObj, MTH3D_tdstVector *p_stMin, MTH3D_tdstVector *p_stMax, MLT_tdstMatrix *p_stMatrix)
{
	MTH3D_tdstVector	p_stPoint;
	int					i;
	
	// check coordinates of each point 
	for (i = 0; i < p_stObj->xNbPoints; i++)
	{
		// get the absolute coordinates
        MLT_xMulMatrixVertex(&p_stPoint, p_stMatrix, p_stObj->d_stListOfPoints+i);
		// check x coordinate
		if (p_stPoint.xX < p_stMin->xX) p_stMin->xX = p_stPoint.xX;
		if (p_stPoint.xX > p_stMax->xX) p_stMax->xX = p_stPoint.xX;
		// check y coordinate
		if (p_stPoint.xY < p_stMin->xY) p_stMin->xY = p_stPoint.xY;
		if (p_stPoint.xY > p_stMax->xY) p_stMax->xY = p_stPoint.xY;
		// check z coordinate
		if (p_stPoint.xZ < p_stMin->xZ) p_stMin->xZ = p_stPoint.xZ;
		if (p_stPoint.xZ > p_stMax->xZ) p_stMax->xZ = p_stPoint.xZ;
	}
}







/****************************************************************************
*		Description:	decode a string : return the sub string before and after the caractere p
*
*		Parameters:		p			: character to search for
*						p_sSection	: initial string
*						p_sFirst	: string before p
*						p_sTemp		: string after p
*---------------------------------------------------------------------------
*		Revision date:                                      Author:         
*****************************************************************************/
void MLT_vSplitSectionInformation (char p, char *p_sSection, char *p_sFirst, char *p_sTemp)
{
  char *p_char;
  
  // iit first string
  sprintf(p_sFirst, "%s", p_sSection);
  p_char = p_sFirst;
  
  // cut first string on p
  if ((p_char=strchr(p_sFirst, p))!=NULL)
	  *p_char = 0;

  // update second string
  if(strlen(p_sFirst) == strlen(p_sSection))
    sprintf(p_sTemp, "");
  else
    sprintf(p_sTemp, "%s", ++p_char);
}







/****************************************************************************
*		Description:	attach the surface whn it belongs to several sectors
*
*		Parameters:		hRoot		: sector super-object
*						sSprObj		: surface super-object
*						sDirSave	: destination directory
*---------------------------------------------------------------------------
*		Revision date:                                      Author:         
*****************************************************************************/
BOOL MLT_bAttachSurfaceToSector(MLT_tdxHandleToSuperObject hRoot, MLT_tdxHandleToSuperObject hSprObj, char *sDirSave)
{
  xString sTemp, sSector;
  char *sSurface;

  sSurface = strstr(hSprObj->sName, M_Surface);

  // decoding the name of the target sectors
  MLT_vSplitSectionInformation('|', sSurface, sSector, sTemp);
  while(strlen(sTemp) != 0)
  {
    MLT_vSplitSectionInformation('|', sTemp, sSector, sTemp);
    if(MLT_bIsGoodSector(sSector))
    {
      MLT_vAddSurfaceToSector(hRoot, hSprObj->sName, sSector, sDirSave);
    }
    else
    {
	  MLT_vOutput( C_ComRes_cErrorLine, "\nError : Bad sector's name %s in surface %s", sSector, sSurface);
    }
  }

  return TRUE;
}


/****************************************************************************
*		Description:	add the surface to the sector structure
*
*		Parameters:		hRoot		: root super-object
*						sSprObj		: surface super-object
*						sSector		: sector name
*						sDirSave	: destination directory
*---------------------------------------------------------------------------
*		Revision date:                                      Author:         
*****************************************************************************/
void MLT_vAddSurfaceToSector(MLT_tdxHandleToSuperObject hRoot, char *sSprObj, char *sSector, char *sDirSave)
{
  long						 i, j, k;
  char						 *sSurface;
  xString					 sFile, sAction, sIdent, sMatrix, sFile2, sAction2, sIdent2;
  MLT_tdxHandleToSuperObject hSprSurface;

  // test the good surface
  sSurface = strstr(sSprObj, M_Surface);
  if (sSurface == NULL) 
	  return;

  // recover the SuperObjet surface
  sprintf(sFile, "%s%s.srf", sDirSave, g_sLevelFileIn);
  hSprSurface = MLT_hFindInHierarchy(sSurface, hRoot);

  // cover the sectors
  for(i=0; i<hRoot->lNbChild; i++)
  {
    if(strlen(hRoot->d_hChild[i]->stSector.sName) == 0) continue;

    SCR_fn_v_RdL0_SplitSectionName(hRoot->d_hChild[i]->stSector.sName, sFile, sAction, sIdent);
    
    // test the good sector
    if(stricmp(sIdent, sSector) == 0)
    {
      MLT_tdstMatrix *p_stMat, stMat1, stMat2;
      xString sTemp1, sTemp2;

      // recover the surface's matrix
      sprintf(sMatrix, "%s", hSprSurface->p_stMatrix->sName);
      p_stMat = hSprSurface->p_stMatrix;

      // sort the surface by growing altitude
      for(j=0; j<hRoot->d_hChild[i]->stSector.lNbSurface; j++)
      {
        if( p_stMat->stTranslation.xZ > hRoot->d_hChild[i]->stSector.a_stSurface[j].stMatrix.stTranslation.xZ )
        {
          stMat1 = hRoot->d_hChild[i]->stSector.a_stSurface[j].stMatrix;
          sprintf(sTemp1, "%s", hRoot->d_hChild[i]->stSector.a_stSurface[j].sGeometric);
          for(k=j; k<hRoot->d_hChild[i]->stSector.lNbSurface; k++)
          {
            stMat2 = hRoot->d_hChild[i]->stSector.a_stSurface[k+1].stMatrix;
            hRoot->d_hChild[i]->stSector.a_stSurface[k+1].stMatrix = stMat1;
            stMat1 = stMat2;

            sprintf(sTemp2, "%s", hRoot->d_hChild[i]->stSector.a_stSurface[k+1].sGeometric);
            sprintf(hRoot->d_hChild[i]->stSector.a_stSurface[k+1].sGeometric, "%s", sTemp1);
            sprintf(sTemp1, "%s", sTemp2);
          }
          break;
        }
      }

      // store the surface
      MLT_vComputeAltitudeSrf(p_stMat, hSprSurface->sGeometric);
      hRoot->d_hChild[i]->stSector.a_stSurface[j].stMatrix = *p_stMat;
      SCR_fn_v_RdL0_SplitSectionName(hSprSurface->sGeometric, sFile2, sAction2, sIdent2);
      sprintf(hRoot->d_hChild[i]->stSector.a_stSurface[j].sGeometric, "%s", sIdent2);

      hRoot->d_hChild[i]->stSector.lNbSurface++;
      break;
    }
  }
}


/****************************************************************************
*		Description:	compute the altitude of the surface.
*
*		Parameters:		p_stMat		: absolute position
*						sSurface	: surface name
*---------------------------------------------------------------------------
*		Revision date:                                      Author:         
*****************************************************************************/
void MLT_vComputeAltitudeSrf(MLT_tdstMatrix *p_stMat, char *sSurface)
{
  MLT_tdstGeometricObject	*p_stSrf;
  float						dAltitude;
  long						i;

  // get surface object
  p_stSrf = MLT_pFindInLib(sSurface);
  if(p_stSrf == NULL)
  {
    MLT_vOutput( C_ComRes_cErrorLine, "\nError : Can't load %s", sSurface);
	return;
  }

  // the z composante of the surface's vertices must be zero
  dAltitude = p_stSrf->d_stListOfPoints[0].xZ;
  for(i=0; i<p_stSrf->xNbPoints; i++)
  {
    p_stSrf->d_stListOfPoints[i].xZ -= dAltitude;
  }

  p_stMat->stTranslation.xZ += dAltitude;
}


