/* ##C_FILE#
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
FILE :          DecompressFunc.h

DESCRIPTION :   Implementation file of module of AVI management : VDO
		Set of functions in charge of searching for a video unshrinker and using it.
		This module avoids the use of the high level function "AVIStreamGetFrame"    	

VERSION :       1.00/Nicolas Meyer/Creation

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

/* ##INCLUDE#----------------------------------------------------------------------------
                          Includes Files
---------------------------------------------------------------------------------------*/
#include "DecompressFunc.h"

/* ##GLOBVAR#----------------------------------------------------------------------------
                          Globale variable declaration
---------------------------------------------------------------------------------------*/
static LPBITMAPINFOHEADER	gpBmpInfoHdrOut;
static LPBITMAPINFOHEADER	gpBmpInfoHdrIn;
static PAVISTREAM		gpAviStream;
static ICINFO			gicInfo;
static HIC			ghIC;
static unsigned char*		gpchDataIn;
static unsigned char*		gpchDataOut;
static long			gnIndiceFrameLast;
static unsigned long		gnNbKeyFrame;
static long*			gpIndiceKeyFrame;
static unsigned long		gnIndiceLastKey;

/* ##F===================================================================================
NAME :          DecompressDesInitModule
DESCRIPTION :   uninit the uncompression video module 
INPUT :
OUTPUT :	BOOL
=========================================================================================
CREATION : Nicolas Meyer
LAST MODIFICATIONS : Date/Author/Modification  (5 maximum)
=======================================================================================*/
BOOL DecompressDesInitModule( )
{
	BOOL bResult;

	bResult	=	TRUE;
	if( ICDecompressEnd( ghIC ) != ICERR_OK ) {
		bResult	=	FALSE;
	}
	if( ICClose( ghIC ) != ICERR_OK ) {
		bResult	=	FALSE;
	}
	if( gpBmpInfoHdrOut )
		free( gpBmpInfoHdrOut );
	if( gpchDataIn )
		free( gpchDataIn );
	if( gpIndiceKeyFrame )
		free( gpIndiceKeyFrame );
	memset( & gicInfo, 0, sizeof(ICINFO) );
	ghIC				=	NULL;
	gpBmpInfoHdrOut		=	NULL;
	gpBmpInfoHdrIn		=	NULL;
	gpAviStream			=	NULL;
	gpchDataIn			=	NULL;
	gpchDataOut			=	NULL;
	gnIndiceFrameLast	=	-1;
	gnNbKeyFrame		=	0;
	return bResult;
}/*DecompressDesInitModule*/

/* ##F===================================================================================
NAME :          DecompressInitModule
DESCRIPTION :   init the uncompression video module, returns TRUE or FALSE depending
		of the success or failure of the operation 
INPUT :		LPBITMAPINFOHEADER	pbiIn
		LPAVISTREAMINFO		pAviStrInfoIn
		PAVISTREAM		ppAviStream
OUTPUT :	BOOL
=========================================================================================
CREATION : Nicolas Meyer
LAST MODIFICATIONS : Date/Author/Modification  (5 maximum)
=======================================================================================*/
BOOL DecompressInitModule( LPBITMAPINFOHEADER pBmpInfoHdrIn, LPAVISTREAMINFO pAviStrInfoIn, PAVISTREAM pAviStream )
{
	unsigned long		nCpt;
	unsigned long		nIndiceKey;
	unsigned long		nResult;
	long				nTemp;
	BITMAPINFOHEADER	BmpInfoHdrOut;

	ghIC	=	NULL;
	if( ! pBmpInfoHdrIn )
		return FALSE;

	if( ! pAviStrInfoIn )
		return FALSE;

	if( ! pAviStream )
		return FALSE;

	ghIC	=	ICGetDisplayFormat( NULL,
		pBmpInfoHdrIn,
 		& BmpInfoHdrOut,
		pBmpInfoHdrIn->biBitCount,
		0,
		0 );
	if( ! ghIC )
		return FALSE;

	if( ( nResult	=	ICGetInfo( ghIC, & gicInfo, sizeof(ICINFO) ) ) != sizeof(ICINFO) ) {
		ICClose( ghIC );
		return FALSE;
	}
	gpBmpInfoHdrIn	=	pBmpInfoHdrIn;
	gpAviStream		=	pAviStream;
	if( BmpInfoHdrOut.biSizeImage ) {
		gpBmpInfoHdrOut	=	(LPBITMAPINFOHEADER)malloc( BmpInfoHdrOut.biSizeImage + sizeof(BITMAPINFOHEADER) );
		gpchDataOut		=	(unsigned char*)( gpBmpInfoHdrOut + 1 );
	}
	else {
		gpBmpInfoHdrOut	=	(LPBITMAPINFOHEADER)malloc( BmpInfoHdrOut.biBitCount * BmpInfoHdrOut.biWidth * BmpInfoHdrOut.biHeight >> 3 + sizeof(BITMAPINFOHEADER) );
		gpchDataOut		=	(unsigned char*)( gpBmpInfoHdrOut + 1 );
	}
	memcpy( gpBmpInfoHdrOut, & BmpInfoHdrOut, sizeof(BITMAPINFOHEADER) );
	if( gpBmpInfoHdrIn->biSizeImage )
		gpchDataIn	=	(unsigned char*)malloc( gpBmpInfoHdrIn->biSizeImage );
	else
		gpchDataIn	=	(unsigned char*)malloc( gpBmpInfoHdrIn->biBitCount * gpBmpInfoHdrIn->biWidth * gpBmpInfoHdrIn->biHeight >> 3 );
	if( ( nResult	=	ICDecompressBegin( ghIC, gpBmpInfoHdrIn, gpBmpInfoHdrOut ) ) != ICERR_OK ) {
		memset( & gicInfo, 0, sizeof(ICINFO) );
		ICClose( ghIC );
		return FALSE;
	}

	gnNbKeyFrame		=	0;
	for( nCpt = 0; nCpt < pAviStrInfoIn->dwLength; /*nCpt++*/ ) {
		if( ( nTemp = AVIStreamFindSample( gpAviStream,
			(long)nCpt,
			FIND_KEY | FIND_NEXT ) ) != -1 ) {
			gnNbKeyFrame++;
			nCpt	=	(unsigned long)nTemp + 1;
		}
		else
			break;
	}
	gpIndiceKeyFrame	=	(long*)malloc( gnNbKeyFrame * sizeof(long) );

	for( nIndiceKey = 0, nCpt = 0; nIndiceKey < gnNbKeyFrame; nIndiceKey++ ) {
		gpIndiceKeyFrame[ nIndiceKey ]	=	AVIStreamFindSample( gpAviStream,
			(long)nCpt,
			FIND_KEY | FIND_NEXT );
		nCpt	=	gpIndiceKeyFrame[ nIndiceKey ] + 1;
	}

	gnIndiceFrameLast	=	-1;
	gnIndiceLastKey		=	0;
}/*DecompressInitModule*/

