/****************************************************************************
*
* SPECIFIC.C
*
*    VERSION : 00.00
*    CREATED : 08/06/98
*   MODIFIED : 08/06/98
*       TABS : 5
*
* DESCRIPTION
*	All specific functions (S_xxx) contained within
*
*****************************************************************************/

/*---------------------------------------------------------------------------
 *	Import Headers
\*--------------------------------------------------------------------------*/

#include <libsn.h>
#include <libpad.h>
#include "typedefs.h"
#include "gpu.h"
#include "cd.h"
#include "file.h"
#include "psxinput.h"
#include "spusound.h"
#include "3d_obj.h"
#include "specific.h"
#include "pad.h"
#include "fmv.h"
#include "..\game\control.h"
#include "..\game\camera.h"
#include "..\game\input.h"
#include "..\game\sound.h"
#include "..\game\savegame.h"
#include "..\game\text.h"
#include "..\game\lot.h"
#include "..\game\invdata.h"

/*---------------------------------------------------------------------------
 *	Constants
\*--------------------------------------------------------------------------*/

//#define LOG

#define ANIMATED_TEXTURE 5

#define DCACHE		0x1f800000
#define DCACHE_SIZE	0x300
#define DCACHE_TOP	(DCACHE + DCACHE_SIZE - 16)	// stupid nip fucks

/*---------------------------------------------------------------------------
 *	Globals
\*--------------------------------------------------------------------------*/

sint16* aranges;
sint32 water_effect=0;	// ## these can go
sint32 shade_effect=0;

int game_paused=0;

int GtSFXEnabled;
uint32 GdwUWClutSub;
uint32 swClutSub;

extern sint32 inputDB;

static LONG oldSP;

/*---------------------------------------------------------------------------
 *	Macros
\*--------------------------------------------------------------------------*/

#ifdef LOG
#define Log_(format,args...) DebugOut(format "\n" , ## args)
#else
#define Log_(args...)
#endif

/*---------------------------------------------------------------------------
 *	Local Functions
\*--------------------------------------------------------------------------*/

static void CalcVolumes(int nVolume,int nPan,int* pnLeftVolume,int* pnRightVolume)
{
	int nLeftVolume,nRightVolume;
	int nVol37Percent,nVol62Percent;
	int nAmount;

	nVolume*=Option_SoundFX_Volume;
	nVol62Percent=(nVolume+(nVolume>>2))>>1;
	nVol37Percent=(nVolume+(nVolume<<1))>>3;
	nPan+=0x4000;
	nAmount=(phd_cos(nPan)*nVol37Percent)>>W2V_SHIFT;
	nLeftVolume=(nVol62Percent-nAmount)>>8;
	nRightVolume=(nVol62Percent+nAmount)>>8;
	//Log_("%d,%d",nLeftVolume,nRightVolume);
	if (nLeftVolume>0x3fff)
		nLeftVolume=0x3fff;
	if (nRightVolume>0x3fff)
		nRightVolume=0x3fff;
//	*pnLeftVolume=nLeftVolume;
//	*pnRightVolume=nRightVolume;
	*pnLeftVolume=(nLeftVolume*nLeftVolume)>>14;
	*pnRightVolume=(nRightVolume*nRightVolume)>>14;
}

static int PlaySample(int nSample,int nVolume,int nPitch,int nPan,int nType)
{
	int nLeftVolume,nRightVolume;
	CalcVolumes(nVolume,nPitch,&nLeftVolume,&nRightVolume);
	return SPU_Play(nSample,nLeftVolume,nRightVolume,nPitch,nType);
}

#if defined(PLAY_FMV) && defined(RELOC)
UBYTE FMVActive = 0;
#endif

