/*-------------------------------------------------------------------*/
/*!																	!*/
/*!		Module SNDRNDOM.C: management of TYPE_RANDOM resources		!*/
/*!																	!*/
/*-------------------------------------------------------------------*/
 
#include "SNDinc.h"
 
#include "sndres.h"
#include "sndrndom.h"
#include "sndplay.h"
#include "sndrnd.h"
/*#include "sndmem.h"*/
 
#ifndef NO_ACP_SOUND
 
typedef struct random_voice_ random_voice;
struct random_voice_{
	long	lLowVoice;	/*voice of low-level resource*/
	SndBool	bValid;	/*TRUE while voice is active*/
	tduRefRes	uRes;/*Random resource associated*/
};
 
#define C_NEVER_CHOOSEN -2
#define C_NOTHING_CHOOSEN -1
 
/*----------------------------------------------------------------------------------
        Get the NON-random resource associated with a instance of RANDOM resource
-----------------------------------------------------------------------------------*/
tduRefRes SND_fn_tdGetEffectiveResourceRandom(tduRefRes res_random)
{
		tdstRandomElement*		pRandomElement;
		tdstTypeRandomMem*		pRandomList;
        tduRefRes       uRes;
        int             i;
		int				lElementChoosen=C_NOTHING_CHOOSEN;
		int				lPreviousElementChoosen,lPreviousResChoosen;
        SndBool         bFound;
		SndReal			rOffset;
 
		uRes=res_random;
		pRandomList=&(uRes.pstPtr->uRes.stRandom);
 
		/*lPreviousElementChoosen is set to C_NEVER_CHOOSEN if any element can be chosen*/
		/* otherwise, is set to the element wich can not be chosen*/
		/*lPreviousResChoosen is set to C_NEVER_CHOOSEN if any res can be chosen*/
		/* otherwise, is set to the res wich can not be chosen*/
		/*element = res + NOTHING*/
		lPreviousElementChoosen=C_NEVER_CHOOSEN;
		lPreviousResChoosen=C_NEVER_CHOOSEN;

		if (res_random.pstPtr->uRes.stRandom.lLastResChoosen>=0)
		{
			SNDLST2_M_StaticGetElementNumber(pRandomList,pRandomElement,res_random.pstPtr->uRes.stRandom.lLastResChoosen,i);
			if (!pRandomElement->bCanBeChosenTwice)
				lPreviousResChoosen=res_random.pstPtr->uRes.stRandom.lLastResChoosen;
		}

		if (res_random.pstPtr->uRes.stRandom.lLastElementChoosen>=0)
		{
			SNDLST2_M_StaticGetElementNumber(pRandomList,pRandomElement,res_random.pstPtr->uRes.stRandom.lLastElementChoosen,i);
			if (!pRandomElement->bCanBeChosenTwice)
				lPreviousElementChoosen=res_random.pstPtr->uRes.stRandom.lLastElementChoosen;
		}
		else
		{
			if ((res_random.pstPtr->uRes.stRandom.lLastElementChoosen==C_NOTHING_CHOOSEN) && (!res_random.pstPtr->uRes.stRandom.bNothingCanBeChosenTwice))
				lPreviousElementChoosen=C_NOTHING_CHOOSEN;
		}
 
		/*random number*/
		rOffset=SND_fn_rGetRandomSnd(C_SNDREAL_1);
 
		bFound=FALSE;
		if ((rOffset>=pRandomList->rProbNothing) || (lPreviousElementChoosen==C_NOTHING_CHOOSEN))
		{/*"Silence/nothing" is not chosen -> run the list to find the good element*/
			rOffset-=pRandomList->rProbNothing;
 
			SNDLST2_M_StaticForEachElementOf(pRandomList,pRandomElement,i)
			{
				if ((rOffset<=pRandomElement->rProb) && (lPreviousResChoosen!=i))
				{
					lElementChoosen=i;
					uRes=pRandomElement->uRes;
					bFound=TRUE;
					break;
				}
				rOffset-=pRandomElement->rProb;
			}
			if (!bFound)
			{/*if sum(prob)<1.0, choose the first element, or silence*/
				if (lPreviousElementChoosen!=0)
				{
					lElementChoosen=0;
					SNDLST2_M_StaticGetElementNumber(pRandomList,pRandomElement,0,i);
					uRes=pRandomElement->uRes;
					bFound=TRUE;
				}
				else
				{
					lElementChoosen=C_NOTHING_CHOOSEN;
					uRes.pstPtr=SND_C_RES_FANTOME_PTR;
					bFound=TRUE;
				}
			}
 		}
 
		if (!bFound)
		{/*no RANDOM element has been isolated (chosen); the "Nothing" element should be chosen (or there is a bug !)*/
			lElementChoosen=C_NOTHING_CHOOSEN;
			uRes.pstPtr=SND_C_RES_FANTOME_PTR;
		}
 
		res_random.pstPtr->uRes.stRandom.lLastElementChoosen=lElementChoosen;
		if (lElementChoosen!=C_NOTHING_CHOOSEN)
			res_random.pstPtr->uRes.stRandom.lLastResChoosen=lElementChoosen;

        return uRes;
}
 
