#include "game.h"

#ifdef GAMEDEBUG
#define MANUAL_CAMERA
#define DIK_PRIOR           0xC9    /* PgUp on arrow keypad */
#define DIK_NEXT            0xD1    /* PgDn on arrow keypad */
#define DIK_HOME            0xC7    /* Home on arrow keypad */
#define DIK_END             0xCF    /* End on arrow keypad */
#define DIK_INSERT          0xD2    /* Insert on arrow keypad */
#define DIK_DELETE          0xD3    /* Delete on arrow keypad */
#endif

	sint16		*cine;
	sint16		num_cine_frames;
	sint16		cine_frame;
	sint16		cinematic_level;
	sint16		cine_loaded;
	PHD_3DPOS	cinematic_pos;
	int		    cutscene_track=1;
	int		    cutscene_play=-1;
	sint16		orient;

#ifdef MANUAL_CAMERA
	PHD_3DPOS	mycam;
	sint32		mycam_distance=1024;
	int			useCineCam=1;
#endif

void	InitCinematicRooms( void );
int		GetCinematicRoom(sint32 x, sint32 y, sint32 z);

#define CINE_ADJUST 6
static uint32	actual_current_frame=0;
int			cinematic_animation_rate = 32768;

#ifdef PSX_VERSION
	static uint32	actual_start_frame=0;
/*
#if 1
#define CINE_ADJUST 6
#else
	#ifdef NTSC
		#define CINE_ADJUST	-8
	#else
		#define CINE_ADJUST	-8
		//	int			cinematic_animation_rate = (32768*60)/50;
	#endif
#endif
*/
#endif

#ifdef GAMEDEBUG

void SetCutscene( int cut )
{
	cutscene_play = cut-1;
	cutscene_track = cut+1;
}

void SetCutsceneAngle( int angle )
{
	GF_Cutscene_Orientation =(uint16) angle*0x4000;
}

#endif


void SetCutsceneTrack( int track )
{
	cutscene_track = track;
}


/******************************************************************************
 *					Start A Cinematic Cut-Scene
 *****************************************************************************/
sint32	StartCinematic( int level_number )
{
	sint32 nframes, cine_over;//, doit;
	int		saved_sound_active;
#ifdef PSX_VERSION
	int		saved_music_vol;
	static	int past_frames[4]={2,2,2,2};
	static	int thisframe=0;
	int		gone,togo;
#endif
#ifdef GAMEDEBUG
//	int	i;
	int nPaused,nPausedDebounce,nAdvanceDebounce;
	nPaused=nPausedDebounce=nAdvanceDebounce=0;
#endif

	title_loaded = 0;
	cinematic_level = level_number;
#ifdef PC_VERSION
	S_FadeToBlack();
#endif
	if (!InitialiseLevel( level_number, IL_CUTSCENE ))
		return(END_ACTION);
	InitCinematicRooms();

#ifdef PSX_VERSION
	saved_music_vol = Option_Music_Volume;			// Use SoundFX Volume for Cutscenes
	Option_Music_Volume = Option_SoundFX_Volume;
    CDDA_SetMasterVolume(200);                             // Set Volume of CD on PlayStation
#endif


	InitialisePlayer1(lara.item_number);

	// Start Audio!!!
	camera.target_angle = GF_Cutscene_Orientation;
	saved_sound_active = sound_active;
	sound_active = 0;
	cine_frame = 0;

	S_ClearScreen();

#ifdef PC_VERSION
	if (!S_StartSyncedAudio( cutscene_track ))
		return 1;
	S_CDVolume(255); // set volume of CD to max for cutscene
#else
	S_StartSyncedAudio( cutscene_track );
#endif

#ifdef MANUAL_CAMERA
	useCineCam = 1;
#endif
#ifdef PSX_VERSION
	fades_enabled = 0;

//	S_Wait(16);
//	GPU_ResetLastFrameCount();
//	actual_start_frame = GPU_GetFrameCount();

//	DoCinematic(2);
	actual_current_frame = 0;
#else
	//DoCinematic(2);
	//S_DumpScreen();
	actual_current_frame = 0;
#endif
	do
	{
		DrawPhaseCinematic();

		nframes = actual_current_frame-(cine_frame*2)+CINE_ADJUST+2;
		if (nframes<2) nframes = 2;	// FIX FOR PSX
#ifdef GAMEDEBUG
		sprintf(exit_message,"nframes: %d %d", nframes,(nframes<2)?2:nframes);
//		PrintDbug(4,7,exit_message);
#endif

		cine_over = DoCinematic(nframes);
	}
	while (!cine_over);

#ifdef PSX_VERSION
//	printf("Cine frames played = %d\n", cine_frame);
//	printf("Actual frames taken = %d\n", actual_current_frame);
	fades_enabled = 1;

	DrawSync(0);								// added by del... to fix gpu timeout..

	Option_Music_Volume = saved_music_vol;
    CDDA_SetMasterVolume(200);                             // Set Volume of CD on PlayStation
#else
	// reset CD audio to users chosen in game level
	if (Option_Music_Volume)
		S_CDVolume(Option_Music_Volume*25 + 5);
	else
		S_CDVolume(0);
#endif
	S_CDStop();

	sound_active = saved_sound_active;
	S_SoundStopAllSamples();                    // Stop any remaining samples
	level_complete = 1;							// Indicate weve finished with this level

	return( cine_over );
}

