/*
   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:
      Boba Fett Alias module.
*/

#include "global.h"

// BS: My first character =)
#define BOBA_FETT_MODEL "c:/DiscoStu/gfx/BobaFett/tris.md2"
#define BOBA_FETT_TEXTURE "c:/DiscoStu/gfx/BobaFett/ESB_Fett.tga"
#define POINT 0
#define WAVE 1
#define BOUNCE 2
#define TWIST 3
#define POINT_FRAME_START 123
#define POINT_FRAME_MID	  127
#define POINT_FRAME_END   134
#define POINT_FRAME_LENGTH POINT_FRAME_END - POINT_FRAME_START
#define WAVE_FRAME_START 112
#define WAVE_FRAME_MID  118
#define WAVE_FRAME_END 122
#define WAVE_FRAME_LENGTH WAVE_FRAME_END - WAVE_FRAME_START
#define BOUNCE_FRAME_START 71
#define BOUNCE_FRAME_MID   69
#define BOUNCE_FRAME_END   71
#define TWIST_FRAME_START  0
#define TWIST_FRAME_MID	   20
#define TWIST_FRAME_END	   39

// BS: character data for animations
m_data_t BobaFettData =
{
	4,
	TWIST,
	{POINT, WAVE, BOUNCE, TWIST},
	{POINT_FRAME_START, WAVE_FRAME_START, BOUNCE_FRAME_START, TWIST_FRAME_START},
	{POINT_FRAME_MID, WAVE_FRAME_MID, BOUNCE_FRAME_MID, TWIST_FRAME_END},
	{POINT_FRAME_END, WAVE_FRAME_END, BOUNCE_FRAME_MID, TWIST_FRAME_MID},
	2,
	{0, 1, 0},
	16,
	0.1,
	{0, 7, 10},
	{0, 0, 0},
	{-142, 19, 140}
};

/*
	BS: points camera at Boba Fett and spins it around his origin while moving
	the camera toward and away from him
*/
void BobaFettPosCamera(struct edict_s *self)
{
	static float cam_spin_x = self->angle[YAW];
	static float cam_spin_y = 0;
	static float delta_x = BobaFettData.camera_spin_delta;
	static float delta_y = 0;
	float cam_offset;
	vec3_t arrow = {0, 0, 0};
	arrow[1] += cam_spin_x;
	arrow[0] += cam_spin_y;

	AngleVectors(arrow, arrow, NULL, NULL);
	VectorMA(arrow, BobaFettData.displacement_factor, self->origin, cam.origin);
	VectorSubtract(self->origin, cam.origin, arrow);
	VectorAngles(arrow, cam.angle);

	glLoadIdentity();
	glRotatef(-cam.angle[PITCH], 1, 0, 0);
	glRotatef(-cam.angle[YAW], 0, 1, 0);
	glRotatef(-cam.angle[ROLL], 0, 0, 1);
	glTranslatef(-cam.origin[0], -cam.origin[1], -cam.origin[2]);      

	cam_spin_x += delta_x;
	cam_spin_y += delta_y;
	cam_offset = (cam_spin_x - self->angle[YAW]);
	if((cam_offset < -45) || (cam_offset > 45))
	{
		/* stop moving camera in x direction, */
		cam_spin_x += -delta_x;
		delta_y = delta_x;
		delta_x = 0;
	}
	else if((cam_spin_y < -30) || (cam_spin_y > 30))
	{
		cam_spin_y += -delta_y;
		delta_x = -delta_y;
		delta_y = 0;
	}

	BobaFettData.displacement_factor += BobaFettData.displacement_delta;
	if((BobaFettData.displacement_factor - 25) > 0)
	{
		BobaFettData.displacement_delta = -0.1;
	}
	else if((BobaFettData.displacement_factor - 17) < 0)
	{
		BobaFettData.displacement_delta = 0.1;
	}
}

/*
	BS: My first attempt to drive an animation by the beat of the song
		By picking a midpoint in the "move" the character only completes
		the move once a kick is registered above 0.5
*/
void BobaFettListen1(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 = BobaFettData.move_begin_frames[BobaFettData.curr_move];
		   self->end_frame = BobaFettData.move_mid_frames[BobaFettData.curr_move];
		   direction = -1;
	   }
	   else
	   {
		   self->end_frame = BobaFettData.move_end_frames[BobaFettData.curr_move];
		   self->loop_frame = BobaFettData.move_begin_frames[BobaFettData.curr_move];
	   }

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