tduRefRes SND_fn_tdGetResourceChoosenRandom(tduRefRes res_random)
{
	tdstRandomElement*		pRandomElement;
	int i;

	if ((res_random.pstPtr->uRes.stRandom.lLastElementChoosen==C_NOTHING_CHOOSEN)
		|| (res_random.pstPtr->uRes.stRandom.lLastElementChoosen>=SNDLST2_M_StaticGetNumberOfElements(&res_random.pstPtr->uRes.stRandom)))
		/* nothing chosen*/
		return SND_RES_FANTOME;
	else
	{
		SNDLST2_M_StaticGetElementNumber(&res_random.pstPtr->uRes.stRandom
			,pRandomElement
			,res_random.pstPtr->uRes.stRandom.lLastElementChoosen
			,i);
		
		return pRandomElement->uRes;
	}
}
 
/*
void SND_fn_vInitRandom(void)
{
}
 
void SND_fn_vDesInitRandom(void)
{
}
*/
 
long SND_fn_lPlayRandom(tduRefRes num_res,SoundParam* par,long prio,SND_td_pfn_vSoundCallback fn_callback,long par_callback)
{
	tduRefRes	low_res;
	random_voice *pstRandomVoice;
    SoundParam *pstSoundPar;
#ifdef NB_MICRO_MAX
    SoundParam stSoundPar;
#endif
 
	pstRandomVoice=SND_fn_pvMallocSndEx(E_ucSndBlockMain,sizeof(random_voice));
	/*pstRandomVoice=SND_fn_pvMallocSnd(sizeof(random_voice));*/
	pstRandomVoice->bValid=TRUE;
	pstRandomVoice->uRes.pstPtr=num_res.pstPtr;
 
	low_res=SND_fn_tdGetEffectiveResourceRandom(num_res);
 
	if (low_res.pstPtr!=SND_C_RES_FANTOME_PTR)
	{
#ifdef NB_MICRO_MAX
		pstSoundPar=&stSoundPar;
#else
		pstSoundPar=SND_fn_pCreateSoundParam(par->iNbLinks);
#endif
		memcpy(pstSoundPar,par,SND_M_lGetSizeOfSoundParam(par));
 
		/*sxd params are modified by Random static params*/
		pstSoundPar->ucVol=(unsigned char)(((long)pstSoundPar->ucVol*pstRandomVoice->uRes.pstPtr->ucVolume)>>7);
		pstSoundPar->iFlags|=C_SOUNDPARAM_COEF_VOL;
 
		pstRandomVoice->lLowVoice=SND_fn_lPlaySnd(low_res,pstSoundPar,prio,fn_callback,par_callback,&pstRandomVoice->bValid);
	
#ifndef NB_MICRO_MAX
		SND_fn_vDestroySoundParam(pstSoundPar);
#endif
		if (pstRandomVoice->lLowVoice!=C_PLAY_FAILED)
			return (long)pstRandomVoice;
	}
 
	/*failed*/
	SND_fn_vFreeSndEx(E_ucSndBlockMain,pstRandomVoice);
	/*SND_fn_vFreeSnd(pstRandomVoice);*/
 
	return C_PLAY_FAILED;
}
 
