/*=========================================================================
*
* Merge.c	- merge functions
*
* Version			1.0
* Revision date
*
*=======================================================================*/
#include "Merge.h"

#include "print.h"

#include "ModLib.h"
#include "ModLoad.h" 


//--- Macros ---------------------------------------------------------

#define MLT_M_xEpsilon	1e-3

#define MLT_M_bVectorEqualWithEpsilon( _p_stVect1_, _p_stVect2_)\
(\
		( MTH_M_bEqualWithEpsilon((_p_stVect1_)->xX, (_p_stVect2_)->xX, MLT_M_xEpsilon ))\
	&&	( MTH_M_bEqualWithEpsilon((_p_stVect1_)->xY, (_p_stVect2_)->xY, MLT_M_xEpsilon ))\
	&&	( MTH_M_bEqualWithEpsilon((_p_stVect1_)->xZ, (_p_stVect2_)->xZ, MLT_M_xEpsilon ))\
)


//--- Global statics ---------------------------------------------------------

static char gs_szMergedName[ SCR_CV_ui_Cfg_MaxLenName ];


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



/****************************************************************************
*		Description:	merge super object child if needed (recursive function) 
*
*		Parameters:		_hSO	: SuperObject to treat		
*---------------------------------------------------------------------------
*		Revision date:                                      Author:         
*****************************************************************************/
void MLT_vMergeObjectsInHierarchy ( MLT_tdxHandleToSuperObject _hSO )
{
	MLT_tdxHandleToSuperObject	*hCurrentChild, *hLastChild, *hSecondChild, *hThirdChild;
	long						lGroupIndex;
	tdstMergeData				*p_stMergeData;
	
	// init pointers
	hCurrentChild = _hSO->d_hChild;
	hLastChild = hCurrentChild + _hSO->lNbChild;

	for (; hCurrentChild < hLastChild; hCurrentChild++)
	{
		// check if this is an element
		lGroupIndex = MLT_lMergeGetIndexAndName( (*hCurrentChild)->sName, gs_szMergedName);
		if ( lGroupIndex != -1 )
		{
			// create data and search for all item in group
			p_stMergeData = MLT_pstMergeDataCreate( gs_szMergedName, _hSO->lNbChild );
			
			MLT_vMergeDataAddSO( p_stMergeData, *hCurrentChild, lGroupIndex );
			for( hSecondChild = hCurrentChild + 1; hSecondChild < hLastChild; hSecondChild++)
			{
				lGroupIndex = MLT_lMergeGetIndexAndName( (*hSecondChild)->sName, gs_szMergedName);
				if ( (lGroupIndex != -1) && (stricmp( gs_szMergedName, p_stMergeData->szSOName) == 0) )
					MLT_vMergeDataAddSO( p_stMergeData, *hSecondChild, lGroupIndex );
			}

			//	Merge geometric object
			MLT_vComputeNewGeometric( p_stMergeData );

			// update super object (flags and SPOFlags must not be erased !)
			strcpy(_hSO->sName, p_stMergeData->szSOName);
			lGroupIndex = MLT_lMergeGetIndexAndName( (*hCurrentChild)->p_stMatrix->sName, gs_szMergedName);
			strcpy(_hSO->p_stMatrix->sName, gs_szMergedName);
			strcpy(_hSO->sGeometric, p_stMergeData->szObjName);

			// update hierarchy
			hSecondChild = hThirdChild = hCurrentChild;
			for( ; hSecondChild < hLastChild; hSecondChild++)
			{
				lGroupIndex = MLT_lMergeDataGetSOIndex( p_stMergeData, *hSecondChild );
				if (lGroupIndex == -1)
					*hThirdChild++ = *hSecondChild;
			}
	
			// update children of super-object
			_hSO->lNbChild -= p_stMergeData->lNumberOfSO;
			hLastChild -= p_stMergeData->lNumberOfSO;
			//free merge data
			MLT_vMergeDataFree( p_stMergeData );
		} 
		// test next level
		else
			MLT_vMergeObjectsInHierarchy(*hCurrentChild);
	}
}


