#include "standard.h"
#include "global.h"
#include "ds.h"
#include "../game/sound.h"
#include "../game/control.h"

#include <math.h>
#include <process.h>
#include "winmain.h"

/*** defines ***/
#define FULL_ATTENUATION (50.0*100.0)	// 50dB (1/316)

/*** locals ***/
local int LnMasterVolume;

/*** sound stuff ***/

// Calc a volume factor for DirectSound
// nGameVolume is 0..7fff
// DS wants 0..-10000 dB
// Also takes into account global master volume
local CalcVolume(int nGameVolume)
	{
//	if (LnMasterVolume==0 || nGameVolume==0)
//		return -10000;
	nGameVolume*=LnMasterVolume;
//	if (nGameVolume<21)
//		return -10000;
//	return int(2000.0*log10(double(nGameVolume)/2097152.0));
	return int (FULL_ATTENUATION*((double(nGameVolume)/2097152.0)-1.0));
	}

// Calc a pan factor for DirectSound
// angle is -8000..7fff
// DS wants -10000..10000 dB
local int CalcPan(sint16 angle)
	{
	angle+=0x4000;
	if (angle<0)
		{
		angle=-angle;
		if (angle<0)
			angle=0x7fff;
		}
	angle-=0x4000;
	return angle>>4; //int((1000.0/16384.0)*angle);
	}

sint32 S_SoundPlaySample(int nSample,uint16 wVol,int nPitch,sint16 wPan)
	{
	if (!sound_active)
		return ERR_SOUND_NOT_ENABLED;
	return DS_StartSample(nSample,CalcVolume(wVol),nPitch,CalcPan(wPan),0);
	}

sint32 S_SoundPlaySampleLooped(int nSample,uint16 wVol,int nPitch,sint16 wPan)
	{
	if (!sound_active)
		return ERR_SOUND_NOT_ENABLED;
	return DS_StartSample(nSample,CalcVolume(wVol),nPitch,CalcPan(wPan),DSBPLAY_LOOPING);
	}

void S_SoundSetPanAndVolume(sint32 handle,sint16 wPan,uint16 wVolume)
	{
	if (!sound_active)
		return;
	DS_AdjustVolumeAndPan(handle,CalcVolume(wVolume),CalcPan(wPan));
	}

void S_SoundSetPitch(sint32 handle,int nNewPitch)
	{
	if (!sound_active)
		return;
	DS_AdjustPitch(handle,nNewPitch);
	}

void S_SoundSetMasterVolume(int nVolume)
	{
	LnMasterVolume=nVolume;
	}

void S_SoundStopSample(sint32 handle)
	{
	if (!sound_active)
		return;
	DS_StopSample(handle);
	}

void S_SoundStopAllSamples(void)
	{
	if (!sound_active)
		return;
	DS_StopAllSamples();
	}

sint32 S_SoundSampleIsPlaying(sint32 handle)
	{
	if (!sound_active)
		return false;
	return DS_IsChannelPlaying(handle);
	}

/*** CD stuff ***/
extern "C" int GetRealTrack(int track);

int cd_looping = 0;
UINT wDeviceID;

void S_CDLoop(void)
{
	MCI_PLAY_PARMS pp;
	MCI_STATUS_PARMS stat;

#ifdef HANDY_SNIPPETS
	MCI_SEEK_PARMS skp;

	// get current position
	stat.dwItem = MCI_STATUS_POSITION;
	if (mciSendCommand(wDeviceID,MCI_STATUS,MCI_STATUS_ITEM,(DWORD)&stat) == 0)
		dwCurrent = stat.dwReturn;
	else
		Log(LT_Error,"Can't get track position");

	// seek; unfortunately, this is blocking, so totally pointless
	skp.dwTo=MCI_MAKE_TMSF(cd_request,0,0,0);
	if (mciSendCommand(wDeviceID,MCI_SEEK,MCI_TO,(DWORD)&skp))
		Log(LT_Error,"Can't seek to track %d",cd_request);
#endif

	// if no looping track, then this function has nothing to do
	if (!cd_looping)
		return;

	// check for CD stopped; if so, restart the looping track
	stat.dwItem = MCI_STATUS_MODE;
	if (mciSendCommand(wDeviceID,MCI_STATUS,MCI_STATUS_ITEM,(DWORD)&stat) == 0)
	{
		if (stat.dwReturn == MCI_MODE_STOP)
		{
			// restart looping track
			pp.dwFrom=MCI_MAKE_TMSF(cd_looping,0,0,0);
			pp.dwTo=MCI_MAKE_TMSF(cd_looping+1,0,0,0);
			if (mciSendCommand(wDeviceID,MCI_PLAY,MCI_FROM|MCI_TO,(DWORD)&pp))
				Log(LT_Error,"Can't restart looping track %d",cd_looping);
		}
	}
//	else
//		Log(LT_Error,"Can't get track status");
}


