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

#include "global.h"

#define MARTIAN_MODEL "c:/DiscoStu/gfx/martian.md2"
#define MARTIAN_TEXTURE "c:/DiscoStu/gfx/martian0.tga"
#define STAND_FRAME_START  0
#define STAND_FRAME_END    39
#define RUN_FRAME_START    40
#define RUN_FRAME_END      45
#define ATTACK_FRAME_START 46
#define ATTACK_FRAME_END   53
#define JUMP_FRAME_START   54
#define JUMP_FRAME_END     59
#define WAVE1_FRAME_START  60
#define WAVE1_FRAME_END    70
#define WAVE2_FRAME_START  71
#define WAVE2_FRAME_END    87
#define WAVE3_FRAME_START  88
#define WAVE3_FRAME_END    98
#define DANCE_FRAME_START  99
#define DANCE_FRAME_END    110

#define FIELD_DESTINATION  0

void MartianListen1(edict_t *self, struct winampVisModule *this_mod);
void MartianListen2(edict_t *self, struct winampVisModule *this_mod);
void MartianListen3(edict_t *self, struct winampVisModule *this_mod);

/*
   NT:
   Listen to DSP.
*/
void MartianListen1(edict_t *self, struct winampVisModule *this_mod)
{
   register float kick = dsp.kick;
   self->rgba[0] = .3f + kick;
   self->rgba[1] = .3f + kick;
   self->rgba[2] = .3f + kick;
}

/*
   NT:
   Listen to DSP.
*/
void MartianListen2(edict_t *self, struct winampVisModule *this_mod)
{
   vec3_t shooter;
   edict_t *ent;
   register float kick = dsp.kick;

   self->rgba[0] = .3f + kick;
   self->rgba[1] = .3f + kick;
   self->rgba[2] = .3f + kick;
   AngleVectors (self->angle, shooter, NULL, NULL);
   VectorMA(shooter, 3.8f, self->origin, shooter);
  
   ent = FireFlySpawn(shooter, 0, cam.RGB_filter, 1);   
   AngleVectors(self->angle, ent->velocity, NULL, NULL);

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

/*
   NT:
   Camera renderer 1: Follow martian and rotate around him
*/
void MartianCam1(edict_t *self)
{
   static float xrotate = 180;
   vec3_t arrow = {30, 0, 0};
   arrow[1] = xrotate;
   xrotate += .3f;
   AngleVectors (arrow, arrow, NULL, NULL);
   VectorMA(arrow, 25, 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]);      
}

/*
   NT:
   Camera renderer 2: Zoom in and out of martian with dsp.kick
*/
void MartianCam2(edict_t *self)
{
   static float xrotate = 0, xskipper = 3.0;
   static int current_dsp_tick = 0;
   vec3_t arrow = {30, 0, 0};
   arrow[YAW] = self->angle[YAW] + xrotate;
   if (current_dsp_tick != dsp.tick)
      {
      current_dsp_tick = dsp.tick;
      xskipper = -xskipper;
      }
   xrotate += xskipper;
   AngleVectors (arrow, arrow, NULL, NULL);
   VectorMA(arrow, 25 + 10 * dsp.kick, 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]);      
}

/*
   NT:
   Render Martian.
*/
void MartianRender(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);
   glRotatef(30, 0, 1, 0);
   RenderAliasShadow(self);
   glDisable(GL_DEPTH_TEST);
}

/*
   NT:
   Think function for Martian.
*/
void MartianThink(edict_t *self)
{
   // Moon Walk:
   self->avelocity[YAW] = 0;
   self->loop_frame = RUN_FRAME_START;
   self->next_frame = RUN_FRAME_START;
   self->end_frame = RUN_FRAME_END;
   self->listen = MartianListen1;
   self->camera = MartianCam1;
   RunEdict(self);
}