/****************************************************************************
*		Description:	merge geometric object associated with the SO to be merged 
*
*		Parameters:		_p_stMergeData	: merge data initialized with list of SO to merge
*---------------------------------------------------------------------------
*		Revision date:                                      Author:         
*****************************************************************************/
void MLT_vComputeNewGeometric ( tdstMergeData *_p_stMergeData )
{
	long					lIndex;
	long					lGroupIndex;
	MLT_tdstGeometricObject *p_stObj;

	// allocate memory to store geometric pointer
	_p_stMergeData->d_p_stObject = (MLT_tdstGeometricObject **) malloc( sizeof(MLT_tdstGeometricObject *) * _p_stMergeData->lNumberOfSO);

	// get all geometric object to store
	for ( lIndex = 0; lIndex < _p_stMergeData->lNumberOfSO; lIndex++ )
	{
		_p_stMergeData->d_p_stObject[lIndex] = p_stObj = MLT_pFindInLib( _p_stMergeData->d_hSO[lIndex]->sGeometric );
		if(p_stObj == NULL)
  		{
			MLT_vOutput( C_ComRes_cErrorLine, "\nError : Can't load %s", _p_stMergeData->d_hSO[lIndex]->sGeometric);
			return;
		}
 		
		if (lIndex == 0)
			lGroupIndex = MLT_lMergeGetIndexAndName( p_stObj->sName, _p_stMergeData->szObjName );
		else
			p_stObj->bNotForSave = TRUE;
	}

	// merge all elements
	_p_stMergeData->p_stMergedObject = p_stObj = (MLT_tdstGeometricObject *) malloc( sizeof(MLT_tdstGeometricObject) );
	memset( p_stObj, 0, sizeof(MLT_tdstGeometricObject) );
	strcpy( p_stObj->sName, _p_stMergeData->szObjName );
	// count number of point, element, vertex, ...
	for (lIndex = 0; lIndex < _p_stMergeData->lNumberOfSO; lIndex++ )
	{
		p_stObj->lNbLod = _p_stMergeData->d_p_stObject[lIndex]->lNbLod;
		p_stObj->xNbPoints += _p_stMergeData->d_p_stObject[lIndex]->xNbPoints;
		p_stObj->xNbElements += _p_stMergeData->d_p_stObject[lIndex]->xNbElements;
		p_stObj->xNbEdges += _p_stMergeData->d_p_stObject[lIndex]->xNbEdges;
	}
	// allocate new object
	MLT_xAllocateGeometric( p_stObj );

	// merge geometric data
	MLT_vMergeGeometric( _p_stMergeData );
	
	// put new object in lib, in place of old one
	memcpy( _p_stMergeData->d_p_stObject[0], _p_stMergeData->p_stMergedObject, sizeof(MLT_tdstGeometricObject) );
}