int S_PlayFMV(int sequence,int flag)
{
#ifdef PLAY_FMV
	LONG len;
	int file;
	char buf[80];
	LONG *mod, *rel;

	S_CDStop();

#ifdef RELOC

	if (!FMVActive)
	{
		init_game_malloc();

		file = FILE_Open("DATA\\FMV.MOD");

		FILE_Read((char *)&len, sizeof(LONG), 1, file);
		mod = (LONG *)game_malloc(len, 0);
		FILE_Read((char *)mod, sizeof(char), len, file);

		FILE_Read((char *)&len, sizeof(LONG), 1, file);
		rel = (LONG *)game_malloc(len, 0);
		FILE_Read((char *)rel, sizeof(char), len, file);

		FILE_Close(file);

		RelocateModule((LONG)mod, rel);
		VehiclePtr = mod;

		game_free(len, 0);

		FMVActive = 1;
	}

#endif

	sprintf(buf, "%s;1", GF_fmvfilenames[sequence]);

	return PlayFMV(buf, GF_fmv_data[sequence].endframe);

#endif
	return 0;
}




static int OnIdle(PFNIDLECHECKROUTINE pfnIdleCheckRoutine)
{
//	OptimiseOTagR(db.ot,db.nOTSize);
	return OnIdleLOTUpdate(pfnIdleCheckRoutine);
	//return (*pfnIdleCheckRoutine)();
}

#ifdef CREATE_MOVIE
static void DoScreenShotter()
{
	if ((GoodData(&GPad2)
	&&  (GetType(&GPad2) == CTL_PAD)
	&&  (GPad2.data.pad & PAD_L1) == 0))
	{
		uint32 f = GPU_GetFrameCount();

		PIC_SaveCurrentScreen();

		GnFrameCounter = f;
	}
}
#endif

/*---------------------------------------------------------------------------
 *	Public Functions
\*--------------------------------------------------------------------------*/

sint32 S_SoundPlaySample(int nSample,uint16 wVol,int nPitch,sint16 wPan)
{
	if (!GtSFXEnabled)
		return ERR_SOUND_NOT_ENABLED;

	return PlaySample(nSample, wVol, nPitch, wPan, SPU_SAMPLETYPE_NORMAL);
}

sint32 S_SoundPlaySampleLooped(int nSample, uint16 wVol, int nPitch, sint16 wPan)
{
	if (!GtSFXEnabled)
		return ERR_SOUND_NOT_ENABLED;

	return PlaySample(nSample, wVol, nPitch, wPan, SPU_SAMPLETYPE_LOOPED);
}

void S_SoundStopSample(sint32 handle)
{
	if (GtSFXEnabled)
		SPU_Stop(handle);
}

void S_SoundStopAllSamples()
{
	Log_(__FUNCTION__);

	if (GtSFXEnabled)
		SPU_StopAll();
}

void S_PauseAllSamples()
{
	Log_(__FUNCTION__);

	if (GtSFXEnabled)
		SPU_PauseAll();
}

void S_UnpauseAllSamples()
{
	Log_(__FUNCTION__);

	if (GtSFXEnabled)
		SPU_UnpauseAll();
}

void S_SoundSetPitch(sint32 handle, int nPitch)
{
	if (GtSFXEnabled)
		SPU_AdjustPitch(handle, nPitch);
}

void S_SoundSetPanAndVolume(sint32 handle, sint16 wPan, uint16 wVolume)
{
	if (GtSFXEnabled)
	{
		int nLeftVolume, nRightVolume;

		CalcVolumes(wVolume, wPan, &nLeftVolume, &nRightVolume);
		SPU_AdjustVolume(handle,nLeftVolume,nRightVolume);
	}
}

sint32 S_SoundSampleIsPlaying(sint32 handle)
{
	if (!GtSFXEnabled)
		return 0;

	return SPU_Status(handle);
}


/* -------- Misc. functions */

