/*
 *  VolumePainter.cpp
 *  lsystems_one
 *
 *  Created by lewhill2 on Fri Mar 12 2004.
 *  Copyright (c) 2004 __MyCompanyName__. All rights reserved.
 *
 */

#include "MidiVizVolumePainter.h"

MidiVizVolumePainter::MidiVizVolumePainter(){
  data = new MidiVizVolumePainterCell**[VOLUME_SIZE];
  for(int i = 0; i < VOLUME_SIZE; i++){
    data[i] = new MidiVizVolumePainterCell*[VOLUME_SIZE];
    for(int j = 0; j < VOLUME_SIZE; j++)
      data[i][j] = new MidiVizVolumePainterCell[VOLUME_SIZE];
  }
  
  size[0] = VOLUME_SIZE;
  size[1] = VOLUME_SIZE;
  size[2] = VOLUME_SIZE;
  cursor[0] = 0;
  cursor[1] = 0;
  cursor[2] = 0;
  color[0] = 1;
  color[1] = 1;
  color[2] = 1;
  
  autopilot = 1;
  

}

void MidiVizVolumePainter::setCursorPosition(int x, int y, int z){
	cursor[0] = x;
	cursor[1] = y;
	cursor[2] = z;
}

void MidiVizVolumePainter::moveCursor(int index, int distance){
	
	// 0 = x 
	// 1 = y
	// 2 = z

	cursor[index] += distance;
	if(cursor[index] >= size[index]){
		cursor[index] -= size[index];
	} else if (cursor[index] < 0){
		cursor[index] +=size[index];
	}

}

void MidiVizVolumePainter::moveCursorX(int distance){
	moveCursor(0, distance);
}

void MidiVizVolumePainter::moveCursorY(int distance){
	moveCursor(1, distance);
}

void MidiVizVolumePainter::moveCursorZ(int distance){
	moveCursor(2, distance);
}

void MidiVizVolumePainter::setColor(float r, float g, float b){
	color[0] = r;
	color[1] = g;
	color[2] = b;
}

void MidiVizVolumePainter::fillCell(){
	fillCell(cursor[0], cursor[1], cursor[2], 1);
}

void MidiVizVolumePainter::emptyCell(){
	fillCell(cursor[0], cursor[1], cursor[2], 1);
}


void MidiVizVolumePainter::fillCell(int x, int y, int z, int filled){
// must obey dimesnsions set for input
// filled 0 .. empty cell.
	// filled 1 ... fill cell
	
  /*	int drawX, drawY, drawZ;
	drawX = x % size[0];
	drawY = y % size[1];
	drawZ = z % size[2];
  */

	if (filled == 1){
		data[x][y][z].setFilled();
		data[x][y][z].setColor(color[0], color[1], color[2]);
	} else {
		data[x][y][z].setEmpty();
	}
}


void MidiVizVolumePainter::contextInit(){

  // generate box list if necessary

  if (contextData->firstTime == true){

    cout << "VolumePainter context initialzed." << endl;

    contextData->blockIndex = glGenLists(1);
    glNewList(contextData->blockIndex, GL_COMPILE);                      
    drawbox(.5, -.5, .5, -.5, .5, -.5, GL_QUADS);
    glEndList();

    contextData->firstTime = false;
  }

}

void MidiVizVolumePainter::draw(){
// draw iterates over the volume and draws where ever there is data

  
  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]);
  
  
  glTranslatef(-size[0]/2.0, -size[1]/2.0, -size[2]/2.0);
  
  for (int i = 0; i < size[0]; i++){
    for (int j = 0; j < size[1]; j++){
      for (int k = 0; k < size[2]; k++){
	if (data[i][j][k].isOccupied()){
	  drawCellObject(i,j,k);
	}
      }
    }
  }
  
  glColor3f(.5, .5, .5);
  
  // draw grid around space
  
  glBegin(GL_LINE_LOOP);
  glVertex3f(0, 0, 0);
  glVertex3f(size[0], 0, 0);
  glVertex3f(size[0], size[1], 0);
  glVertex3f(0, size[1], 0);
  glEnd();
  
  glBegin(GL_LINE_LOOP);
  glVertex3f(0, 0, size[2]);
  glVertex3f(size[0], 0, size[2]);
  glVertex3f(size[0], size[1], size[2]);
  glVertex3f(0, size[1], size[2]);
  glEnd();
  
  glBegin(GL_LINES);
  glVertex3f(0, 0, 0);
  glVertex3f(0, 0, size[2]);
  glVertex3f(size[0], 0, 0);
  glVertex3f(size[0], 0, size[2]);
  glVertex3f(size[0], size[1], 0);
  glVertex3f(size[0], size[1], size[2]);
  glVertex3f(0, size[1], 0);
  glVertex3f(0, size[1], size[2]);
  glEnd();
  
  
  // draw color axis
  
  glColor3fv(color);
  
  glBegin(GL_LINES);
  
  glVertex3f(cursor[0], cursor[1], 0);
  glVertex3f(cursor[0], cursor[1], size[2]);
  
  glVertex3f(cursor[0], 0, cursor[2]);
  glVertex3f(cursor[0], size[1], cursor[2]);
  
  glVertex3f(0, cursor[1], cursor[2]);
  glVertex3f(size[0], cursor[1], cursor[2]);
  
  glEnd();
  
  glPopMatrix();
  
}

