/******************************************************************************************
	CRTEST PS   (c) 1999-2001 ISL

	viewer.c:			PSI Model-Viewer
******************************************************************************************/

//#define LIGHTING_TEST

#include "global.h"

#include "main.h"	// for "psfont", "frame", and "mycamera"
#include "menu.h"
#include "viewer.h"
#include "map_draw.h"
// #include <convert.h>	// psx libs, for "atoi" now in global.h

//extern void customPsiDrawSortedPrimitives(int depth);
//extern void customPsiDrawSegments(PSIDATA *psiData);
//extern void customPsiFixupPrims(PSIMODEL *psiModel);

PSIMODEL	*viewer_model = 0;
ACTOR		*viewer_actor = 0;
TextureBankType	*viewer_textures = 0;

int viewer_onscnhelp = 1;
int viewer_model_loaded = 0;
char **viewer_model_fnames = NULL;
char **viewer_model_idnames = NULL;
int *viewer_scales = NULL;
int *viewer_speeds = NULL;
int *viewer_flags = NULL;
char *viewer_name_file = NULL;
int viewer_n_models = 0;

int viewer_camera_ya = 512*3;
//int viewer_camera_xa = 512;
int viewer_camera_xa = 256;
int viewer_camera_range = 3500;

int viewer_lite_xa = 0;
int viewer_lite_ya = 0;

int viewer_n_anims = 0;
int viewer_current_anim = 0;

int viewer_light_id = 0;
int viewer_ambient_id = 0;

PSIOBJECT *viewer_head;
PSIOBJECT *viewer_upperbod;	// Best one to use for the throwing blend?
int viewer_throw_counter = 0;

extern void viewerDraw();
extern void viewerCycle();

unsigned int viewer_lite_bgr = 0x808080;

int viewer_oneshotting = 0;

void viewerSetAnim(int anim_no, int blend)
{
	if(anim_no >= viewer_model->animSegments)
		anim_no = 0;
	if(anim_no < 0)
		anim_no = viewer_model->animSegments-1;
	if(anim_no < 0)
		anim_no = 0;	// models with 0 anim-segments

	actorAnimate(viewer_actor, anim_no, 1, 0, viewer_speeds[viewer_model_loaded], 1, blend);	// loop, queue, speed, skiplast
	viewer_current_anim = anim_no;
	viewer_oneshotting = 0;

	debugPrintf("switched to anim %d\n",anim_no);

}

void viewerReleaseModel()
{
	DB("Destroying old model\n");
	psiDestroy();
	textureUnloadBank(viewer_textures);
	actorFree(viewer_actor);
	viewer_model = NULL;
}

void viewerLoadModel(int model)
{
	char temp[40];

	if (model < 0)
		model = viewer_n_models-1;
	if (model >= viewer_n_models)
		model = 0;

	if(viewer_model)
	{
		viewerReleaseModel();
	}

	psiInitialise(10);
	actorInitialise();
//	psiRegisterDrawFunction(customPsiDrawSortedPrimitives);

//	psiInitSortList(4000);	// truck-testing purposes only


	sprintf(temp,"VIEWER\\%s.SPT",viewer_model_fnames[model]);
	viewer_textures = textureLoadBank(temp);
	textureDownloadBank(viewer_textures);
	textureDestroyBank(viewer_textures);

	sprintf(temp,"VIEWER\\%s.PSI",viewer_model_fnames[model]);
	viewer_model = psiLoad(temp);
//	customPsiFixupPrims(viewer_model);

	viewer_actor = actorCreate(viewer_model);

	viewer_actor->position.vx = 0;
	viewer_actor->position.vy = 0;
	viewer_actor->position.vz = 0;

// initialse model and actor
	actorInitialise();

	viewer_actor->size.vx = viewer_actor->size.vy = viewer_actor->size.vz = viewer_scales[model];

	viewer_camera_range = viewer_actor->radius * 4;


	viewer_head = psiObjectScan(viewer_actor->psiData.object, "HEAD"); // MUST be upper case, even if it isn't in the model
	if(!viewer_head)
		DB("HEAD BONE NOT FOUND\n");
	viewer_upperbod =  psiObjectScan(viewer_actor->psiData.object, "GING_BODY_UPPER");
	if(!viewer_upperbod)
		DB("UPPER BODY BONE NOT FOUND\n");

	viewer_throw_counter = 0;

// set a directional light from the left
	psiSetLight(0, 255, 255, 255,4096, 0, 0);

// DirectionOnly whites out the model's own gouraud, so you're lighting a white model

//	PSImodelctrl.lighting = DIRECTIONONLY;

//	NOLIGHTING,
//	DIRECTIONAL,
//	DIRECTIONONLY,
//	COLOURIZE,
//	AMBIENT,

//	PSImodelctrl.lighting = DIRECTIONAL;
	if(viewer_flags[model] & 1)
	{
		PSImodelctrl.lighting = NOLIGHTING;
	}
	else
	{
		PSImodelctrl.lighting = DIRECTIONONLY;
	}

	psiAllocWorkspace();

	viewer_model_loaded = model;
	DB("loaded model %d ok\n",viewer_model_loaded);

	DB("n anims = %d\n",viewer_model->animSegments);
	viewerSetAnim(0,0);

	PSImodelctrl.depthShift = 3;
	PSImodelctrl.farclip = 32768;

}