/*
   NT:
   Martian has reached its destination
*/
void MartianReached(edict_t *self)
{
   switch(self->idata[FIELD_DESTINATION])
      {
      case 0:
         VectorSet(self->destination, 0, 7, (-400 + 90) * 0.4f);
         self->idata[FIELD_DESTINATION]++;
         break;
      case 1:
         VectorSet(self->destination, 0, 7, -70);
         self->idata[FIELD_DESTINATION]++;
         break;
      case 2:
         VectorSet(self->destination, (408 - 90) * 0.4f, 7, 0);
         self->idata[FIELD_DESTINATION] = 0;
         break;
      }
   switch (rand() & 7)
      {
      case 0:
         self->loop_frame = DANCE_FRAME_START;
         self->next_frame = DANCE_FRAME_START;
         self->end_frame = DANCE_FRAME_END;
         self->listen = MartianListen1;
         self->next_think = eng.time + 2000; 
         break;
      case 1:
         self->loop_frame = WAVE1_FRAME_START;
         self->next_frame = WAVE1_FRAME_START;
         self->end_frame = WAVE1_FRAME_END;
         self->listen = MartianListen1;
         self->next_think = eng.time + 2000; 
         break;
      case 2:
         self->loop_frame = WAVE2_FRAME_START;
         self->next_frame = WAVE2_FRAME_START;
         self->end_frame = WAVE2_FRAME_END;
         self->listen = MartianListen1;
         self->next_think = eng.time + 2000; 
         break;
      case 3:
         self->loop_frame = WAVE3_FRAME_START;
         self->next_frame = WAVE3_FRAME_START;
         self->end_frame = WAVE3_FRAME_END;
         self->listen = MartianListen1;
         self->next_think = eng.time + 2000; 
         break;
      case 4:
         self->loop_frame = JUMP_FRAME_START;
         self->next_frame = JUMP_FRAME_START;
         self->end_frame = JUMP_FRAME_END;
         self->listen = MartianListen1;
         self->next_think = eng.time + 2000; 
         break;
      default:
         self->loop_frame = STAND_FRAME_START;
         self->next_frame = STAND_FRAME_START;
         self->end_frame = STAND_FRAME_END;
         self->listen = MartianListen2;
         self->next_think = eng.time + 7000; 
         break;
      }
   self->camera = MartianCam1;
   VectorClear(self->velocity);
   self->next_think = eng.time + 5000;
}

/*
   NT:
   Spawn Martian.
*/
edict_t *MartianSpawn()
{
   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(MARTIAN_MODEL)) == NOT_FOUND)
         {
         ent->model = new CMD2;
         if (CMD2Spawn(MARTIAN_MODEL, ent->model) == NOT_FOUND)
            return NULL;
         if (ent->model->Read(MARTIAN_MODEL) != 1)
            EngineError("SpawnMartian: Error reading Alias md2 '%s'", MARTIAN_MODEL);
         }
      else
         {
         ent->model = g_CMD2s[model_index]->model;
         }
      }
   else
      ent->model = g_CMD2s[modeled]->model;

   // NT: Only load skins if they are not allocated already:
   if (skinned == NOT_FOUND)
      {
      // NT: Does skin exist in library?
      if ((ent->skin_num = SkinIndex(MARTIAN_TEXTURE)) == NOT_FOUND)
         {
         int ix, iy;
         skin = new skin_t;
         memset(skin, 0, sizeof(*skin));
         ent->skin_num = SkinSpawn(MARTIAN_TEXTURE, skin);
         if (!LoadTGA(MARTIAN_TEXTURE, &skin->tga, skin->width, skin->height))
            EngineError("TGA LOAD: Skin '%s' could not be loaded.\n", MARTIAN_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] = 0;
   ent->origin[1] = 7;
   ent->origin[2] = -70;
   ent->rgba[0] = 1;
   ent->rgba[1] = 1;
   ent->rgba[2] = 1;
   ent->rgba[3] = 1;
   ent->frame = DANCE_FRAME_START;
   ent->loop_frame = DANCE_FRAME_START;
   ent->end_frame = DANCE_FRAME_END;
   ent->scale = 0.3f;
   ent->render = MartianRender;
   ent->listen = MartianListen1;
   ent->think = MartianThink;
   ent->next_think = eng.time + 5000;
   ent->camera = MartianCam1;
   ent->reached = MartianReached;
   ent->idata[FIELD_DESTINATION] = 0;
   ent->speed = .75f;
   VectorSet(ent->destination, (408 - 90) * 0.4f, 7, 0);
   return ent;
}