// LewHill2
// MIDIVIZ WALL


#include "MidiVizLifeSim.h"


MidiVizLifeSim::MidiVizLifeSim()
   : drawPoints(0)
{
  
  for (int i =0; i < LS_WALL_HEIGHT; i++){
    for (int j =0; j < LS_WALL_WIDTH; j++){
      pointsA[i][j] =  0;
      pointsB[i][j] =  0;
      pointAge[i][j] = 0;
      
      texture[i][j][0] = (GLubyte) 0;
      texture[i][j][1] = (GLubyte) 0;
      texture[i][j][2] = (GLubyte) 0;
    }
  }
  
   
  //init();
}

void MidiVizLifeSim::init(){
  generateTexture();
}

void MidiVizLifeSim::contextInit(){
  contextData->wallTextureIndex = glGenLists(1);
}

void MidiVizLifeSim::generateTexture(){
  if (!(contextData->firstTime))
  { 
     glDeleteLists(contextData->wallTextureIndex, 0);
     contextData->firstTime = false;
  }
  glNewList(contextData->wallTextureIndex, GL_COMPILE);                      
  glEnable(GL_TEXTURE_2D);
  
  int note;
  
  for (int i = 0; i < LS_WALL_WIDTH; i++){
    for (int j = 0; j < LS_WALL_HEIGHT; j++){
      
      
      if (drawPoints == 0){
	note = pointsA[i][j]; 
      } else {
	note = pointsB[i][j];
      }
      
      GLubyte color_r, color_g, color_b;
      
      GLubyte* colors = color_table.getByteValue(note %12);
      color_r = colors[0];
      color_g = colors[1];
      color_b = colors[2];
      
      if (pointAge[i][j] > 20) pointAge[i][j] = 20;

      texture[i][j][0] = color_r;	
      texture[i][j][1] = color_g;
      texture[i][j][2] = color_b;
      
    }
  }
  
  
  glTexImage2D(GL_TEXTURE_2D, 0, 3, LS_WALL_WIDTH, LS_WALL_HEIGHT, 
	       0, GL_RGB, GL_UNSIGNED_BYTE, &texture[0][0][0]); 
  
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  glColor4f(1.0, 1.0, 1.0, 1.0);
  glEndList();
  
}

void MidiVizLifeSim::draw(){
  
  generateTexture();
  
  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]);
  
  /*
  glColor3f(1.0, 1.0, 1.0);
  glCallList(contextData->wallTextureIndex);
  glBegin(GL_QUADS);
  glNormal3f(0,0,1);
  glTexCoord2i(0, 0); glVertex3f(-5, -5, 0);
  glTexCoord2i(1, 0); glVertex3f(5, -5, 0);  
  glTexCoord2i(1, 1); glVertex3f(5, 5, 0);  
  glTexCoord2i(0, 1); glVertex3f(-5, 5, 0);
  glEnd();
  glDisable(GL_TEXTURE_2D); 
  */  

  float xbase = -5;
  float ybase = -5;
  float xmax = 5;
  float ymax = 5;
  float xstep = (xmax - xbase)/(float) LS_WALL_WIDTH;
  float ystep = (ymax - ybase)/ (float) LS_WALL_HEIGHT;
  float xcdt = 0;
  float ycdt = 0;
  
  for (int i = 0; i < LS_WALL_WIDTH-1; i++){
    
    xcdt = xbase + i*xstep;

    glBegin(GL_TRIANGLE_STRIP);
    for (int j = 0; j < LS_WALL_HEIGHT; j++){

      ycdt = ybase + j*ystep;

      glNormal3f(xstep, ystep, (pointAge[i][j]-pointAge[i+1][j])/2.0);

      glColor3ubv(texture[i][j]);
      glVertex3f(xcdt, ycdt, 0.05 * pointAge[i][j]);

      glNormal3f(xstep, ystep, (pointAge[i+1][j]-pointAge[i][j])/2.0);

      glColor3ubv(texture[i+1][j]);
      glVertex3f(xcdt+xstep, ycdt, 0.05 * pointAge[i+1][j]);
    }
    glEnd();


    glBegin(GL_TRIANGLE_STRIP);
    for (int j = 0; j < LS_WALL_HEIGHT; j++){

      ycdt = ybase + j*ystep;

      glColor3ubv(texture[i][j]);
      glVertex3f(xcdt, ycdt, 2 - 0.05 * pointAge[i][j]);
      glColor3ubv(texture[i+1][j]);
      glVertex3f(xcdt+xstep, ycdt, 2 - 0.05 * pointAge[i+1][j]);
    }
    glEnd();

  }
  glPopMatrix();
}