/****************************************************************************
*		Description:	merge geometric element into result geometric object 
*
*		Parameters:		_p_stMergeData	: merge data initialized with list of SO Object and allocated result object
*---------------------------------------------------------------------------
*		Revision date:                                      Author:         
*****************************************************************************/
void MLT_vMergeGeometric ( tdstMergeData *_p_stMergeData )
{
	long								lIndex;
	MLT_tdstGeometricObject				*p_stObj;
	MLT_tdstGeometricObject				*p_stCurObj;
	long								*d_lIndex,  *p_stCurIndex;
	long								**d_p_lObjFirstVert;
	MTH3D_tdstVector					stTranslation;
	MTH3D_tdstVector					*p_stCurrentVertex, *p_stVertex, *p_stLastVertex;
	MTH3D_tdstVector					*p_stSearchVertex, *p_stLastSearchVertex;
	long								*p_lCurElementType;
	void								**pp_vCurElement, **pp_vLastElem, **pp_vSearchElement;
    MLT_tdstElementIndexedTriangles     *p_stETIT;
    MLT_tdstElementSprite				*p_stESt;
	MLT_tdstTripledIndex				*p_stFace, *p_stLastFace;
	MLT_tdstIndexedSprite				*p_stSprite, *p_stLastSprite;

	// allocate array for vertices index correspondance
	p_stObj = _p_stMergeData->p_stMergedObject;
	d_lIndex = (long *) calloc( p_stObj->xNbPoints, sizeof(long) );
	d_p_lObjFirstVert = (long **) malloc (_p_stMergeData->lNumberOfSO * sizeof(long *) );

	// copy all points of first object
	p_stCurObj = _p_stMergeData->d_p_stObject[0];
	memcpy( p_stObj->d_stListOfPoints, p_stCurObj->d_stListOfPoints, p_stCurObj->xNbPoints * sizeof( MTH3D_tdstVector ) );
	memcpy( p_stObj->d_stListOfPointsNormals, p_stCurObj->d_stListOfPointsNormals, p_stCurObj->xNbPoints * sizeof( MTH3D_tdstVector ) );
	memcpy( p_stObj->d_stListOfPointsReceivedLightIntensity, p_stCurObj->d_stListOfPointsReceivedLightIntensity, p_stCurObj->xNbPoints * sizeof( MLT_tdstColor ) );
	d_p_lObjFirstVert[0] = d_lIndex;
	p_stCurrentVertex = p_stObj->d_stListOfPoints + p_stCurObj->xNbPoints;

	// now copy all points of others object
	for ( lIndex = 1; lIndex < _p_stMergeData->lNumberOfSO; lIndex++)
	{
		MTH3D_M_vSubVector( &stTranslation, &_p_stMergeData->d_hSO[lIndex]->p_stMatrix->stTranslation, &_p_stMergeData->stOrigin );
		d_p_lObjFirstVert[ lIndex ] = d_p_lObjFirstVert[ lIndex - 1 ] + p_stCurObj->xNbPoints;
		p_stCurObj = _p_stMergeData->d_p_stObject[ lIndex ];
		
		p_stVertex = p_stCurObj->d_stListOfPoints;
		p_stLastVertex = p_stVertex + p_stCurObj->xNbPoints;
		p_stCurIndex = d_p_lObjFirstVert[ lIndex ];

		p_stLastSearchVertex = p_stCurrentVertex;
		
		for ( ; p_stVertex < p_stLastVertex; p_stVertex++, p_stCurIndex++)
		{
			MTH3D_M_vAddVector( p_stVertex, p_stVertex, &stTranslation );
			// search in all previous point for a equal point
			for (p_stSearchVertex = p_stObj->d_stListOfPoints; p_stSearchVertex < p_stLastSearchVertex; p_stSearchVertex++)
			{
				if( MLT_M_bVectorEqualWithEpsilon( p_stVertex, p_stSearchVertex ) )
                {
                    *p_stCurIndex = p_stSearchVertex - p_stObj->d_stListOfPoints;
                    break;
                }
			}
			if (p_stSearchVertex == p_stLastSearchVertex)
			{
				MTH3D_M_vCopyVector( p_stCurrentVertex, p_stVertex );
				*p_stCurIndex = p_stCurrentVertex++ - p_stObj->d_stListOfPoints;
			}
		}
	}

	// recompute number of points
	p_stObj->xNbPoints = p_stCurrentVertex - p_stObj->d_stListOfPoints;

	// copy first object element without change
	p_stCurObj = _p_stMergeData->d_p_stObject[0];
	p_lCurElementType = p_stObj->d_xListOfElementsTypes;
	pp_vCurElement = p_stObj->d_stListOfElements;
	memcpy( p_lCurElementType, p_stCurObj->d_xListOfElementsTypes, p_stCurObj->xNbElements * sizeof( long) );
	memcpy( pp_vCurElement, p_stCurObj->d_stListOfElements, p_stCurObj->xNbElements * sizeof( long) );
	p_lCurElementType += p_stCurObj->xNbElements;
	pp_vCurElement += p_stCurObj->xNbElements;

	// add others element with change to vertices point
	for (lIndex = 1; lIndex < _p_stMergeData->lNumberOfSO; lIndex++)
	{
		p_stCurObj = _p_stMergeData->d_p_stObject[ lIndex ];
		memcpy( p_lCurElementType, p_stCurObj->d_xListOfElementsTypes, p_stCurObj->xNbElements * sizeof( long) );
		memcpy( pp_vCurElement, p_stCurObj->d_stListOfElements, p_stCurObj->xNbElements * sizeof( long) );
		pp_vLastElem = pp_vCurElement + p_stCurObj->xNbElements;

		for (; pp_vCurElement < pp_vLastElem; pp_vCurElement++, p_lCurElementType++)
		{
			switch( *p_lCurElementType )
			{
				case 1:
					p_stETIT = (MLT_tdstElementIndexedTriangles *) *pp_vCurElement;
					p_stFace = p_stETIT->d_stListOfFacesTripled;
					p_stLastFace = p_stFace + p_stETIT->xNbFaces;

					for ( ; p_stFace < p_stLastFace ; p_stFace++ )
					{
						p_stFace->a3_xIndex[0] = d_p_lObjFirstVert[lIndex][ p_stFace->a3_xIndex[0] ];
						p_stFace->a3_xIndex[1] = d_p_lObjFirstVert[lIndex][ p_stFace->a3_xIndex[1] ];
						p_stFace->a3_xIndex[2] = d_p_lObjFirstVert[lIndex][ p_stFace->a3_xIndex[2] ];
					}
					break;

				case 3:
					p_stESt = (MLT_tdstElementSprite *) pp_vCurElement;
					p_stSprite = p_stESt->d_stListOfSprites;
					p_stLastSprite = p_stSprite + p_stESt->xNbSprites;

					for ( ; p_stSprite < p_stLastSprite ; p_stSprite++ )
					{
						p_stSprite->xCenterPoint = d_p_lObjFirstVert[lIndex][ p_stSprite->xCenterPoint ];
					}
					break;
			}
		}
	}

	// compute all element bounding box index and name
	pp_vCurElement = p_stObj->d_stListOfElements;
	pp_vLastElem = pp_vCurElement + p_stObj->xNbElements;
	p_lCurElementType = p_stObj->d_xListOfElementsTypes;
    
	for ( ; pp_vCurElement < pp_vLastElem; pp_vCurElement++, p_lCurElementType++ )
	{
		p_stETIT = (MLT_tdstElementIndexedTriangles *) *pp_vCurElement;
		p_stETIT->xIndexOfParallelBox = (ACP_tdxIndex) MLT_lMergeGetIndexAndName( p_stETIT->sName, gs_szMergedName);
		// look if element name is duplicate
		pp_vSearchElement = p_stObj->d_stListOfElements;
		for ( ; pp_vSearchElement < pp_vCurElement; pp_vSearchElement++ )
		{
			if (stricmp( gs_szMergedName, ((MLT_tdstElementIndexedTriangles *) (*pp_vSearchElement))->sName) == 0)
				break;
		}
		if (pp_vSearchElement != pp_vCurElement)
			sprintf( p_stETIT->sName, "%s%d", gs_szMergedName, p_stETIT->xIndexOfParallelBox );
		else
			strcpy( p_stETIT->sName, gs_szMergedName );
	}
}