void MidiVizVolumePainter::drawCellObject(int x, int y, int z){
  // this cell occupies a unit square centered around x,y,z
  
  glColor3fv(data[x][y][z].getColor());
  
  glPushMatrix();
  
  /*
  // draw trace on x side,
  glBegin(GL_POLYGON);
  glNormal3f(1,0,0);
  glVertex3f(0, y+0.5, z+0.5);
  glVertex3f(0, y-0.5, z+0.5);
  glVertex3f(0, y-0.5, z-0.5);
  glVertex3f(0, y+0.5, z-0.5);
  glEnd();
  glBegin(GL_POLYGON);
  glNormal3f(-1,0,0);
  glVertex3f(size[0], y+0.5, z+0.5);
  glVertex3f(size[0], y-0.5, z+0.5);
  glVertex3f(size[0], y-0.5, z-0.5);
  glVertex3f(size[0], y+0.5, z-0.5);
  glEnd();
  
  
  // draw trace on y side,
  glBegin(GL_POLYGON);
  glNormal3f(0,1,0);
  glVertex3f(x+0.5, 0, z+0.5);
  glVertex3f(x+0.5, 0, z-0.5);
  glVertex3f(x-0.5, 0, z-0.5);
  glVertex3f(x-0.5, 0, z+0.5);
  glEnd();
  glBegin(GL_POLYGON);
  glNormal3f(0,-1,0);
  glVertex3f(x+0.5, size[1], z+0.5);
  glVertex3f(x-0.5, size[1], z+0.5);
  glVertex3f(x-0.5, size[1], z-0.5);
  glVertex3f(x+0.5, size[1], z-0.5);
  glEnd();
  
  // draw trace on z side,
  
  glBegin(GL_POLYGON);
  glNormal3f(0,0,1);
  glVertex3f(x+0.5, y+0.5, size[2]);
  glVertex3f(x-0.5, y+0.5, size[2]);
  glVertex3f(x-0.5, y-0.5, size[2]);
  glVertex3f(x+0.5, y-0.5, size[2]);
  glEnd();
  glBegin(GL_POLYGON);
  glNormal3f(0,0,-1);
  glVertex3f(x+0.5, y+0.5, 0);
  glVertex3f(x-0.5, y+0.5, 0);
  glVertex3f(x-0.5, y-0.5, 0);
  glVertex3f(x+0.5, y-0.5, 0);
  glEnd();
  
  */

  // draw cell box	
  glTranslatef(x, y, z);
  
  glCallList(contextData->blockIndex);
  
  
  // left over planar code draws three planes X,Y,Z
  /*
    glBegin(GL_TRIANGLE_STRIP);
    glNormal3f(0,0,1);
    glVertex3f(0.5,  0.5, 0.0);
    glVertex3f(0.5, -0.5, 0.0);
    glVertex3f(-0.5, -0.5, 0.0);
    glVertex3f(-0.5, 0.5,  0.0);
    glEnd();
    
    glBegin(GL_TRIANGLE_STRIP);
    glNormal3f(0,1,0);
    glVertex3f(0.5, 0.0, 0.5);
    glVertex3f(0.5, 0.0, -0.5);
    glVertex3f(-0.5, 0.0, -0.5);
    glVertex3f(-0.5, 0.0, 0.5);
    glEnd();
    
    glBegin(GL_TRIANGLE_STRIP);
    glNormal3f(1,0,0);
    glVertex3f(0.0, 0.5, 0.5);
    glVertex3f(0.0, 0.5, -0.5);
    glVertex3f(0.0, -0.5, -0.5);
    glVertex3f(0.0, -0.5, 0.5);
    glEnd();
  */
  glPopMatrix();
  
}


void MidiVizVolumePainter::processMidiEvent(MidiEvent event){
  
  int note = event.getNote();
  int intensity = event.getVelocity();
  
  noteValues[note%12] = intensity;	
  
  updateColor();
  
  if (autopilot && (intensity > 0)){
    doAutomation(note, intensity);
    fillCell();
  }
  
}

void MidiVizVolumePainter::setAutopilot(int state){
	autopilot = state;
}

void MidiVizVolumePainter::doAutomation(int note, float intensity){
	
	if (note %6 ==0)
		moveCursorX(1);
	else if (note %6 ==1)
		moveCursorX(-1);
	else if (note %6 ==2)
		moveCursorY(1);
	else if (note %6 ==3)
		moveCursorY(-1);
	else if (note %6 ==4)
		moveCursorZ(1);
	else if (note %6 ==5)
		moveCursorZ(-1);
	
}

void MidiVizVolumePainter::updateColor(){
// set the current color to the weighted average of the musical note settings

	float colorSum[3];
	colorSum[0] = 0;
	colorSum[1] = 0;
	colorSum[2] = 0;
	
	float weightTotal = 0;
	float* note_color;
	
	for (int i = 0; i < 12; i++){
		if(noteValues[i] > .01){
			note_color= note_color_table.getValue(i);
			colorSum[0] += note_color[0]*noteValues[i];
			colorSum[1] += note_color[1]*noteValues[i];
			colorSum[2] += note_color[2]*noteValues[i];
			weightTotal += noteValues[i];
		}
	}
	
	if (weightTotal != 0){
		color[0] = colorSum[0]/weightTotal;
		color[1] = colorSum[1]/weightTotal;
		color[2] = colorSum[2]/weightTotal;
	} else {
		color[0] = 1.0;
		color[1] = 1.0;
		color[2] = 1.0;
	}
}


void MidiVizVolumePainter::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();
	}
}


