/*
   CMSC 427 - Term Project Candidate
   ---------------------------------

   Beth Weinstein - bweinste@wam.umd.edu
   Ben Stewart - stewartb@wam.umd.edu
   Nawfel Tricha - Topaz@endorproductions.com

   Minimal OpenGL project using Microsoft Visual C++.
   This project is a premilinary candidate for the term 
   project required by CMSC 427.

   ABSTRACT:
      In this project, we will develop several fast 
      rendering and collision routines which would 
      require C/C++ speed and accuracy and then 
      implement a VisualJ++ interface.

   MODULE:
      Chun Li Alias module.
*/

#include "global.h"


#define CHUN_LI_MODEL "c:/DiscoStu/gfx/ChunLi/tris.md2"
#define CHUN_LI_TEXTURE "c:/DiscoStu/gfx/ChunLi/Jade.tga"
#define SPIN 0
#define STAND_UP 1
#define WALK 2
#define GET_DOWN 3
#define SPIN_FRAME_START 126
#define SPIN_FRAME_MID	  129
#define SPIN_FRAME_END   132
#define SPIN_FRAME_LENGTH SPIN_FRAME_END - SPIN_FRAME_START
#define STAND_FRAME_UP_START 133
#define STAND_FRAME_UP_MID   134
#define STAND_FRAME_UP_END   0
#define WALK_FRAME_START     40
#define WALK_FRAME_MID	   43
#define WALK_FRAME_END	   45
#define GET_DOWN_FRAME_START 0
#define GET_DOWN_FRAME_MID   123
#define GET_DOWN_FRAME_END   125

// BS: Chun Li runs, she spins, and she's good looking!
m_data_t ChunLiData =  
{
	4,
	WALK,
	{SPIN, STAND_UP, WALK, GET_DOWN}, 
    {SPIN_FRAME_START, STAND_FRAME_UP_START, WALK_FRAME_START, GET_DOWN_FRAME_START},
	{SPIN_FRAME_MID, STAND_FRAME_UP_MID, WALK_FRAME_MID, GET_DOWN_FRAME_MID},
	{SPIN_FRAME_END, STAND_FRAME_UP_END, WALK_FRAME_END, GET_DOWN_FRAME_END},
	2,
	{0, 1, 0},
	1,
	0.01,
	{0, 10, 20},
	{0, 0, 0},
	{15, 7, 50}
};

/*
	BS: Position Camera on Chun Li
*/
void ChunLiPosCamera(struct edict_s *self)
{
	static float cam_spin = 0;

	VectorSet(cam.origin, 
			  ChunLiData.camera_start[0] + ChunLiData.displacement_factor * (ChunLiData.displacement[0]),// - self->origin[0]),
			  ChunLiData.camera_start[1] + ChunLiData.displacement_factor * (ChunLiData.displacement[1]),// - self->origin[1]),
			  ChunLiData.camera_start[2] + ChunLiData.displacement_factor * (ChunLiData.displacement[2]));// - self->origin[2]));

	glLoadIdentity();
	LookAt(cam.origin[0], cam.origin[1], cam.origin[2],
		   self->origin[0], self->origin[1], self->origin[2],
		   0, 1, 0);

	glTranslatef(self->origin[0], self->origin[1], self->origin[2]);
	glRotatef(cam_spin, 
			  ChunLiData.cam_spin_axis[0], 
			  ChunLiData.cam_spin_axis[1], 
			  ChunLiData.cam_spin_axis[2]);
	glTranslatef(-self->origin[0], -self->origin[1], -self->origin[2]);

	cam_spin += ChunLiData.camera_spin_delta;
	ChunLiData.displacement_factor += ChunLiData.displacement_delta;
	if((ChunLiData.displacement_factor - 1.5) > 0)
	{
		ChunLiData.displacement_delta = -0.01;
	}
	else if((ChunLiData.displacement_factor - 0.5) < 0)
	{
		ChunLiData.displacement_delta = 0.01;
	}
}

/*
   BS:
   Listen to DSP.
*/
void ChunLiListen1(edict_t *self, struct winampVisModule *this_mod)
{
	static int direction = 1;
   register float kick = dsp.kick;

   if(kick > 0.5)
   {
	   if(direction > 0)
	   {
		   self->frame = ChunLiData.move_begin_frames[ChunLiData.curr_move];
		   self->end_frame = ChunLiData.move_mid_frames[ChunLiData.curr_move];
		   direction = -1;
	   }
	   else
	   {
		   self->end_frame = ChunLiData.move_end_frames[ChunLiData.curr_move];
		   self->loop_frame = ChunLiData.move_begin_frames[ChunLiData.curr_move];
	   }

   }
   else
   {
	   if(direction < 0)
	   {
		   self->end_frame = ChunLiData.move_end_frames[ChunLiData.curr_move];
		   self->loop_frame = -1;
		   direction = 1;
	   }
   }
}