/****************************************************************************
*		Description:	create a merge data structure and allocate memory to store information 
*
*		Parameters:		_szName		: name of merged group
*						_lNumberMax	: number max of element in group
*---------------------------------------------------------------------------
*		Revision date:                                      Author:         
*****************************************************************************/
tdstMergeData *MLT_pstMergeDataCreate ( char *_szName, long _lNumberMax )
{
	tdstMergeData *p_stMergeData;

	p_stMergeData = (tdstMergeData *) malloc (sizeof( tdstMergeData ) );
	
	strcpy( p_stMergeData->szSOName, _szName );
	p_stMergeData->d_hSO = (MLT_tdxHandleToSuperObject *) malloc ( sizeof(MLT_tdxHandleToSuperObject) * _lNumberMax );
	p_stMergeData->d_lIndex = (long *) malloc ( sizeof(long) * _lNumberMax );
	p_stMergeData->lNumberOfSO = 0;
	p_stMergeData->d_p_stObject = NULL;
	return p_stMergeData;
}

/****************************************************************************
*		Description:	free merge data. (no parameters validity check ) 
*
*		Parameters:		p_stMergeData : merge data to deallocate
*---------------------------------------------------------------------------
*		Revision date:                                      Author:         
*****************************************************************************/
void MLT_vMergeDataFree ( tdstMergeData *_p_stMergeData )
{
	free( _p_stMergeData->d_hSO );
	free( _p_stMergeData->d_lIndex );
	free( _p_stMergeData->d_p_stObject );
	free( _p_stMergeData );
}