/****************************************************************************
 *				Initialise rooms for drawing Cutscene
 ***************************************************************************/
void	InitCinematicRooms( void )
{
	int		i;

	for ( i=0;i<number_rooms;i++ )
	{
		if ( room[i].flipped_room>=0 )
			room[ room[i].flipped_room ].bound_active = 1;
#ifdef PC_VERSION
		room[i].flags|=OUTSIDE;	// will this frig work maybe?
#endif
	}
	number_draw_rooms = 0;
	for ( i=0;i<number_rooms;i++ )
	{
		if ( !room[i].bound_active )
		{
			draw_rooms[number_draw_rooms++] = i;
		}
	}
}

/****************************************************************************
 *					Play Cinematic CutScene
 ***************************************************************************/
sint32	DoCinematic(sint32 nframes)
{
	sint16 		item_num,nex;
	static int	framecount = 0;

	framecount += nframes * cinematic_animation_rate;
	for ( ; framecount>=0; framecount-=65536 )
	{
#ifdef PC_VERSION
		if (S_UpdateInput())
			return 3;					// do an EXITGAME because the window has been closed!
#else
		S_UpdateInput();
#endif

		if ( input&IN_ACTION)			// hit action to leave cinematic sequence on psx...
			return (1);
#ifdef PSX_VERSION
		if ( input&IN_PAUSE )			// hit start to leave story so far sequence on psx...
			return (2);
#else
		if ( input&IN_OPTION)			// hit esc to leave story so far sequence on psx...
			return (2);
#endif
		number_dynamics = 0;

		/* Call control routines for active items on linked list (can create/destroy items) */
		item_num = next_item_active;
		while ( item_num!=NO_ITEM )
		{
			nex = items[item_num].next_active;
			if ( objects[items[item_num].object_number].control )
				(*objects[items[item_num].object_number].control)(item_num);
			item_num = nex;
		}

		/* Call control routines for active effects on linked list (can create/destroy items) */
		item_num = next_fx_active;
		while ( item_num!=NO_ITEM )
		{
			nex = effects[item_num].next_active;
			if ( objects[ effects[item_num].object_number ].control )
				(*objects[ effects[item_num].object_number ].control)(item_num);
			item_num = nex;
		}

		HairControl(1);

#ifdef MANUAL_CAMERA
		if (useCineCam)
#endif
		CalculateCinematicCamera();
		cine_frame++;
		if ( cine_frame>=num_cine_frames )			// Return True If Finished Playing Cinematics
			return(1);
	}

	actual_current_frame=(S_CDGetLoc()*4)/5; // Get 75Hz CDLoc in current track

#ifdef PSX_VERSION
	// Always 60Hz whatever version !!!
#if defined(GAMEDEBUG) || defined(SHOW_ROOM_INFO)
		sprintf(exit_message,"Cine %d Actual %d Dif %d",cine_frame*2, actual_current_frame, (cine_frame*2)-actual_current_frame-CINE_ADJUST);
		PrintDbug(4,2,exit_message);
#endif
#endif

	return(0);
}

/*****************************************************************************
 *				Get Camera Position for Cinematics
 ****************************************************************************/