void viewerParseFile()
{
	int len;
	int i;
	int n;

#if PREVIEW_VERSION==0
	viewer_name_file = fileLoad("VIEWER\\VIEWER.TXT",&len);
#else
	viewer_name_file = fileLoad("VIEWER\\VIEWERM.TXT",&len);
#endif

	if(viewer_name_file[len-2] != 13)
	{
		viewer_name_file[len-2] = 13;	// just make sure there's a (13,10) at the end
	}

	n = 0;
	for(i = 0; i < len; i++)
	{
		if(viewer_name_file[i] == 13)	// (13,10)
		{
			viewer_name_file[i] = 0;
			n++;
		}
	}

	n++;	// in case of file-end newline
	viewer_model_fnames = MALLOC(n * 4);
	viewer_model_idnames = MALLOC(n * 4);
	viewer_scales = MALLOC(n * 4);
	viewer_speeds = MALLOC(n * 4);
	viewer_flags = MALLOC(n * 4);

	for(i = 0; i < n; i++)
	{
		viewer_scales[i] = 4096;
		viewer_speeds[i] = (1<<ANIMSHIFT);
		viewer_flags[i] = 0;
	}

	n = 0;
	viewer_model_fnames[0] = viewer_name_file;
	viewer_model_idnames[0] = viewer_name_file;

	for(i = 0; i < len; i++)
	{
		if(viewer_name_file[i] == 0)
		{
			if(strlen(viewer_model_fnames[n]))
				n++;
			viewer_model_fnames[n] = &viewer_name_file[i+2];
		}

		if(viewer_name_file[i] == 9)
		{
			switch(viewer_name_file[i+1])
			{
				case 's':
					viewer_scales[n] = atoi(&viewer_name_file[i+2]);
					break;
				case 'a':
					viewer_speeds[n] = atoi(&viewer_name_file[i+2]);
					break;
				case 'f':
					viewer_flags[n] = atoi(&viewer_name_file[i+2]);
					break;
				case 'm':
					viewer_model_idnames[n] = &viewer_name_file[i+2];
					break;
			}
			viewer_name_file[i] = 0;
		}
	}
	viewer_n_models = n;
	DB("%d models\n",n);
}

void viewerFreeFile()
{
	FREE(viewer_model_fnames);
	FREE(viewer_model_idnames);
	FREE(viewer_name_file);
	FREE(viewer_scales);
	FREE(viewer_speeds);
	FREE(viewer_flags);
}




void viewerStart(int model_no)
{

	viewer_model = NULL;
	viewerParseFile();	

	viewerLoadModel(model_no);
	draw_routine = viewerDraw;
	cycle_routine = viewerCycle;

}

void viewerEnd()
{
	if(viewer_model)
	{
		viewerReleaseModel();
	}

	viewerFreeFile();
	viewer_name_file = NULL;
	menuStart();
}

//#define VIEWER_PANEL_X 24
//#define VIEWER_PANEL_Y 48

#define VIEWER_PANEL_X -180
#if PALMODE==YES
#define VIEWER_PANEL_Y -150
#else
#define VIEWER_PANEL_Y -130
#endif

#define THROW_BLEND_IN_FRAMES	3
#define THROW_ACTION_FRAMES		6
#define THROW_BLEND_OUT_FRAMES	6

//	ANIM_ANIMATION *pAnimation = &pActor->animation;
// 		ANIM_SEGMENT *pAS = pAnimation->pSegments + pAD->segment;

// ->	int scaledStartFrame;
// ->	int scaledAnimLength;
// ->	int actualStartFrame;
// ->	int actualAnimLength;

// Need to add a "throw" bit to ANIM_ANIMATION
// Containing:- length of the throw animation.
// possibly a copy of the start & end frames
// a PSIOBJECT pointer to the body
// and the current counter