SndBool SND_CALL SND_fn_bSetParamRandom(long voice,SoundParam* par)
{
	SndBool bRet;
	random_voice *pstRandomVoice=(random_voice*)voice;
    SoundParam *pstSoundPar;
#ifdef NB_MICRO_MAX
    SoundParam stSoundPar;
#endif
 
#ifdef NB_MICRO_MAX
	pstSoundPar=&stSoundPar;
#else
	pstSoundPar=SND_fn_pCreateSoundParam(par->iNbLinks);
#endif
	memcpy(pstSoundPar,par,SND_M_lGetSizeOfSoundParam(par));
 
	/*sxd params are modified by Random static params*/
	pstSoundPar->ucVol=(unsigned char)(((long)pstSoundPar->ucVol*pstRandomVoice->uRes.pstPtr->ucVolume)>>7);
	pstSoundPar->iFlags|=C_SOUNDPARAM_COEF_VOL;
 
	if (pstRandomVoice->bValid && SND_fn_bSetParamSnd(pstRandomVoice->lLowVoice,pstSoundPar))
		bRet=TRUE;
	else
	{
		SND_fn_vFreeSndEx(E_ucSndBlockMain,pstRandomVoice);
/*		SND_fn_vFreeSnd(pstRandomVoice);*/
 
		bRet=FALSE;
	}

	return bRet;
}
 
SndBool SND_CALL SND_fn_bTestIsPlayingRandom(long voice)
{
	random_voice *pstRandomVoice=(random_voice*)voice;
 
	if (pstRandomVoice->bValid && SND_fn_bTestIsPlayingSnd(pstRandomVoice->lLowVoice))
		return TRUE;
	else
	{
		SND_fn_vFreeSndEx(E_ucSndBlockMain,pstRandomVoice);
		/*SND_fn_vFreeSnd(pstRandomVoice);*/
 
		return FALSE;
	}
}
 
void SND_CALL SND_fn_vStopRandom(long voice)
{
	random_voice *pstRandomVoice=(random_voice*)voice;
 
	if (pstRandomVoice->bValid)
		SND_fn_vStopSnd(pstRandomVoice->lLowVoice);
 
	SND_fn_vFreeSndEx(E_ucSndBlockMain,pstRandomVoice);
/*	SND_fn_vFreeSnd(pstRandomVoice);*/
}
 
void SND_CALL SND_fn_vPauseRandom(long voice)
{
	random_voice *pstRandomVoice=(random_voice*)voice;
 
	if (pstRandomVoice->bValid)
		SND_fn_vPauseSnd(pstRandomVoice->lLowVoice);
}
 
void SND_CALL SND_fn_vResumeRandom(long voice)
{
	random_voice *pstRandomVoice=(random_voice*)voice;
 
	if (pstRandomVoice->bValid)
		SND_fn_vResumeSnd(pstRandomVoice->lLowVoice);
}
 
void SND_fn_vRemoveCallbackRandom(long voice)
{
	random_voice *pstRandomVoice=(random_voice*)voice;
 
	if (pstRandomVoice->bValid)
		SND_fn_vRemoveCallbackSnd(pstRandomVoice->lLowVoice);
}
 
void SND_fn_vUnLoadResRandom(tdstBlockResourceMem* mem)
{
}
 
SndReal SND_CALL SND_fn_rGetPosRandom(long voice)
{
	random_voice *pstRandomVoice=(random_voice*)voice;
 
	if (pstRandomVoice->bValid)
		return SND_fn_rGetPosSnd(pstRandomVoice->lLowVoice);
	else
		return SND_C_POS_UNKNOWN;
}
 
SndReal SND_CALL SND_fn_rGetLengthRandom(long voice)
{
	random_voice *pstRandomVoice=(random_voice*)voice;
 
	if (pstRandomVoice->bValid)
		return SND_fn_rGetLengthSnd(pstRandomVoice->lLowVoice);
	else
		return SND_C_LENGTH_UNKNOWN;
}
 
 
/*---- gestion des ressources de type RANDOM*/
void SND_fn_vConvertResDiskToMemRandom(tdstBlockResourceDisk* disk,tdstBlockResourceMem* mem,void* ptrBegin)
{
        mem->Id=disk->Id;
        mem->eType=disk->eType;
        mem->eStorage=disk->eStorage;
        mem->ucVolume=disk->ucVolume;
        mem->bIsLoaded=TRUE;
        
        /*partie specifique au type de ressource*/
        mem->uRes.stRandom.ulNbElements = disk->uRes.stRandom.ulNbElements;
        mem->uRes.stRandom.rProbNothing= disk->uRes.stRandom.rProbNothing;
        mem->uRes.stRandom.bNothingCanBeChosenTwice= disk->uRes.stRandom.bNothingCanBeChosenTwice;
 
		/*parametre dynamique*/
		mem->uRes.stRandom.lLastElementChoosen=C_NEVER_CHOOSEN;
		mem->uRes.stRandom.lLastResChoosen=C_NEVER_CHOOSEN;
}
 
