// MidiVizPlanets
// Lew Hill II

#include "MidiVizPlanets.h"

MidiVizPlanets::MidiVizPlanets(){
  init();
}

void MidiVizPlanets::init(){

  for (int i = 0; i < NOTE_ARRAY_LENGTH; i++){
    noteScale[i] = 0;
    planetAngle[i] = (rand()/32768.0) * 360.0;
  }

  float radians;
  for (int i =0; i < 360; i++){
    radians = i * MV_PI_RAD;
    orbitCoordinates[i][0] = cos(radians);
    orbitCoordinates[i][1] = sin(radians);
  }
  
  position[0] = 0;
  position[1] = 0;  
  position[2] = 0;

  rotation[0] = 0;
  rotation[1] = 0;  
  rotation[2] = 0;

  scale[0] = 1;
  scale[1] = 1;  
  scale[2] = 1;

}


void MidiVizPlanets::contextInit(){

  contextData->sphereQuadric = gluNewQuadric();

   // --- Allocate context specific data --- //
   contextData->displayList = glGenLists(1);

   glNewList(contextData->displayList, GL_COMPILE);
   gluSphere(contextData->sphereQuadric, .25, 12 /* slices */, 12 /*stacks*/);
   glEndList();

}

void MidiVizPlanets::processMidiEvent(MidiEvent event){
  
  int note = event.getNote();
  int velocity = event.getVelocity();

  int note_index = note-MIDI_LOW;

  if (velocity > 0){
    noteScale[note_index] = velocity/127.0;
  }

  float* noteColor = color_table.getValue((note+MIDI_LOW)%12);  

  last_note = (note_index) % 12;
  sunColor[0] = noteColor[0]/noteScale[note_index];
  sunColor[1] = noteColor[1]/noteScale[note_index];
  sunColor[2] = noteColor[2]/noteScale[note_index];


  //  normalize(sunColor);

}

void MidiVizPlanets::normalize(float* vector){
 
  float max_val = 0;

  for (int i = 0; i < 3; i++){
    if (vector[i] > max_val){
      max_val = vector[i];
    }
  } 

  vector[0] = vector[0]/max_val;
  vector[1] = vector[1]/max_val;
  vector[2] = vector[2]/max_val;

}

void MidiVizPlanets::frame(){

  for (int i = 0; i < NOTE_ARRAY_LENGTH; i++){
    
    planetAngle[i] += noteScale[i] * 1.5 * 
      (NOTE_ARRAY_LENGTH-i)/NOTE_ARRAY_LENGTH + .2;
    if (planetAngle[i] > 360.0){
      planetAngle[i] -= 360.0;
    }
    noteScale[i] *=.93;
  }

  sunColor[0] *= .90;
  sunColor[1] *= .90;
  sunColor[2] *= .90;
  sunColorAvg = (sunColor[0] + sunColor[1] + sunColor[2])/60.0;  

  sunColorLongtermAverage = .5*sunColorLongtermAverage + .4 * sunColorAvg;
  if (sunColorLongtermAverage > 1.0){
    sunColorLongtermAverage = 1.0;
  }
}