void viewerThrowBlend()
{
	int main_frame;
	int throw_frame;
	int blendval;

	main_frame = viewer_actor->animation.animData[0].actualFrame;

	if(viewer_throw_counter < THROW_BLEND_IN_FRAMES)
	{
		blendval = 4096 * viewer_throw_counter / THROW_BLEND_IN_FRAMES;
		throw_frame = 1500;
	}
	else if(viewer_throw_counter < THROW_BLEND_IN_FRAMES + THROW_ACTION_FRAMES)
	{
		blendval = 4096;
		throw_frame = 1500 + (viewer_throw_counter-THROW_BLEND_IN_FRAMES);
	}
	else
	{
		blendval = 4096 * (THROW_BLEND_IN_FRAMES + THROW_ACTION_FRAMES + THROW_BLEND_OUT_FRAMES - viewer_throw_counter) / THROW_BLEND_OUT_FRAMES;
		throw_frame = 1505;
	}

	DB("Throw = %3d. blend=%4d. Frm=%4d\n",viewer_throw_counter,blendval,throw_frame);

	psiSetKeyFrames3(viewer_upperbod, 
			         main_frame, 
					 throw_frame, 
					 blendval);

}

void viewerDraw()
{
	char str[20];



	if(viewer_actor->animation.numSegments)
	{
		actorSetAnimation(viewer_actor, viewer_actor->animation.animData->actualFrame);

		if(viewer_throw_counter && viewer_upperbod)
			viewerThrowBlend();
	}
	else
	{
		actorSetAnimation(viewer_actor, 0);
	}

	actorMove(viewer_actor);
	actorDraw(viewer_actor);

//#if PREVIEW_VERSION==0
	if(!viewer_model->animSegments)
	{
		sprintf(str,"No Anims\n");
	}
	else
	{
		sprintf(str,"Anim %d/%d\n",(int)(viewer_current_anim+1),(int)viewer_model->animSegments);
	}
//	fontPrint(font, VIEWER_PANEL_X+30,VIEWER_PANEL_Y+20, str, 128,128,64);
	fontPrint(font, VIEWER_PANEL_X-60,VIEWER_PANEL_Y+20, str, 128,128,64);
//#endif


#if PREVIEW_VERSION==1
	overlayPreviewOverlay();
#endif


	if(viewer_onscnhelp)
	{
		fontPrint(font, VIEWER_PANEL_X-60,VIEWER_PANEL_Y+30, "Left Analog Stick = Rotate", 64,128,128);
		fontPrint(font, VIEWER_PANEL_X-60,VIEWER_PANEL_Y+42, "Right Analog Stick = Zoom", 54,118,118);
		fontPrint(font, VIEWER_PANEL_X-60,VIEWER_PANEL_Y+54, "R1,R2 = Anim Select", 44,108,108);
		fontPrint(font, VIEWER_PANEL_X-60,VIEWER_PANEL_Y+66, "Directional Button= Up/Down", 34,98,98);
		fontPrint(font, VIEWER_PANEL_X-60,VIEWER_PANEL_Y+78, "One-Shot non-looping = Cross", 24,88,88);
		fontPrint(font, VIEWER_PANEL_X-60,VIEWER_PANEL_Y+90, "Slomo = Hold Square", 24,88,88);
		fontPrint(font, VIEWER_PANEL_X-60,VIEWER_PANEL_Y+102, "Get rid of this Panel = Select", 24,88,88);
	}


/*
	fontPrint(font, VIEWER_PANEL_X,VIEWER_PANEL_Y, viewer_model_idnames[viewer_model_loaded], 128,128,128);

// Greyed out next/prev names, sort underneath the current name
	fontPrint(font, VIEWER_PANEL_X+2,VIEWER_PANEL_Y-7, viewer_model_idnames[(viewer_model_loaded+viewer_n_models-1)%viewer_n_models], 32,32,32);
	fontPrint(font, VIEWER_PANEL_X+2,VIEWER_PANEL_Y+7, viewer_model_idnames[(viewer_model_loaded+1)%viewer_n_models], 32,32,32);
*/
#ifdef LIGHTING_TEST
	{
		char *lightnames[5] = {"None","Directional","Dirn-only","Colourize","Ambient"};
		char *lighttypes[5] = {"L=255 white","L=128 white","L=255 red","L=255 yellow","L=255 cyn"};
		char *ambtypes[6] = {"A=512 white","A=1024 white","A=2048 white","A=1024 red","A=1024 yellow","A=1024 cyn"};
		fontPrint(font, VIEWER_PANEL_X+2,VIEWER_PANEL_Y+30, lightnames[PSImodelctrl.lighting], 64,128,128);
		fontPrint(font, VIEWER_PANEL_X+2,VIEWER_PANEL_Y+40, lighttypes[viewer_light_id], 64,128,128);
		fontPrint(font, VIEWER_PANEL_X+2,VIEWER_PANEL_Y+50, ambtypes[viewer_ambient_id], 64,128,128);
	}
#endif

//	overlayScreenAdd(64,64,64,SEMITRANS_ADD);

}