#ifndef NO_ACP_SCRIPT
SndBool SND_fn_bLoadResScriptRandom(tdstBlockResourceDisk *disk,tdstBlockResourceMem *mem)
{
        SND_fn_vConvertResDiskToMemRandom(disk,mem,NULL);
        return TRUE;
}
#endif
 
#ifndef NO_ACP_LDBIN
SndBool SND_fn_bLoadResBinaryRandom(tdstBlockResourceDisk *_pBRDisk,tdstBlockResourceMem *_pBRMem,char *pDataBloc)
{
        tdstRandomElement *pRandomArray,*pRandomElement;
        
        int iPos;
        /* Convert the ulDataOffset member to a pointer:*/
        pRandomArray=(tdstRandomElement *)(pDataBloc + _pBRDisk->ulDataOffset);
 
        /* Set pointers to range list:*/
        SNDLST2_M_StaticSetMember(&_pBRMem->uRes.stRandom,hFirstElement,pRandomArray);
        SNDLST2_M_StaticSetMember(&_pBRMem->uRes.stRandom, lNumberOfElements, _pBRDisk->uRes.stRandom.ulNbElements);
 
        /* Reset pointers in range:*/
        SNDLST2_M_StaticForEachElementOf( &_pBRMem->uRes.stRandom, pRandomElement, iPos)
        {
            pRandomElement->uRes.pstPtr=SND_fn_pGetBinRes(pRandomElement->uRes.Id);
        }
 
        SND_fn_vConvertResDiskToMemRandom(_pBRDisk,_pBRMem,NULL);
        return TRUE;
}
#endif
 
/*get the final simple resource*/
tduRefRes SND_fn_tduGetLowestResourceRandom(tduRefRes uHighRes,SoundParam* par,SndBool eval)
{
	tduRefRes uResRandom;

	if (eval)
		uResRandom=SND_fn_tdGetEffectiveResourceRandom(uHighRes);
	else
		uResRandom=SND_fn_tdGetResourceChoosenRandom(uHighRes);

	return SND_fn_tduGetLowestResourceSnd(uResRandom,par,eval);
}
/*
//return combinaison of static volumes for RanDom/Switch resources
unsigned char SND_fn_ucGetStaticFinalVolumeRandom(tduRefRes uHighRes,SoundParam* par)
{
	if (uHighRes.pstPtr==SND_C_RES_FANTOME_PTR) return C_SOUNDVOL_MAXI;
 
	switch (uHighRes.pstPtr->eType)
	{
	case TYPE_SAMPLE:
	case TYPE_MIDI:
	case TYPE_CDAUDIO:
	case TYPE_SEQUENCE:
	case TYPE_THEME:
		return C_SOUNDVOL_MAXI;
	case TYPE_RANDOM:
		return (unsigned char)(((long)uHighRes.pstPtr->ucVolume*(long)SND_fn_ucGetStaticFinalVolumeRandom(uHighRes,par))>>7);
	case TYPE_SWITCH:
		return (unsigned char)(((long)uHighRes.pstPtr->ucVolume*(long)SND_fn_ucGetStaticFinalVolumeSwitch(uHighRes,par))>>7);
	case TYPE_SPLIT:
	case TYPE_INVALID:
	default:
		SND_M_DisplayError(E_uwSndInvalidTypeOfResource,"");
        return C_SOUNDVOL_MAXI;
	}
}
 */

SndBool SND_CALL SND_fn_bSetResourceStaticVolumeRandom(tdstBlockResourceMem* pstRes,unsigned char ucVolume)
{
	pstRes->ucVolume=ucVolume;

	return TRUE;
}

 
#endif /*NO_ACP_SOUND*/