void MidiVizLifeSim::processMidiEvent(MidiEvent event){
  
  int note = event.getNote();
  int velocity = event.getVelocity();
  
  noteValues[note%12] = velocity;

  int x_cdt, y_cdt;

  if (velocity > -1 && velocity < 128){
  
    int numNewCells = velocity/12;

    for (int i =0; i < numNewCells ; i++){
      
      //      int x_cdt = (rand()/32768.0) * LS_WALL_HEIGHT;
      //      int y_cdt = (rand()/32768.0) * LS_WALL_WIDTH;
      
      x_cdt =  (int) ((float) LS_WALL_HEIGHT * rand()/(RAND_MAX));
      y_cdt =  (int) ((float) LS_WALL_WIDTH * rand()/(RAND_MAX));

      //      cout << "LS" << x_cdt << " "  << y_cdt << endl;

      if (drawPoints ==0){
	pointsA[x_cdt][y_cdt] = note%12;
	pointAge[x_cdt][y_cdt] = 0;
      } else {
	pointsB[x_cdt][y_cdt] = note%12;
	pointAge[x_cdt][y_cdt] = 0;
      }
    }
  }   
}
  

void MidiVizLifeSim::frame(){
  
  int i;
  int j;

  for (i =0; i < LS_WALL_HEIGHT; i++){
    
    int up_i = i+1;
    if (up_i > LS_WALL_HEIGHT){
      up_i -= LS_WALL_HEIGHT;
    }
    
    int down_i =i-1;
    if (down_i < 0){
      down_i += LS_WALL_HEIGHT;
    }
    
    for (j =0; j < LS_WALL_WIDTH; j++){
      
      int up_j = j+1;
      if (up_j > LS_WALL_WIDTH){
	up_j -= LS_WALL_WIDTH;
      }   
      
      int down_j = j-1;
      if (down_j < 0){
	down_j += LS_WALL_WIDTH;
      }
      
      // play tournament

      int myNote = pointsA[i][j];
      float myScore = noteValues[myNote];
      int myAge = pointAge[i][j];
      
      int neighborNotes[4];

      if (drawPoints==0){
	
      	neighborNotes[0] = pointsA[up_i][j]%12;
	neighborNotes[1] = pointsA[down_i][j]%12;
	neighborNotes[2] = pointsA[i][up_j]%12;
	neighborNotes[3] = pointsA[i][down_j]%12;

      } else {

	neighborNotes[0] = pointsB[up_i][j]%12;
	neighborNotes[1] = pointsB[down_i][j]%12;
	neighborNotes[2] = pointsB[i][up_j]%12;
	neighborNotes[3] = pointsB[i][down_j]%12;
      }

      float highestScore = 0;
      int highestNote = 0;

      
      for (int k = 0; k < 4; k++){
	if (noteValues[neighborNotes[k]] > highestScore){
	  highestNote = neighborNotes[k];
	  highestScore = noteValues[highestNote];
	} 
      }
      
      int newNote = 0;
      int newAge = 0;

      if (highestScore > myScore){
	newNote = highestNote%12;
	newAge = 0;
      } else {
	newNote = myNote%12;
	newAge = myAge+1;
      }
      

      if (drawPoints == 0){
	pointsB[i][j] = newNote;
	pointAge[i][j] = newAge;
      } else {
	pointsA[i][j] = newNote;
	pointAge[i][j] = newAge;
      }
    }
  }  
  
  if (drawPoints == 1){
    drawPoints = 0;
  }else {
    drawPoints =  1;
  }
  
}