void	CalculateCinematicCamera( void )
{
	sint32		tx,ty,tz;
	sint32		cx,cy,cz;
	sint32		c,s;
	sint16		roll,fov;
	sint16		*ptr;
	PHD_VECTOR	campos,camtar;
//	PHD_VECTOR pos;
	int	room_number;
#ifdef MANUAL_CAMERA
	int			x,y,z;
#endif

	ptr = cine + (int)(8*cine_frame);
	tx = (sint32)*(ptr+0);
	ty = (sint32)*(ptr+1);
	tz = (sint32)*(ptr+2);
	cx = (sint32)*(ptr+3);
	cy = (sint32)*(ptr+4);
	cz = (sint32)*(ptr+5);
	fov = *(ptr+6);
	roll = *(ptr+7);

	c = phd_cos( camera.target_angle );									// Rotate Scene
	s = phd_sin( camera.target_angle );                                 // into backGround
//	camtar.x = camera.pos.x + ((tx*c + tz*s)>>W2V_SHIFT);
//	camtar.y = camera.pos.y + ty;
//	camtar.z = camera.pos.z + ((tz*c - tx*s)>>W2V_SHIFT);
//	campos.x = camera.pos.x + ((cx*c + cz*s)>>W2V_SHIFT);
//	campos.y = camera.pos.y + cy;
//	campos.z = camera.pos.z + ((cz*c - cx*s)>>W2V_SHIFT);

	camtar.x = lara_item->pos.x_pos + ((tx*c + tz*s)>>W2V_SHIFT);
	camtar.y = lara_item->pos.y_pos + ty;
	camtar.z = lara_item->pos.z_pos + ((tz*c - tx*s)>>W2V_SHIFT);
	campos.x = lara_item->pos.x_pos + ((cx*c + cz*s)>>W2V_SHIFT);
	campos.y = lara_item->pos.y_pos + cy;
	campos.z = lara_item->pos.z_pos + ((cz*c - cx*s)>>W2V_SHIFT);

	if ((room_number = GetCinematicRoom( campos.x, campos.y, campos.z ))>=0)
		camera.pos.room_number = room_number;

#ifdef MANUAL_CAMERA
	mycam.x_pos = campos.x;
	mycam.y_pos = campos.y;
	mycam.z_pos = campos.z;
	mycam.z_rot = roll;
	x = campos.x-camtar.x;
	y = campos.y-camtar.y;
	z = campos.z-camtar.z;
	mycam_distance = phd_sqrt(x*x + z*z);
	mycam.y_rot = phd_atan(z,x);
	mycam.x_rot = phd_atan(mycam_distance,y);
#endif
	AlterFOV( fov );
	phd_LookAt( campos.x, campos.y, campos.z, camtar.x, camtar.y, camtar.z, roll );

}

/*****************************************************************************
 *			Simple Cinematic Object Control Routine
 ****************************************************************************/
int	original_room=0;

int	GetCinematicRoom(sint32 x, sint32 y, sint32 z)
{
	int		i;


	for ( i=0;i<number_rooms;i++ )
	{

		if ( (x>=(room[i].x+WALL_L) && x<room[i].x+(room[i].y_size-1)*WALL_L) &&
			 (y>=room[i].maxceiling && y<=room[i].minfloor) &&
			 (z>=(room[i].z+WALL_L) && z<room[i].z+(room[i].x_size-1)*WALL_L) )
		{
			return(i);
		}
	}
	return(-1); // room not found
}


void	ControlCinematicPlayer( sint16 item_num )
{
	ITEM_INFO	*item;
	PHD_VECTOR pos;
	sint16	room_number;
//	sint16 *bounds;
//	int		x,y,z;

//	CRASH_TELL( __FILE__, __LINE__ );

	item = &items[item_num];
	item->pos.y_rot = camera.target_angle;		// Always Align a cut scene
	item->pos.x_pos = camera.pos.x;             // player with the Camera
	item->pos.y_pos = camera.pos.y;             // and align how far we are in animation
	item->pos.z_pos = camera.pos.z;             // with current cine_frame...

//	room_number = item->room_number;

#if 1
	// Get true pos using first object's pivot point ...?
	pos.x = 0;
	pos.y = 0;
	pos.z = 0;
	GetJointAbsPosition(item,&pos,0);
	room_number = GetCinematicRoom( pos.x, pos.y, pos.z );
#else
	bounds = GetBoundsAccurate( item );
	x = (bounds[0]+bounds[1]) >>1;
	y = (bounds[2]+bounds[3]) >>1;
	z = (bounds[4]+bounds[5]) >>1;
	switch (GF_Cutscene_Orientation)
	{
		case 0:
			pos.x = lara_item->pos.x_pos + x;
			pos.y = lara_item->pos.y_pos + y;
			pos.z = lara_item->pos.z_pos + z;
			break;
		case 16384:
			pos.x = lara_item->pos.x_pos + z;
			pos.y = lara_item->pos.y_pos + y;
			pos.z = lara_item->pos.z_pos - x;
			break;
		case 32768:
			pos.x = lara_item->pos.x_pos - x;
			pos.y = lara_item->pos.y_pos + y;
			pos.z = lara_item->pos.z_pos - z;
			break;
		case 49152:
			pos.x = lara_item->pos.x_pos - z;
			pos.y = lara_item->pos.y_pos + y;
			pos.z = lara_item->pos.z_pos + x;
			break;
	}
	room_number = GetCinematicRoom( pos.x, pos.y, pos.z );
#endif

//	GetFloor(item->pos.x_pos, item->pos.y_pos, item->pos.z_pos, &room_number);
	if (room_number!=-1 && item->room_number!=room_number)
		ItemNewRoom(item_num,room_number);

	if ( item->dynamic_light && item->status!=INVISIBLE )
	{
		pos.x = 0;
		pos.y = 0;
		pos.z = 0;
		GetJointAbsPosition(item,&pos,0);
		AddDynamicLight(pos.x, pos.y, pos.z, 12, 11 );
	}
	AnimateItem( item );
}