#define CD_FIX
#ifdef CD_FIX
bool OpenCD(void)
{
	MCI_OPEN_PARMS op;
	MCI_SET_PARMS sp;

	/* Open audio device */
	op.lpstrDeviceType="cdaudio";
 	if (mciSendCommand(0,MCI_OPEN,MCI_OPEN_TYPE,(DWORD)&op)==0)
	{
		wDeviceID=op.wDeviceID;
		sp.dwTimeFormat=MCI_FORMAT_TMSF;
		if (mciSendCommand(wDeviceID,MCI_SET,MCI_SET_TIME_FORMAT,(DWORD)&sp))
		{
			Log(LT_Error,"Can't set TMSF format");
			return false;
		}
	}
	else
	{
		Log(LT_Error,"S_CDOpen failed");
		return false;
	}

	return true;
}

bool CloseCD(void)
{
	mciSendCommand(wDeviceID,MCI_CLOSE,0,0);
	return true;
}


bool CD_Init(void)
{
#ifdef CD_LOAD
	while (!FindCdDrive())
		if (!UT_OKCancelBox(MAKEINTRESOURCE(IDD_INSERT_CD),App.WindowHandle /*RF*/))
			return false;
#endif

	return true;
}

void CD_Cleanup(void)
{
	MCI_GENERIC_PARMS gp;

	/* Shutdown again */
	if (mciSendCommand(wDeviceID,MCI_STOP,0,(DWORD)&gp))
		Log(LT_Error,"Can't stop track");

	CloseCD();
}


void S_CDPlay(sint16 nTrack,int tMode)
	{
	Log(LT_Enter,"S_CDPlay: nTrack=%d; tMode=%d",nTrack,tMode);

	if (!Option_Music_Volume)
		return;

#ifndef DUDE_DEMO
#ifndef DEMO
	MCI_PLAY_PARMS pp;
	cdtrack=nTrack;
	nTrack = GetRealTrack(nTrack);

	OpenCD();

	// start track
	pp.dwFrom=MCI_MAKE_TMSF(nTrack,0,0,0);
	pp.dwTo=MCI_MAKE_TMSF(nTrack+1,0,0,0);
	if (mciSendCommand(wDeviceID,MCI_PLAY,MCI_FROM|MCI_TO,(DWORD)&pp))
		Log(LT_Error,"Can't play track %d", nTrack);

	CloseCD();

	// set looping flag if need be
	if (tMode)
		cd_looping = nTrack;
#endif
#endif
	}

void S_CDStop(void)
{
	Log(LT_Enter,"S_CDStop");

	if (cdtrack <= 0)
		return;
	
	MCI_GENERIC_PARMS gp;

	OpenCD();

	if (mciSendCommand(wDeviceID,MCI_STOP,0,(DWORD)&gp))
		Log(LT_Error,"Can't stop track");

	CloseCD();

	cd_looping = 0;
	cdtrack = 0;
}

int	S_StartSyncedAudio(int nTrack)
{
	MCI_SET_PARMS sp;
	MCI_PLAY_PARMS pp;

	cdtrack=nTrack;
	nTrack = GetRealTrack(nTrack);	// TODO: Uncomment this when S_CDPlay doesn't use this routine

	Log(LT_Enter,"S_StartSyncedAudio: Track %d", nTrack);

	OpenCD();

	sp.dwTimeFormat=MCI_FORMAT_TMSF;
	if (mciSendCommand(wDeviceID,MCI_SET,MCI_SET_TIME_FORMAT,(DWORD)&sp)==0)
	{

		//*** ToDo : Put This Back In When Tracks Finalised...
		
		pp.dwFrom=MCI_MAKE_TMSF(nTrack,0,0,0);
//		pp.dwTo=MCI_MAKE_TMSF(nTrack+1,0,0,0);
		if (mciSendCommand(wDeviceID,MCI_PLAY,MCI_FROM/*|MCI_TO*/,(DWORD)&pp))
			return 0;
	}
	else
		return 0;

	CloseCD();

	return 1;
}


int S_CDGetLoc(void)
{
	MCI_STATUS_PARMS stat;

	OpenCD();

	// get current position
	stat.dwItem = MCI_STATUS_POSITION;
	if (mciSendCommand(wDeviceID,MCI_STATUS,MCI_STATUS_ITEM,(DWORD)&stat) == 0)
		return MCI_TMSF_FRAME(stat.dwReturn) + 75 * (MCI_TMSF_SECOND(stat.dwReturn) + MCI_TMSF_MINUTE(stat.dwReturn) * 60);
	else
	{
		Log(LT_Error,"Can't get track position");
		return 0;
	}

	CloseCD();
}
#else