/*
   BS:
   Listen to DSP.
   Makes Boba Fett bounce to the music
*/
void BobaFettListen2(edict_t *self, struct winampVisModule *this_mod)
{

	static int direction = 1;
   register float kick = dsp.kick;
   if((kick > 0.5))
   {
		self->loop_frame = -1;
		self->next_frame = self->frame + direction;
		if((unsigned int) self->next_frame > BobaFettData.move_end_frames[BobaFettData.curr_move])
		{
			self->next_frame = BobaFettData.move_end_frames[BobaFettData.curr_move] - 1;
			direction = -1;
		}
		else if((unsigned int) self->next_frame < BobaFettData.move_mid_frames[BobaFettData.curr_move])
		{
			self->next_frame = BobaFettData.move_mid_frames[BobaFettData.curr_move] + 1;
			direction = 1;
		}
		self->end_frame = self->next_frame;
   }
   else
   {
		self->end_frame = BobaFettData.move_end_frames[BobaFettData.curr_move];
   }
}

/*
   BS:
   Render Boba Fett.
*/
void BobaFettRender(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 Boba Fett.
*/
void BobaFettThink(edict_t *self)
{
	// BS: Boba Fett shows off all of his moves
	BobaFettData.curr_move++;
	if(BobaFettData.curr_move >= BobaFettData.num_moves)
	{
		BobaFettData.curr_move = 0;
	}
	switch(BobaFettData.curr_move)
	{
		case BOUNCE:
			self->frame = BobaFettData.move_end_frames[BobaFettData.curr_move];
			self->next_frame = BobaFettData.move_end_frames[BobaFettData.curr_move];
			self->end_frame = BobaFettData.move_end_frames[BobaFettData.curr_move];
			self->loop_frame = -1;
			self->next_think = eng.time + 10000;
			self->listen = BobaFettListen2;
			break;
		case POINT:
		case WAVE:
		case TWIST:
			self->listen = BobaFettListen1;
			self->next_think = eng.time + 5000 + (1000 * BobaFettData.curr_move);
			self->frame = BobaFettData.move_begin_frames[BobaFettData.curr_move];
			self->next_frame = BobaFettData.move_begin_frames[BobaFettData.curr_move];
			self->end_frame = BobaFettData.move_mid_frames[BobaFettData.curr_move];
			self->loop_frame = -1;
			break;
		default:
			break;
	}
}

/*
   BS:
   Spawn Boba Fett.
*/
edict_t *BobaFettSpawn()
{
   static unsigned int skinned = NOT_FOUND, modeled = NOT_FOUND;
   int model_index;
   edict_t *ent;
   skin_t *skin;
   ent = EdictSpawn();
   vec3_t arrow = {0, 0, 0};

   if (modeled == NOT_FOUND)
      {
      if ((model_index = CMD2Exist(BOBA_FETT_MODEL)) == NOT_FOUND)
         {
         ent->model = new CMD2;
         if (CMD2Spawn(BOBA_FETT_MODEL, ent->model) == NOT_FOUND)
            return NULL;
         if (ent->model->Read(BOBA_FETT_MODEL) != 1)
            EngineError("SpawnBobaFett: Error reading Alias md2 '%s'", BOBA_FETT_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(BOBA_FETT_TEXTURE)) == NOT_FOUND)
         {
         int ix, iy;
         skin = new skin_t;
         memset(skin, 0, sizeof(*skin));
         ent->skin_num = SkinSpawn(BOBA_FETT_TEXTURE, skin);
         if (!LoadTGA(BOBA_FETT_TEXTURE, &skin->tga, skin->width, skin->height))
            EngineError("TGA LOAD: Skin '%s' could not be loaded.\n", BOBA_FETT_TEXTURE);
         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] = BobaFettData.m_start[0];
   ent->origin[1] = BobaFettData.m_start[1];
   ent->origin[2] = BobaFettData.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->frame = BobaFettData.move_begin_frames[BobaFettData.curr_move];
   ent->loop_frame = -1;
   ent->end_frame = BobaFettData.move_begin_frames[BobaFettData.curr_move];
   ent->next_frame = BobaFettData.move_begin_frames[BobaFettData.curr_move];
   ent->next_think = eng.time + 50;
   ent->scale = 0.3f;
   ent->render = BobaFettRender;
   ent->listen = BobaFettListen1;
   ent->think = BobaFettThink;
   ent->camera = BobaFettPosCamera;

   VectorSubtract(arrow, ent->origin, arrow);   
   ent->angle[YAW] = VectorYaw(arrow);

   return ent;
}