void	LaraControlCinematic( sint16 item_num )
{
//	SG_COL black_col = S_GOURAUD_COL( 0,0,0,128 );
	ITEM_INFO	*item;
	PHD_VECTOR pos;
	sint16	room_number;
//	char	str[64];


	item = &items[item_num];
	item->pos.y_rot = camera.target_angle;		// Always Align a cut scene
	item->pos.x_pos = camera.pos.x;             // player with the Camera
	item->pos.y_pos = camera.pos.y;             // and align how far we are in animation
	item->pos.z_pos = camera.pos.z;             // with current cine_frame...

#if 1
	// Get true pos using first object's pivot point ...?
	pos.x = 0;
	pos.y = 0;
	pos.z = 0;
	GetJointAbsPosition(item,&pos,0);
	room_number = GetCinematicRoom( pos.x, pos.y, pos.z );
#else
	bounds = GetBoundsAccurate( lara_item );
	x = (bounds[0]+bounds[1]) >>1;
	y = (bounds[2]+bounds[3]) >>1;
	z = (bounds[4]+bounds[5]) >>1;
	switch (GF_Cutscene_Orientation)
	{
		case 0:
			pos.x = lara_item->pos.x_pos + x;
			pos.y = lara_item->pos.y_pos + y;
			pos.z = lara_item->pos.z_pos + z;
			break;
		case 16384:
			pos.x = lara_item->pos.x_pos + z;
			pos.y = lara_item->pos.y_pos + y;
			pos.z = lara_item->pos.z_pos - x;
			break;
		case 32768:
			pos.x = lara_item->pos.x_pos - x;
			pos.y = lara_item->pos.y_pos + y;
			pos.z = lara_item->pos.z_pos - z;
			break;
		case 49152:
			pos.x = lara_item->pos.x_pos - z;
			pos.y = lara_item->pos.y_pos + y;
			pos.z = lara_item->pos.z_pos + x;
			break;
	}
	room_number = GetCinematicRoom( pos.x, pos.y, pos.z );
#endif
/*	sprintf(str,"Origin: %d %d %d", item->pos.x_pos, item->pos.y_pos, item->pos.z_pos);
	PrintDbug(10,5,str);
	sprintf(str,"Bounds: %d %d %d", pos.x,pos.y,pos.z);
	PrintDbug(10,6,str);
	sprintf(str,"Camera: %d %d %d Room: %d", mycam.x_pos,mycam.y_pos,mycam.z_pos, camera.pos.room_number);
	PrintDbug(10,7,str);
*/

	if (room_number!=-1 && item->room_number !=room_number)
		ItemNewRoom(item_num,room_number);

	AnimateLara( item );
}


/******************************************************************************
 *				Initialise First CutScene Player (camera is aligned )
 *****************************************************************************/