bool CD_Init(void)
{
	MCI_OPEN_PARMS op;
	MCI_SET_PARMS sp;

#ifdef CD_LOAD
	while (!FindCdDrive())
		if (!UT_OKCancelBox(MAKEINTRESOURCE(IDD_INSERT_CD),GhWnd))
			return false;
#endif


	/* Open audio device */
	op.lpstrDeviceType="cdaudio";
	if (mciSendCommand(0,MCI_OPEN,MCI_OPEN_TYPE,(DWORD)&op)==0)
	{
		wDeviceID=op.wDeviceID;
		sp.dwTimeFormat=MCI_FORMAT_TMSF;
		if (mciSendCommand(wDeviceID,MCI_SET,MCI_SET_TIME_FORMAT,(DWORD)&sp))
			Log(LT_Error,"Can't set TMSF format");
	}
	else
		Log(LT_Error,"S_CDOpen failed");
	return true;
}

void CD_Cleanup(void)
{
	MCI_GENERIC_PARMS gp;

	/* Shutdown again */
	if (mciSendCommand(wDeviceID,MCI_STOP,0,(DWORD)&gp))
		Log(LT_Error,"Can't stop track");

	mciSendCommand(wDeviceID,MCI_CLOSE,0,0);
}


void S_CDPlay(sint16 nTrack,int tMode)
	{
	Log(LT_Enter,"S_CDPlay: nTrack=%d; tMode=%d",nTrack,tMode);

	if (!Option_Music_Volume)
		return;

#ifndef DUDE_DEMO
#ifndef DEMO
	MCI_PLAY_PARMS pp;
	cdtrack=nTrack;
	nTrack = GetRealTrack(nTrack);

	// start track
	pp.dwFrom=MCI_MAKE_TMSF(nTrack,0,0,0);
	pp.dwTo=MCI_MAKE_TMSF(nTrack+1,0,0,0);
	if (mciSendCommand(wDeviceID,MCI_PLAY,MCI_FROM|MCI_TO,(DWORD)&pp))
		Log(LT_Error,"Can't play track %d", nTrack);

	// set looping flag if need be
	if (tMode)
		cd_looping = nTrack;
#endif
#endif
	}

void S_CDStop(void)
{
	Log(LT_Enter,"S_CDStop");

	if (cdtrack <= 0)
		return;
	
	MCI_GENERIC_PARMS gp;

	if (mciSendCommand(wDeviceID,MCI_STOP,0,(DWORD)&gp))
		Log(LT_Error,"Can't stop track");

	cd_looping = 0;
	cdtrack = 0;
}

int	S_StartSyncedAudio(int nTrack)
{
	MCI_SET_PARMS sp;
	MCI_PLAY_PARMS pp;

	cdtrack=nTrack;
	nTrack = GetRealTrack(nTrack);	// TODO: Uncomment this when S_CDPlay doesn't use this routine

	Log(LT_Enter,"S_StartSyncedAudio: Track %d", nTrack);

	sp.dwTimeFormat=MCI_FORMAT_TMSF;
	if (mciSendCommand(wDeviceID,MCI_SET,MCI_SET_TIME_FORMAT,(DWORD)&sp)==0)
	{
		pp.dwFrom=MCI_MAKE_TMSF(nTrack,0,0,0);
		pp.dwTo=MCI_MAKE_TMSF(nTrack+1,0,0,0);
		if (mciSendCommand(wDeviceID,MCI_PLAY,MCI_FROM|MCI_TO,(DWORD)&pp))
			return 0;
	}
	else
		return 0;

	return 1;
}


int S_CDGetLoc(void)
{
	MCI_STATUS_PARMS stat;

	// get current position
	stat.dwItem = MCI_STATUS_POSITION;
	if (mciSendCommand(wDeviceID,MCI_STATUS,MCI_STATUS_ITEM,(DWORD)&stat) == 0)
		return MCI_TMSF_FRAME(stat.dwReturn) + 75 * (MCI_TMSF_SECOND(stat.dwReturn) + MCI_TMSF_MINUTE(stat.dwReturn) * 60);
	else
	{
		Log(LT_Error,"Can't get track position");
		return 0;
	}
}
#endif


void S_CDVolume(int volume)
{
	UINT device, num, cdok, auxin;
	AUXCAPS caps;

	num = auxGetNumDevs();
	if (num > 0)
	{
		// go through devices and set volume for all CDAUDIO devices
		auxin = -1;
		cdok = 0;
		for (device=0; device<num; device++)
		{
			auxGetDevCaps(device, &caps, sizeof(caps));
			if (caps.wTechnology == AUXCAPS_CDAUDIO)
			{
				auxSetVolume(device, (volume<<24) + (volume<<8));
				cdok = 1;
			}
			else if (caps.wTechnology == AUXCAPS_AUXIN)
				auxin = device;
		}

		// may be that CD is attached to AUXIN, so if no CDAUDIO was found, set volume of that instead
		if (!cdok && auxin>=0)
			auxSetVolume(auxin, (volume<<24) + (volume<<8));
	}
	else
		Log(LT_Error,"CDVolume: Could not find CD device");
}