// BS: drives an animation of Chun Li - nothing special, but potential for some
//     control over how fast the animation runs (i.e. stick in repeat frames to
//	   slow things down
void ChunLiListen2(edict_t *self, struct winampVisModule *this_mod)
{
	if((unsigned int) self->next_frame == ChunLiData.move_mid_frames[ChunLiData.curr_move])
	{
		self->next_frame = ChunLiData.move_end_frames[ChunLiData.curr_move];
		self->end_frame = self->next_frame;
	}
	else
	{
		self->next_frame++;
		self->end_frame = self->next_frame;
	}
}

/*
   BS:
   Listen to DSP.
   My first attempt to make a character stay within the confines of a shape
*/
void ChunLiListen3(edict_t *self, struct winampVisModule *this_mod)
{

   static int direction = -1;
/*
   static int direction_x = -1;
   static int direction_z = -1;
*/
   static int move = 1;
   static float start_angle;
   register float kick = dsp.kick;

   if(((self->origin[0] > 150) || (self->origin[2] > 150)) &&
	   (direction > 0))
   {
	   direction = -1;
	   self->avelocity[YAW] = direction * 10;
	   start_angle = self->angle[YAW];
	   self->velocity[0] = 0;
	   self->velocity[2] = 0;	
	   move = 0;
   }
   else if(((self->origin[0] < -150) || (self->origin[2] < -150)) &&
		(direction < 0))
   {
	   direction = 1;
	   self->avelocity[YAW] = direction * 10;
	   start_angle = self->angle[YAW];
	   self->velocity[0] = 0;
	   self->velocity[2] = 0;	
	   move = 0;
   }

   if(self->angle[YAW] > (90 + start_angle))
   {
	   self->angle[YAW] = 90 + start_angle;
	   self->avelocity[YAW] = 0;
	   move = 1;
   }
   
   else if(self->angle[YAW] < (start_angle - 90))
   {
	   self->angle[YAW] = start_angle - 90;
	   self->avelocity[YAW] = 0;
	   move = 1;
   }

   if(move)
   {
	  self->velocity[0] = (direction * (kick)) + direction;
	  self->velocity[2] = (direction * (1 - kick));
	  self->angle[YAW] = VectorYaw(self->velocity);
   }

   ChunLiData.camera_start[0] = self->origin[0];
   ChunLiData.camera_start[1] = self->origin[1];
   ChunLiData.camera_start[2] = self->origin[2];
}

/*
	BS: Listen to DSP
	Another possibility of how to slow down or speed up an animation to the beat
	by controlling it frame by frame (Maybe even reverse an animation???)
*/
void ChunLiListen4(edict_t *self, struct winampVisModule *this_mod)
{
	if((unsigned int) self->next_frame < ChunLiData.move_mid_frames[ChunLiData.curr_move])
	{
		self->next_frame = ChunLiData.move_mid_frames[ChunLiData.curr_move];
		self->end_frame = self->next_frame;
	}
	else if((unsigned int) self->next_frame != ChunLiData.move_end_frames[ChunLiData.curr_move])
	{
		self->next_frame++;
		self->end_frame = self->next_frame;
	}

}
/*
   BS:
   Render Boba Fett.
*/
void ChunLiRender(edict_t *self)
{
   if (last_texture != self->skin_index)
   {
      glBindTexture (GL_TEXTURE_2D, self->skin_index); 
      last_texture = self->skin_index;
   }   
   glEnable(GL_DEPTH_TEST);
   RenderAliasModel(self);
   glDisable(GL_DEPTH_TEST);
}

/*
   BS:
   Think function for Chun Li
	Chun Li runs for a while and then flips down to a spinning position
	not the smoothest animation, but fun to watch
*/
void ChunLiThink(edict_t *self)
{
	ChunLiData.curr_move++;
	if(ChunLiData.curr_move >= ChunLiData.num_moves)
	{
		ChunLiData.curr_move = 0;
	}
	switch(ChunLiData.curr_move)
	{
		case SPIN:
			self->listen = NULL;
			self->next_think = eng.time + 3000;
			self->next_frame = ChunLiData.move_begin_frames[ChunLiData.curr_move];
			self->loop_frame = ChunLiData.move_begin_frames[ChunLiData.curr_move];
			self->end_frame = ChunLiData.move_end_frames[ChunLiData.curr_move];
			self->velocity[1] = 0;
			self->avelocity[PITCH] = 0;
			self->angle[PITCH] = 180;
			break;
		case STAND_UP:
			self->listen = ChunLiListen2;
			self->avelocity[PITCH] = -60;
			self->next_think = eng.time + (50 * 4);
			self->loop_frame = -1;
			self->origin[1] *= 2;
			self->end_frame = ChunLiData.move_begin_frames[ChunLiData.curr_move];
			break;
		case WALK:
			self->listen = ChunLiListen3;
			self->next_think = eng.time + 20000;
			self->velocity[1] = 0;
			self->next_frame = ChunLiData.move_begin_frames[ChunLiData.curr_move];
			self->end_frame = ChunLiData.move_end_frames[ChunLiData.curr_move];
			self->loop_frame = ChunLiData.move_begin_frames[ChunLiData.curr_move];
			self->avelocity[PITCH] = 0;
			self->angle[PITCH] = 0;
			break;
		case GET_DOWN:
			self->listen = ChunLiListen4;
			self->next_think = eng.time + (50 * 5);
			self->loop_frame = -1;
			self->velocity[0] = 0;
			self->velocity[1] = 0;
			self->velocity[2] = 0;
			self->origin[1] /= 2;
			self->avelocity[PITCH] = 45;
			self->end_frame = ChunLiData.move_begin_frames[ChunLiData.curr_move];
			break;
		default:
			break;
	}
	self->next_frame = ChunLiData.move_begin_frames[ChunLiData.curr_move];
	
	ChunLiData.camera_start[0] = self->origin[0];
    ChunLiData.camera_start[1] = self->origin[1];
	ChunLiData.camera_start[2] = self->origin[2];
}