/****************************************************************************
*		Description:	add a super-object to the list of data to merge 
*
*		Parameters:		_p_stMergeData : merge data to update
*						_hSO		   : super-object to add
*						_lGroupIndex   : index of the element
*---------------------------------------------------------------------------
*		Revision date:                                      Author:         
*****************************************************************************/
void MLT_vMergeDataAddSO( tdstMergeData *_p_stMergeData, MLT_tdxHandleToSuperObject _hSO, long _lGroupIndex )
{
	// if first element of group get merged object origin and move element at start of list
	if (_lGroupIndex == 0)
	{
		MTH3D_M_vCopyVector( &_p_stMergeData->stOrigin, &_hSO->p_stMatrix->stTranslation );
		if (_p_stMergeData->lNumberOfSO != 0)
		{
			_p_stMergeData->d_hSO[ _p_stMergeData->lNumberOfSO ] = _p_stMergeData->d_hSO[0];
			_p_stMergeData->d_lIndex[ _p_stMergeData->lNumberOfSO ] = _p_stMergeData->d_lIndex[0];
		}
		_p_stMergeData->d_hSO[0] = _hSO;
		_p_stMergeData->d_lIndex[0] = 0;
	}
	else
	{
		_p_stMergeData->d_hSO[ _p_stMergeData->lNumberOfSO ] = _hSO;
		_p_stMergeData->d_lIndex[ _p_stMergeData->lNumberOfSO ] = _lGroupIndex;
	}


	_p_stMergeData->lNumberOfSO++;
}




/****************************************************************************
*		Description:	get index in group of a SO 
*
*		Parameters:		_p_stMergeData : merge data to search
*						_hSO		   : super-object to search
*---------------------------------------------------------------------------
*		Revision date:                                      Author:         
*****************************************************************************/
long MLT_lMergeDataGetSOIndex ( tdstMergeData *_p_stMergeData, MLT_tdxHandleToSuperObject _hSO )
{
	long	lIndex;
	
	for (lIndex = 0; lIndex < _p_stMergeData->lNumberOfSO; lIndex++)
	{
		if ( _p_stMergeData->d_hSO[lIndex] == _hSO )
			return _p_stMergeData->d_lIndex[lIndex];
	}
	return -1;

}

/****************************************************************************
*		Description:	get group index and name from a spo or object name
*
*		Parameters:		_szName			: name to parse
*						_szResultName	: result name
*---------------------------------------------------------------------------
*		Revision date:                                      Author:         
*****************************************************************************/
long MLT_lMergeGetIndexAndName ( char *_szName, char *_szResultName )
{
	char *szGroupChar, *szEndGroup;
	long lGroupIndex;
	long lLength;


	if ( (szGroupChar = strchr( _szName, '&')) == NULL )
		return -1;

	// it's a super object to be grouped
	szEndGroup = szGroupChar + 1;
	lGroupIndex = 0;
	while ( 1 )
	{
		if (!isdigit(*szEndGroup))
			break;
		lGroupIndex = lGroupIndex*10 + *szEndGroup++ - '0';
	}

	// compute transformed name
	lLength = szGroupChar - _szName;
	memcpy( _szResultName, _szName, lLength );
	strcpy( _szResultName + lLength, szEndGroup );

	return lGroupIndex;
}