void	S_Pause()
{
	int nojoy;
	int nojoytotal;
	uint32 padd;
	TEXTSTRING *pausemessage=NULL, *nojoymessage=NULL;
#ifdef PSX_SONY_DEMO_SHELL
	int idle=0;
#endif
//##	#if defined(NTSC) && !defined(JAPAN)
	int	reset_option=0,choice;
//##	#endif

	game_paused = 1;
//	fades_enabled = 1;

	nojoytotal=0;
	SOUND_Stop();

	XASetFadeSpeed(32);
	XAFade(0);

	CreateMonoScreen();

	XAWaitFade();
	S_CDPause();									// also pause CD tracks

	camera.number_frames=2;

	do {
		UBYTE type;

		XAFade(0);	// try this in case of re triggering

		nojoy = 0;
		S_UpdateInput();

		type = PadInfoMode(0, InfoModeCurID, 0);

		if ((!GoodData(&GPad1))
		||  ((type != CTL_PAD) && (type != CTL_ANALOGC)))
		{
   			padd = 0;											// error reading pad...
			nojoy = 1;

			if (nojoymessage == NULL)
			{
  				nojoymessage = T_Print(0, 62, 9, GF_PSXStrings[PSSTR_NOJOY_TEXT]);
   				T_CentreH(nojoymessage, 1);
				T_FlashText(nojoymessage, 1, 20);
			}
			nojoytotal = 1;
		}
  		else
		{
   			padd = (~GPad1.data.pad)&0xffff;					// grab the pad data...
			if ( nojoytotal==1 )
				nojoytotal=2;
		}

		S_InitialisePolyList();
//		DrawGameInfo(0);
//		DrawRooms(camera.pos.room_number);
		DrawMonoScreen(0x70, 0x50, 0x50);
		T_DrawText();
		S_OutputPolyList();
		camera.number_frames = S_DumpScreen();

//##	#if defined(NTSC) && !defined(JAPAN)
		if ( input&IN_OPTION && !reset_option)
			reset_option = 1;

		if (reset_option==1)
		{
			inputDB=GetDebouncedInput(input);
			if ((choice=PauseResetRequester())>0)
			{
				do {
					S_UpdateInput();
				} while (input & IN_ACTION);

				if (choice==1)
					reset_option = 2;
				else
				{
					reset_option = 0;
					input|=IN_PAUSE;
				}
			}
		}
		if (reset_option==2)
		{
			inputDB=GetDebouncedInput(input);

			if ((choice=SureRequester())>0)
			{
				if (choice==1)
				{
					reset_flag = 1;

				}
				else
					input|=IN_PAUSE;

				reset_option = 0;
			}
		}
//##	#endif

#ifdef PSX_SONY_DEMO_SHELL
		if (!input && !inputDB)
		{
			if (idle++ > gameflow.noinput_time)
				reset_flag = 1;
		}
		else
			idle = 0;
#endif

//		S_InitialisePolyList();
//		S_CopyBufferToScreen();
//		S_DrawPic(); 											// DELS! bang tinted piccy onscreen...
//		T_DrawText();
//		S_OutputPolyList();
//		S_DumpScreen();

		if ( pausemessage==NULL )
		{
			pausemessage = T_Print(0, -24, 5, GF_PSXStrings[PSSTR_PAUSED_TEXT]);
   			T_CentreH(pausemessage, 1);
   			T_BottomAlign(pausemessage, 1);
		}

		if (nojoytotal==2)
		{
				T_RemovePrint(nojoymessage);
				nojoymessage=NULL;
		}

		if ( input&IN_PAUSE  || reset_flag )
		{
			T_RemovePrint(pausemessage);
			pausemessage=NULL;
			T_RemovePrint(nojoymessage);
			nojoymessage=NULL;
			break;
		}

	} while ( 1 );

//	S_InitFade(FADE_MID,FADE_MAX,0);
//	S_WaitFade();

	XASetFadeSpeed(8);
	XAFade(XAMasterVolume);
	S_CDRestart();

	if (pausemessage)
	{
		T_RemovePrint(pausemessage);
		pausemessage=NULL;
	}
	game_paused = 0;

	RemoveMonoScreen();
}