/*
   Chun Li:
   Spawn Chun Li
*/
edict_t *ChunLiSpawn()
{
   static unsigned int skinned = NOT_FOUND, modeled = NOT_FOUND;
   int model_index;
   edict_t *ent;
   skin_t *skin;
   ent = EdictSpawn();

   if (modeled == NOT_FOUND)
      {
      if ((model_index = CMD2Exist(CHUN_LI_MODEL)) == NOT_FOUND)
         {
         ent->model = new CMD2;
         if (CMD2Spawn(CHUN_LI_MODEL, ent->model) == NOT_FOUND)
            return NULL;
         if (ent->model->Read(CHUN_LI_MODEL) != 1)
            EngineError("SpawnBobaFett: Error reading Alias md2 '%s'", CHUN_LI_MODEL);
         }
      else
         {
         ent->model = g_CMD2s[model_index]->model;
         }
      }
   else
      ent->model = g_CMD2s[modeled]->model;

   // BS: Only load skins if they are not allocated already:
   if (skinned == NOT_FOUND)
      {
      // BS: Does skin exist in library?
      if ((ent->skin_num = SkinIndex(CHUN_LI_TEXTURE)) == NOT_FOUND)
         {
         int ix, iy;
         skin = new skin_t;
         memset(skin, 0, sizeof(*skin));
         ent->skin_num = SkinSpawn(CHUN_LI_TEXTURE, skin);
         if (!LoadTGA(CHUN_LI_TEXTURE, &skin->tga, skin->width, skin->height))
            EngineError("TGA LOAD: Skin '%s' could not be loaded.\n", CHUN_LI_TEXTURE);
         //glGenTextures (1, &ent->skin_index);
         ent->skin_index = eng.skin_index++;
         glBindTexture (GL_TEXTURE_2D, ent->skin_index);
         TGA2GLTexture (skin, 0, 255, ix, iy);
	      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
	      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
	      glTexImage2D (GL_TEXTURE_2D, 0, 4, ix, iy, 0, GL_RGBA, GL_UNSIGNED_BYTE, skin->glTexture);
         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	      glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	      glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_DONT_CARE);	
         skin->index = ent->skin_index;
         skinned = ent->skin_index;
		 UpdateTextureCoordinates(ent->model, skin);
         InitGL();      
         }
      else // Just use skin from already loaded list:
         ent->skin_index = g_skins[ent->skin_num]->skin->index;
      }
   else
      ent->skin_index = skinned;

   // State of entity:
   ent->state = ENT_ST_USED | ENT_ST_PHYS | ENT_ST_VISIBLE;

   ent->origin[0] = ChunLiData.m_start[0];
   ent->origin[1] = ChunLiData.m_start[1];
   ent->origin[2] = ChunLiData.m_start[2];
   ent->rgba[0] = 1;
   ent->rgba[1] = 1;
   ent->rgba[2] = 1;
   ent->rgba[3] = 1;
   ent->avelocity[PITCH] = 0;
   ent->avelocity[ROLL] = 0;
   ent->avelocity[YAW] = 0;
   ent->angle[PITCH] = 0;
   ent->frame = ChunLiData.move_begin_frames[ChunLiData.curr_move];
   ent->loop_frame = ChunLiData.move_begin_frames[ChunLiData.curr_move];
   ent->next_frame = ChunLiData.move_begin_frames[ChunLiData.curr_move];
   ent->end_frame = ChunLiData.move_begin_frames[ChunLiData.curr_move];
   ent->next_think = eng.time + 50;
   ent->scale = 0.3f;
   ent->render = ChunLiRender;
   ent->listen = ChunLiListen1;
   ent->think = ChunLiThink;
   ent->camera = ChunLiPosCamera;

   ChunLiData.camera_start[0] = ent->origin[0];
   ChunLiData.camera_start[1] = ent->origin[1];
   ChunLiData.camera_start[2] = ent->origin[2];
   return ent;
}