/* ##F===================================================================================
NAME :          DecompressGetFrame
DESCRIPTION :   return a pointer on uncompressed datas 
INPUT :		long 		nIndiceFrame
OUTPUT :	LPBITMAPINFOHEADER
=========================================================================================
CREATION : Nicolas Meyer
LAST MODIFICATIONS : Date/Author/Modification  (5 maximum)
=======================================================================================*/
LPBITMAPINFOHEADER DecompressGetFrame( long nIndiceFrame )
{
	long	nSamplesRead;
	long	nBytesRead;
	long	nResult;
	long	nDifKeyNext;
	long	nDifCur;

//	nDifKeyLast	=	abs( nIndiceFrame - gpIndiceKeyFrame[ gnIndiceLastKey ] );

	if( gnIndiceFrameLast >= nIndiceFrame )
		return gpBmpInfoHdrOut;
	else
		if( gnIndiceFrameLast == -1 )
			gnIndiceFrameLast++;

// Find the position of the "KeyFrame"
	while( gnIndiceLastKey < gnNbKeyFrame ) {
		if( ( nIndiceFrame >= gpIndiceKeyFrame[ gnIndiceLastKey ] ) &&
			( nIndiceFrame < gpIndiceKeyFrame[ gnIndiceLastKey + 1 ] ) )
			break;
		else {
			gnIndiceLastKey++;
			gnIndiceFrameLast	=	gpIndiceKeyFrame[ gnIndiceLastKey ];
		}
	}
// find the most favourable strategy
	nDifKeyNext	=	abs( nIndiceFrame - gpIndiceKeyFrame[ gnIndiceLastKey + 1 ] );
	nDifCur		=	abs( nIndiceFrame - gnIndiceFrameLast );
	if( nDifCur >= nDifKeyNext )
		gnIndiceFrameLast	=	gpIndiceKeyFrame[ gnIndiceLastKey + 1 ];
	do {
		nResult	=	AVIStreamRead( gpAviStream,
			gnIndiceFrameLast++,
			1,
			gpchDataIn,
			gpBmpInfoHdrIn->biSizeImage,
			& nBytesRead,
			& nSamplesRead );

		nResult	=	ICDecompress( ghIC,
			ICDECOMPRESS_HURRYUP,
			gpBmpInfoHdrIn,
			gpchDataIn,
			gpBmpInfoHdrOut,
			gpchDataOut );
	}
	while( gnIndiceFrameLast < nIndiceFrame + 1 );
	gnIndiceFrameLast--;
	return gpBmpInfoHdrOut;
}/*DecompressGetFrame*/