void S_Wait(int nFrames)
{
	int nTargetFrame=GnFrameCounter+nFrames;
	while (GnFrameCounter<nTargetFrame)
		;
}


/****************************************************************************
 *				Control The Screen Position
 ***************************************************************************/
void	S_control_screen_position(void)
{
//	printf( "X:%d Y:%d\n",savegame.screen_x,savegame.screen_y );
	if ( input&IN_FORWARD )
	{
   		savegame.screen_y--;

		if (savegame.screen_y < -12)
			savegame.screen_y = -12;
	}
	if ( input&IN_BACK )
	{
  		savegame.screen_y++;
		if ( savegame.screen_y>40 ) savegame.screen_y = 40;
	}
	if ( input&IN_LEFT )
	{
  		savegame.screen_x--;
		if ( savegame.screen_x<-10 ) savegame.screen_x = -10;
	}
	if ( input&IN_RIGHT )
	{
   		savegame.screen_x++;
		if ( savegame.screen_x>32 ) savegame.screen_x = 32;
	}

	GPU_SetScreenPosition(savegame.screen_x,savegame.screen_y);
}

#if 0
void	S_SetupBelowWater(int underwater)
{
	water_effect = 1;
	shade_effect = 1;	// -(wibble_offset+1);	// 1..64 but masked so it's ok
}

void	S_SetupAboveWater(int underwater)
{
	water_effect = underwater;
	shade_effect = 0;
}
#endif

void	S_AnimateTextures(int nframes)
{
	static int frame_comp=0;

	frame_comp += nframes;

	while (frame_comp > ANIMATED_TEXTURE)
	{
		int i, j;
		WORD *ptr;
		MMTEXTURE tmp1;

		ptr = aranges;

		for ( i=(int)*(ptr++) ; i>0 ; i--,ptr++ )	// Get number of Ranges
		{
			j = (int)*(ptr++);					// Get number of frames-1

			tmp1 = RoomTextInfo[*(ptr)];

			for ( ; j>0 ; j--,ptr++ )
				RoomTextInfo[*(ptr)] = RoomTextInfo[*(ptr+1)];
			RoomTextInfo[*(ptr)] = tmp1;
		}
		frame_comp -= ANIMATED_TEXTURE;
	}
}

void S_InsertInvBgnd(sint16 *objptr)
{
	phd_left = phd_top = 0;
	phd_right = phd_winxmax;
	phd_bottom = phd_winymax;

	phd_PutPolygons_AtBack(objptr, -1, 1);
}

sint32 S_DumpScreen()
{
	int nFrames;

//##	nFrames = GPU_Flip(OnIdle);
	nFrames = GPU_FlipNoIdle();

#ifdef CREATE_MOVIE
	DoScreenShotter();
#endif

#if !defined(CD_LOAD) && !defined(EZORAY)
	pollhost();
#endif

	return nFrames;
}


sint32 S_DumpScreenCine(void)
	{
	int nFrames;

	nFrames=GPU_FlipNoIdle();

#ifdef GAMEDEBUG
//	FntFlush(-1);
#endif
#ifdef CREATE_MOVIE
	DoScreenShotter();
#endif
#ifndef CD_LOAD
#ifndef EZORAY
	pollhost();
#endif
#endif
	//SPU_UpdateStatus();

	return nFrames;
	}

#if 0
void S_InitialiseScreen(int type)
	{
	phd_InitWindow(GAME_FOV);
	}
#endif

void S_InitialisePolyList(void)
{
//	int nY;

	GPU_BeginScene();

//##	nY=GPU_GetDrawRect(BUF_NEXT)->y;		// draw rect of frame during which this stuff will be drawn...
//##	dude_envmap_tpage=getTPage(2,0,128,nY)<<16;
}