void viewerCycle()
{
	static int viewerlightmech = NOLIGHTING;
/*
enum
{
	NOLIGHTING,
	DIRECTIONAL,
	DIRECTIONONLY,
	COLOURIZE,
	AMBIENT,
};
*/

#ifdef LIGHTING_TEST
	if(padData.debounce[0] & PAD_CIRCLE)
	{
		PSImodelctrl.lighting = (PSImodelctrl.lighting + 1) % 5;
	}
	if(padData.debounce[0] & PAD_CROSS)
	{
		viewer_light_id = (viewer_light_id + 1) % 5;
	}
	if(padData.debounce[0] & PAD_SQUARE)
	{
		viewer_ambient_id = (viewer_ambient_id + 1) % 6;
	}
#endif

	switch(viewer_light_id)
	{
	case 0:
		viewer_lite_bgr = 0xffffff;
		break;
	case 1:
		viewer_lite_bgr = 0x808080;
		break;
	case 2:
		viewer_lite_bgr = 0x0000ff;
		break;
	case 3:
		viewer_lite_bgr = 0x00ffff;
		break;
	case 4:
		viewer_lite_bgr = 0xffff00;
		break;
	}
	switch(viewer_ambient_id)
	{
	case 0:
		psiSetAmbient(512,512,512);
		break;
	case 1:
		psiSetAmbient(1024,1024,1024);
		break;
	case 2:
		psiSetAmbient(2048,2048,2048);
		break;
	case 3:
		psiSetAmbient(1024,0,0);
		break;
	case 4:
		psiSetAmbient(1024,1024,0);
		break;
	case 5:
		psiSetAmbient(0,1024,1024);
		break;
	}

//void psiSetAmbient(int r, int g, int b);




/*
	if(padData.debounce[0] & PAD_L1)
	{
		viewerLoadModel(viewer_model_loaded-1);
	}
	else if(padData.debounce[0] & PAD_L2)
	{
		viewerLoadModel(viewer_model_loaded+1);
	}
*/

//#if PREVIEW_VERSION==0
	if(padData.debounce[0] & PAD_R1)
	{
		viewerSetAnim(viewer_current_anim+1,256);
	}
	else if(padData.debounce[0] & PAD_R2)
	{
		viewerSetAnim(viewer_current_anim-1,256);
	}
//#endif

	if(padData.debounce[0] & PAD_CIRCLE)
	{
		viewer_throw_counter = 1;
	}

	if(padData.debounce[0] & (PAD_CROSS))
	{
		viewer_oneshotting = 1;
//int actorAnimate(ACTOR *actor, int animNum, char loop, char queue, int speed, char skipendframe, int blend);
		actorAnimate(viewer_actor, viewer_current_anim, 0, 0,     0     , 1, 0);
	}
	else
	{
		if(viewer_oneshotting == 1)
		{
			if(! (padData.digital[0] & (PAD_CROSS)))
			{
				viewer_oneshotting = 2;
				actorAnimate(viewer_actor, viewer_current_anim, 0, 0, viewer_speeds[viewer_model_loaded], 0, 0);
			}
		}
	}

	if(viewer_oneshotting != 1)
	{
		if(padData.digital[0] & (PAD_SQUARE))
		{
			viewer_actor->animation.animData[0].speed = viewer_speeds[viewer_model_loaded] / 4;
		}
		else
		{
			viewer_actor->animation.animData[0].speed = viewer_speeds[viewer_model_loaded];
		}
	}



// Note that any "actoranimate"'s need to before the "updateanimations" (!)
	actorUpdateAnimations(viewer_actor);

/* Titan's "ActorMove"
	// Blend
	if (actor->animControl.blending)
	{
		actorSetAnimation2(actor, 
			               0, 
						   actor->animation[actor->animControl.fromAnim].frame, 
						   actor->animation[actor->animControl.toAnim].frame, 
						   actor->animControl.blendVal);
	}
	else
	{
		actorSetAnimation(actor, 0, actor->animation[0].frame);
	}

	// Modify 
	if (actor->animControl.modifying)
	{
		psiSetKeyFrames3(actor->animControl.modifyRoot, 
			             actor->animation[0].frame, 
						 actor->animation[2].frame, 
						 actor->animControl.modifyVal);
	}
*/

	if(viewer_throw_counter && viewer_upperbod)
	{

		viewer_throw_counter++;
		if(viewer_throw_counter >= THROW_BLEND_IN_FRAMES + THROW_ACTION_FRAMES + THROW_BLEND_OUT_FRAMES)
		{
			viewer_throw_counter = 0;
		}
	}

// Which essentially does
//	if(pAnimation->blendSpeed)
//		psiSetKeyFrames2(pActor->psiData.object, pAnimation->animData[1].actualFrame,
//							pAnimation->animData[0].actualFrame, pAnimation->blendCounter);
// else
//		psiSetKeyFrames(pActor->psiData.object, pAnimation->animData[0].actualFrame);



	mycamera.vrx = 0; //viewer_actor->position.vx;
	mycamera.vry = 0; //viewer_actor->position.vy;
	mycamera.vrz = 0; //viewer_actor->position.vz;

	mycamera.vpx = mycamera.vrx + ((((viewer_camera_range * rsin(viewer_camera_ya))>>12) * rcos(viewer_camera_xa)) >> 12);
	mycamera.vpz = mycamera.vrz + ((((viewer_camera_range * rcos(viewer_camera_ya))>>12) * rcos(viewer_camera_xa)) >> 12);
	mycamera.vpy = mycamera.vry - ((viewer_camera_range * rsin(viewer_camera_xa))>>12);

/*
#if PREVIEW_VERSION==1
	{
		static int ang1 = 0;
		viewer_camera_ya += 9;
		ang1 += 3;
		viewer_camera_xa = (rsin(ang1) >> 6) + 100;
	}
#endif
*/
//#if PREVIEW_VERSION==0
	viewer_camera_ya += padData.analogXS[0] >> 7;
	viewer_camera_xa += padData.analogYS[0] >> 7;
//	viewer_camera_range += padData.analogY2S[0] >> (8 - 3/10);
	viewer_camera_range += padData.analogY2S[0] >> 5;
//#endif

//		x = viewer_actor->position.vx;
	if(padData.digital[0] & PAD_UP)
		viewer_actor->position.vy -= 10;
	if(padData.digital[0] & PAD_DOWN)
		viewer_actor->position.vy += 10;
//		z = viewer_actor->position.vz;

	if(viewer_head)
	{
		viewer_head->rotate.vy = padData.analogXS[4] / 4;
		viewer_head->rotate.vx = padData.analogYS[4] / 4;
	}


	if(viewer_camera_range < 100) viewer_camera_range = 100;
//	if(viewer_camera_range > 5000) viewer_camera_range = 5000;
	if(viewer_camera_range > 30000) viewer_camera_range = 30000;
	if(viewer_camera_xa > 1000) viewer_camera_xa = 1000;
	if(viewer_camera_xa < -1000) viewer_camera_xa = -1000;

	mycamera.rz = 0;
	mycamera.super = 0;
//	GsSetRefView2L(&mycamera);
	mapCamSetRefView2L(&mycamera);



	{
		int x,y,z;
		int range = 5000;

// Don't rotate the light around any more
//		viewer_lite_xa += 0x3;
//		viewer_lite_ya += 0x20;

		viewer_lite_ya = (viewer_camera_ya + 1300) & 4095;

		x = viewer_actor->position.vx;
		y = viewer_actor->position.vy;
		z = viewer_actor->position.vz;

		x = x + ((((range * rsin(viewer_lite_ya))>>12) * rcos(viewer_lite_xa)) >> 12);
		z = z + ((((range * rcos(viewer_lite_ya))>>12) * rcos(viewer_lite_xa)) >> 12);
		y = y -   ((range * rsin(viewer_lite_xa))>>12);

		psiSetLight(0, (viewer_lite_bgr) & 0xff,(viewer_lite_bgr>>8) & 0xff,(viewer_lite_bgr>>16) & 0xff,x, y, z);
		psiSetLight(1, 0, 0, 0,0, 0, 0);
		psiSetLight(2, 0, 0, 0,0, 0, 0);
	}


	if(padData.debounce[0] & (PAD_SELECT))
		viewer_onscnhelp = !viewer_onscnhelp;


#if PREVIEW_VERSION==1
//	if(padData.debounce[0] & (PAD_START+PAD_CROSS+PAD_CIRCLE))
	if(padData.debounce[0] & PAD_START)
#else
	if(padData.debounce[0] & PAD_START)
#endif
	{
		viewerEnd();
	}
}