void	InitialisePlayer1( sint16 item_num )
{
	ITEM_INFO 	*item;
//	sint16		*temp;
//	int i;
	OBJECT_INFO *obj;

	obj = &objects[LARA];
	obj->draw_routine = DrawLara; //DrawDummyItem;  // Lara is drawn seperately!!!
	obj->control = LaraControlCinematic;

	AddActiveItem( item_num );
	item = &items[item_num];
	camera.pos.x = item->pos.x_pos;	// initialise what Camera is relative to...
	camera.pos.y = item->pos.y_pos;
	camera.pos.z = item->pos.z_pos;
	camera.target_angle = item->pos.y_rot = 0;
	original_room = camera.pos.room_number = item->room_number;

	item->goal_anim_state = 0;
	item->current_anim_state = 0;
	item->frame_number = 0;
	item->anim_number = 0;
	item->dynamic_light = 0;
	lara.hit_direction = -1;
//	printf("Lara Item %d: room:%d\n", item_num,items[item_num].room_number);

/*	if ( cinematic_level==CUTSCENE2 || cinematic_level==CUTSCENE4 )	// Lara is Player One so make her Start with guns in Holster
	{
		temp = meshes[objects[PLAYER_1].mesh_index + THIGH_L];
		meshes[objects[PLAYER_1].mesh_index + THIGH_L] = meshes[objects[PISTOLS].mesh_index + THIGH_L];
		meshes[objects[PISTOLS].mesh_index + THIGH_L] = temp;
		temp = meshes[objects[PLAYER_1].mesh_index + THIGH_R];
		meshes[objects[PLAYER_1].mesh_index + THIGH_R] = meshes[objects[PISTOLS].mesh_index + THIGH_R];
		meshes[objects[PISTOLS].mesh_index + THIGH_R] = temp;
	}
*/
}


/******************************************************************************
 *				Initialise General player
 *****************************************************************************/
void	InitialiseGenPlayer( sint16 item_num )
{
//	Log(LT_Info,"General Player Initialised!!! Item=%d", item_num);
	AddActiveItem( item_num );
	items[item_num].pos.y_rot = 0;
	items[item_num].dynamic_light = 0;
//	printf("Item %d: room:%d\n", item_num,items[item_num].room_number);
}


/******************************************************************************
 ******************************************************************************
 *	Pre-Calculated Camera Movement and Action for In-Game stuff
 ******************************************************************************
 *****************************************************************************/
void	InGameCinematicCamera( void )
{
	sint32		c,s;
	sint32		tx,ty,tz;
	sint32		cx,cy,cz;
	sint16		roll,fov;
	sint16		*ptr;

	cine_frame++;
	if ( cine_frame>=num_cine_frames )
		cine_frame = num_cine_frames-1;

	ptr = cine + (int)(8*cine_frame);
	tx = (sint32)*(ptr+0);
	ty = (sint32)*(ptr+1);
	tz = (sint32)*(ptr+2);
	cx = (sint32)*(ptr+3);
	cy = (sint32)*(ptr+4);
	cz = (sint32)*(ptr+5);
	fov = *(ptr+6);
	roll = *(ptr+7);

	c = phd_cos( cinematic_pos.y_rot );
	s = phd_sin( cinematic_pos.y_rot );
	camera.target.x = cinematic_pos.x_pos + ((tx*c + tz*s)>>W2V_SHIFT);
	camera.target.y = cinematic_pos.y_pos + ty;
	camera.target.z = cinematic_pos.z_pos + ((tz*c - tx*s)>>W2V_SHIFT);
	camera.pos.x = cinematic_pos.x_pos + ((cx*c + cz*s)>>W2V_SHIFT);
	camera.pos.y = cinematic_pos.y_pos + cy;
	camera.pos.z = cinematic_pos.z_pos + ((cz*c - cx*s)>>W2V_SHIFT);

	AlterFOV( fov );
	phd_LookAt( camera.pos.x, camera.pos.y, camera.pos.z, camera.target.x, camera.target.y, camera.target.z, roll );
	GetFloor(camera.pos.x, camera.pos.y, camera.pos.z, &camera.pos.room_number);		// Update Cameras Room

#ifdef PC_VERSION
	if (camera.mike_at_lara)
	{
		camera.actual_angle=lara_item->pos.y_rot+lara.head_y_rot+lara.torso_y_rot;
		camera.mike_pos.x=lara_item->pos.x_pos;
		camera.mike_pos.y=lara_item->pos.y_pos;
		camera.mike_pos.z=lara_item->pos.z_pos;
	}
	else
#endif
	{
		camera.actual_angle = phd_atan(camera.target.z - camera.pos.z, camera.target.x - camera.pos.x);
		camera.mike_pos.x=camera.pos.x+((phd_persp*phd_sin(camera.actual_angle))>>W2V_SHIFT);
		camera.mike_pos.z=camera.pos.z+((phd_persp*phd_cos(camera.actual_angle))>>W2V_SHIFT);
		camera.mike_pos.y=camera.pos.y;
	}
}


