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

#include "global.h"
#include <math.h>

#define SNORK_MODEL "c:/DiscoStu/gfx/Snork/tris.md2"
#define DUKE_SNORK_TEXTURE "c:/DiscoStu/gfx/Snork/DukeS.tga"
#define TUXEDO_SNORK_TEXTURE "c:/DiscoStu/gfx/Snork/Tuxedo.tga"
#define DISCO_SNORK_TEXTURE "c:/DiscoStu/gfx/Snork/Disco.tga"
#define HARRYIT_SNORK_TEXTURE "c:/DiscoStu/gfx/Snork/Harryit.tga"
#define MR_PANTS_SNORK_TEXTURE "c:/DiscoStu/gfx/Snork/MrPants.tga"
#define SNORK_TEXTURE "c:/DiscoStu/gfx/Snork/Disco.tga"
#define SNORT 0
#define DANCE 1
#define WAVE  2
#define STAND 3
#define JUMP  4
#define SNORT_START 72
#define SNORT_MID	83
#define SNORT_END   83
#define DANCE_START 46
#define DANCE_MID   53
#define DANCE_END   53
#define WAVE_START  95
#define WAVE_MID	104
#define WAVE_END	111
#define STAND_START 0
#define STAND_MID   20
#define STAND_END	39
#define JUMP_START  66
#define JUMP_MID    71
#define JUMP_END	71

#define DISCO   0
#define TUXEDO  1
#define DUKE    2
#define HARRYIT 3
#define MRPANTS 4

static skin_t disco_skin,
              tuxedo_skin,
			  duke_skin,
			  harryit_skin,
			  mrpants_skin;

// BS: Snorks, snorks everywhere!
m_data_t SnorkData =  
{
	5,
	DANCE,
	{SNORT, DANCE, WAVE, STAND, JUMP}, 
    {SNORT_START, DANCE_START, WAVE_START, STAND_START, JUMP_START},
	{SNORT_MID, DANCE_MID, WAVE_MID, STAND_MID, JUMP_MID},
	{SNORT_END, DANCE_END, WAVE_END, STAND_END, JUMP_END},
	1,
	{0, 1, 0},
	35,
	0,
	{0, 2, 20},
	{0, 0, 0},
	{-15, 7, -70}
};

/*
	BS: Position Camera on Snork
*/
void SnorkPosCamera(struct edict_s *self)
{
	static int cam_spin = 0;
	vec3_t arrow = {10, -90, 0};
	arrow[1] += cam_spin;

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

	/*
		To preserve the smoke effect, the camera must be rotated in the
		same order every time, hence the LookAt function is not sufficient
		enough for camera orientation
	*/

	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 += SnorkData.camera_spin_delta;
}

// BS: drives an animation of Snork. Blows smoke!
void SnorkListen2(edict_t *self, struct winampVisModule *this_mod)
{  
   vec3_t shooter;
   edict_t *ent;
   vec3_t smoke_rgb = {0.75, 0.75, 0.75};
   register float kick = dsp.kick;

		AngleVectors (self->angle, NULL, NULL, shooter);
		VectorMA(shooter, 5.5f, self->origin, shooter);
		shooter[2] = self->origin[2] + 2.75f;

		ent = FireFlySpawn(shooter, 0, smoke_rgb, 1);   
		AngleVectors(self->angle, NULL, NULL, ent->velocity);

		ent->velocity[0] *= dsp.kick;
		ent->velocity[1] *= dsp.kick/2;
		ent->velocity[2] *= dsp.kick;
}

/*
   BS:
   Render Snork.
*/
void SnorkRender(edict_t *self)
{
/* What shall our snork wear? */
	switch(self->skin_num)
   {
   case DISCO:
		glBindTexture (GL_TEXTURE_2D, disco_skin.index); 
	    last_texture = disco_skin.index;
		break;
   case TUXEDO:
		glBindTexture (GL_TEXTURE_2D, tuxedo_skin.index); 
		last_texture = tuxedo_skin.index;
		break;
   case DUKE:
		glBindTexture (GL_TEXTURE_2D, duke_skin.index); 
		last_texture = duke_skin.index;
		break;
   case HARRYIT:
		glBindTexture (GL_TEXTURE_2D, harryit_skin.index); 
		last_texture = harryit_skin.index;
		break;
   case MRPANTS:
		glBindTexture (GL_TEXTURE_2D, mrpants_skin.index); 
		last_texture = mrpants_skin.index;
		break;
   default:
		glBindTexture (GL_TEXTURE_2D, disco_skin.index); 
		last_texture = disco_skin.index;
		break;
	}

   glEnable(GL_DEPTH_TEST);
   RenderAliasModel(self);
   glRotatef(-self->angle[YAW] + 30, 0, 1, 0);
   RenderAliasShadow(self);

   glDisable(GL_DEPTH_TEST);
}