void MidiVizPlanets::draw(){

  glLineWidth(2.0);
    
  glPushMatrix();
  glTranslatef(position[0], position[1], position[2]);
  glRotatef(rotation[2], 0.0, 0.0, 1.0); 
  glRotatef(rotation[1], 0.0, 1.0, 0.0); 
  glRotatef(rotation[0], 1.0, 0.0, 0.0); 
  glScalef(scale[0], scale[1], scale[2]);

  // draw sun
  glColor3fv(sunColor);
  glPushMatrix();
  glScalef(sunColorLongtermAverage, 
	   sunColorLongtermAverage, 
	   sunColorLongtermAverage);
  glCallList(contextData->displayList);
  glPopMatrix();

  for (int i = 0; i < NOTE_ARRAY_LENGTH; i++){
    
    float* colors = color_table.getValue((i+MIDI_LOW)%12);
    float attColor[3];
    float attFactor = 0.02 + (0.98 * noteScale[i]);
    attColor[0] = colors[0] * attFactor;
    attColor[1] = colors[1] * attFactor;
    attColor[2] = colors[2] * attFactor;

    glColor3fv(attColor);

    float orbitRadius = ((float)(NOTE_ARRAY_LENGTH - i)/(float)6.0) +3; 
    
    // DRAW PLANETS IN ORDER FROM LOWEST TO HIGHEST
    glPushMatrix();
    glRotatef(planetAngle[i], 0,1,0);
    glTranslatef(orbitRadius, 0, 0);

    float planet_scale =(.15*(1+noteScale[i])) / 1.2;

    glPushMatrix();
    glScalef(planet_scale, planet_scale, planet_scale);
    glCallList(contextData->displayList);
    glPopMatrix();
    
    glPopMatrix();
    
    // DRAW PLANET ORBITS
    glPushMatrix();
    
    glScalef(orbitRadius, orbitRadius, orbitRadius);
    
    if (noteScale[i] > 1){
      
      glBegin(GL_QUAD_STRIP);    
      for (int k =0; k < 360; k++){
	glVertex3f(orbitCoordinates[k][0], noteScale[i], 
		   orbitCoordinates[k][1]);
	glVertex3f(orbitCoordinates[k][0], -noteScale[i], 
		   orbitCoordinates[k][1]);
      }
      glEnd();
      
    } else {
      
      glBegin(GL_LINE_STRIP);    
      for (int k =0; k < 360; k++){    
	glVertex3f(orbitCoordinates[k][0], 0, orbitCoordinates[k][1]);
      }
      glEnd();
      
    }
  }
  
  glPopMatrix();
  glLineWidth(2.0);
}

void MidiVizPlanets::drawbox(GLdouble x0, GLdouble x1, GLdouble y0, GLdouble y1,
             GLdouble z0, GLdouble z1, GLenum type)
{
   static GLdouble n[6][3] = {
      {-1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {1.0, 0.0, 0.0},
      {0.0, -1.0, 0.0}, {0.0, 0.0, 1.0}, {0.0, 0.0, -1.0}
   };
   static GLint faces[6][4] = {
      { 0, 1, 2, 3}, { 3, 2, 6, 7}, { 7, 6, 5, 4},
      { 4, 5, 1, 0}, { 5, 6, 2, 1}, { 7, 4, 0, 3}
   };
   GLdouble v[8][3], tmp;
   GLint i;

   if (x0 > x1)
   {
      tmp = x0; x0 = x1; x1 = tmp;
   }
   if (y0 > y1)
   {
      tmp = y0; y0 = y1; y1 = tmp;
   }
   if (z0 > z1)
   {
      tmp = z0; z0 = z1; z1 = tmp;
   }
   v[0][0] = v[1][0] = v[2][0] = v[3][0] = x0;
   v[4][0] = v[5][0] = v[6][0] = v[7][0] = x1;
   v[0][1] = v[1][1] = v[4][1] = v[5][1] = y0;
   v[2][1] = v[3][1] = v[6][1] = v[7][1] = y1;
   v[0][2] = v[3][2] = v[4][2] = v[7][2] = z0;
   v[1][2] = v[2][2] = v[5][2] = v[6][2] = z1;

   for (i = 0; i < 6; i++)
   {
      glBegin(type);
         glNormal3dv(&n[i][0]);
         glVertex3dv(&v[faces[i][0]][0]);
         glNormal3dv(&n[i][0]);
         glVertex3dv(&v[faces[i][1]][0]);
         glNormal3dv(&n[i][0]);
         glVertex3dv(&v[faces[i][2]][0]);
         glNormal3dv(&n[i][0]);
         glVertex3dv(&v[faces[i][3]][0]);
      glEnd();
   }
}