/*
   BS:
   Think function for Snork.  Randomly selects a move from set of animations
*/
void SnorkThink(edict_t *self)
{
	SnorkData.curr_move = rand() % SnorkData.num_moves;
	switch(SnorkData.curr_move)
	{
		case DANCE:
			self->listen = NULL;
			self->next_think = eng.time + 15000;
			break;
		case SNORT:
			if(self->skin_num == DISCO)
			{
				self->listen = SnorkListen2;
			}
			else
			{
				self->listen = NULL;
			}
			self->next_think = eng.time + 10000;
			break;
		case WAVE:
			self->listen = NULL;
			self->next_think = eng.time + 7000;
			break;
		case STAND:
			self->listen = NULL;
			self->next_think = eng.time + 11000;
			break;
		case JUMP:
			self->listen = NULL;
			self->next_think = eng.time + 3000;
			break;
		default:
			break;
	}
	self->next_frame = SnorkData.move_begin_frames[SnorkData.curr_move];
	self->loop_frame = SnorkData.move_begin_frames[SnorkData.curr_move];
	self->end_frame = SnorkData.move_end_frames[SnorkData.curr_move];
}

/*
   BS:
   Spawn Snork
*/
edict_t *SnorkSpawn()
{
   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(SNORK_MODEL)) == NOT_FOUND)
         {
         ent->model = new CMD2;
         if (CMD2Spawn(SNORK_MODEL, ent->model) == NOT_FOUND)
            return NULL;
         if (ent->model->Read(SNORK_MODEL) != 1)
            EngineError("SpawnSnork: Error reading Alis md2 '%s'", SNORK_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(DISCO_SNORK_TEXTURE)) == NOT_FOUND)
         {
         int ix, iy;
		 skin = &disco_skin;
         memset(skin, 0, sizeof(*skin));
		 SkinSpawn(DISCO_SNORK_TEXTURE, skin);
         if (!LoadTGA(DISCO_SNORK_TEXTURE, &skin->tga, skin->width, skin->height))
            EngineError("TGA LOAD: Skin '%s' could not be loaded.\n", DISCO_SNORK_TEXTURE);
         disco_skin.index = eng.skin_index++;
		 ent->skin_index = disco_skin.index;
         glBindTexture (GL_TEXTURE_2D, disco_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 = disco_skin.index;

         skin = &tuxedo_skin;
         memset(skin, 0, sizeof(*skin));
		 SkinSpawn(TUXEDO_SNORK_TEXTURE, skin);
         if (!LoadTGA(TUXEDO_SNORK_TEXTURE, &skin->tga, skin->width, skin->height))
            EngineError("TGA LOAD: Skin '%s' could not be loaded.\n", TUXEDO_SNORK_TEXTURE);
         tuxedo_skin.index = eng.skin_index++;
         glBindTexture (GL_TEXTURE_2D, tuxedo_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 = tuxedo_skin.index;

		 skin = &duke_skin;
         memset(skin, 0, sizeof(*skin));
		 SkinSpawn(DUKE_SNORK_TEXTURE, skin);
         if (!LoadTGA(DUKE_SNORK_TEXTURE, &skin->tga, skin->width, skin->height))
            EngineError("TGA LOAD: Skin '%s' could not be loaded.\n", DUKE_SNORK_TEXTURE);
         duke_skin.index = eng.skin_index++;
         glBindTexture (GL_TEXTURE_2D, duke_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 = duke_skin.index;
			  
		 skin = &harryit_skin;
         memset(skin, 0, sizeof(*skin));
		 SkinSpawn(HARRYIT_SNORK_TEXTURE, skin);
	     if (!LoadTGA(HARRYIT_SNORK_TEXTURE, &skin->tga, skin->width, skin->height))
            EngineError("TGA LOAD: Skin '%s' could not be loaded.\n", HARRYIT_SNORK_TEXTURE);
         harryit_skin.index = eng.skin_index++;
         glBindTexture (GL_TEXTURE_2D, harryit_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 = harryit_skin.index;

		 skin = &mrpants_skin;
         memset(skin, 0, sizeof(*skin));
		 SkinSpawn(MR_PANTS_SNORK_TEXTURE, skin);
         if (!LoadTGA(MR_PANTS_SNORK_TEXTURE, &skin->tga, skin->width, skin->height))
            EngineError("TGA LOAD: Skin '%s' could not be loaded.\n", TUXEDO_SNORK_TEXTURE);
         mrpants_skin.index = eng.skin_index++;
         glBindTexture (GL_TEXTURE_2D, mrpants_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 = mrpants_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] = SnorkData.m_start[0];
   ent->origin[1] = SnorkData.m_start[1];
   ent->origin[2] = SnorkData.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->angle[YAW] = 180;
   ent->frame = SnorkData.move_begin_frames[SnorkData.curr_move];
   ent->loop_frame = SnorkData.move_begin_frames[SnorkData.curr_move];
   ent->next_frame = SnorkData.move_begin_frames[SnorkData.curr_move];
   ent->end_frame = SnorkData.move_end_frames[SnorkData.curr_move];
   ent->next_think = eng.time + 5000;
   ent->scale = 0.3f;
   ent->render = SnorkRender;
   ent->listen = NULL;
   ent->think = SnorkThink;
   ent->camera = SnorkPosCamera;
   return ent;
}