/*
 *  chill_fractal.cpp
 *  lsystems_one
 *
 *  Created by lewhill2 on Sat Feb 21 2004.
 *  Copyright (c) 2004 __MyCompanyName__. All rights reserved.
 *
 */

#include "TreeFractal.h"

TreeFractal::TreeFractal(){
	setDefaultRotationAnglesAndVectors();
	setConfiguration(0);
}

TreeFractal::TreeFractal(int in_mode){
	setDefaultRotationAnglesAndVectors();
	setConfiguration(in_mode);
}

TreeFractal::~TreeFractal(){
}

void TreeFractal::setDefaultRotationAnglesAndVectors(){
	setRotationAngleAndVector('+', 20, 1,0,0);
	setRotationAngleAndVector('-', 20, 1,0,0);
	setRotationAngleAndVector('(', 20, 0,1,0);
	setRotationAngleAndVector(')', 20, 0,1,0);
	setRotationAngleAndVector('*', 20, 0,0,1);
	setRotationAngleAndVector('&', 20, 0,0,1);
}


void TreeFractal::setInitiator(string in_initiator){
	initiator = in_initiator;
}

void TreeFractal::addRule(char in_generator_key, string in_generator){
	FractalRule* newRule = new FractalRule(in_generator_key, in_generator);
	ruleList.push_back(newRule);
}

void TreeFractal::clearRules(){

	for (ruleListIter=ruleList.begin();  ruleListIter!= ruleList.end(); ruleListIter++){
		FractalRule* frule = (FractalRule*)(*ruleListIter);
		frule->~FractalRule();
	}
	ruleList.clear();
}

void TreeFractal::printRules(){
	// Iterate through list and output each element.
	for (ruleListIter=ruleList.begin();  ruleListIter!= ruleList.end(); ruleListIter++){
		FractalRule* frule = (FractalRule*)(*ruleListIter);
		cout << frule->getKey() << " " << frule->getRule() << endl;
	}
}

int TreeFractal::getNumGenerations(){
  return generationsPerformed;
}

void TreeFractal::doGenerations(){
  
  fractal = initiator;
  generationsPerformed = 0;
  
  while(generationsPerformed < doNumGenerations){
    doOneGeneration();
    generationsPerformed++;
  }

  //  cout << "Fractal is: " << fractal << endl;
}

void TreeFractal::playNote(int note, float intensity){
	noteValues[note%12] = intensity;
}

void TreeFractal::requestUpdate(){
	newData = 1;
}

void TreeFractal::setConfiguration(int mode){
  clearRules();
  
  switch (mode){
    
  case 1:
    
    // basic tree
    cout << "Basic Tree" << endl;
    setInitiator ("F[+F]F[-F]L");
    addRule('F', "F[+F]F[-F]F");
    doNumGenerations = 3;
    
    setRotationAngleAndVector('+', 22.5, 1,0,0);
    setRotationAngleAndVector('-', 22.5, 1,0,0);
    setRotationAngleAndVector('(', 22.5, 0,1,0);
    setRotationAngleAndVector(')', 22.5, 0,1,0);
    setRotationAngleAndVector('*', 22.5, 0,0,1);
    setRotationAngleAndVector('&', 22.5, 0,0,1);
    break;
    
  case 2:
    
    // basic tree
    cout << "blades tree Config" << endl;
    setInitiator ("F");
    addRule('F', "FF[-F+F+F]+[+F-F-F]");
    doNumGenerations = 3;
    setRotationAngleAndVector('+', 22.5, 1,0,0);
    setRotationAngleAndVector('-', 22.5, 1,0,0);
    setRotationAngleAndVector('(', 22.5, 0,1,0);
    setRotationAngleAndVector(')', 22.5, 0,1,0);
    setRotationAngleAndVector('*', 22.5, 0,0,1);
    setRotationAngleAndVector('&', 22.5, 0,0,1);
    break;
    
  case 0:
  default:
    cout << "Default Tree" << endl;
    setInitiator ("F");
    addRule('F', "F[+(FL][-(FL][&FL]L");
    setRotationAngleAndVector('+', 25, 1,0,0);
    setRotationAngleAndVector('-', 25, 1,0,0);
    setRotationAngleAndVector('(', 25, 0,1,0);
    setRotationAngleAndVector(')', 25, 0,1,0);
    setRotationAngleAndVector('*', 25, 0,0,1);
    setRotationAngleAndVector('&', 25, 0,0,1);
    doNumGenerations = 3;
    break;
  }
  
  //  printRules();
  doGenerations();
  generateAngles();
  
}

void TreeFractal::generateAngles(){
  
  fractalAngleIndex = new int[fractal.length()];
  randomAngleIndex = new float[fractal.length()];
  
  //	srand(glutGet(GLUT_ELAPSED_TIME));
  
  int angle_number = rand()%12;
  
  for (unsigned i = 0; i < fractal.length(); i++){
    if ((fractal[i] == '+') ||
	(fractal[i] == '-') ||
	(fractal[i] == '(') ||
	(fractal[i] == ')') ||
	(fractal[i] == '&') ||
	(fractal[i] == '*')){
      
      fractalAngleIndex[i] = angle_number;
      angle_number = (angle_number + rand())%12;
    } else {
      fractalAngleIndex[i] = 0;
    }
  }
  
}

void TreeFractal::doOneGeneration(){
  
  //  cout << "doOneGeneration " << endl;
  
  int ruleApplied = 0;
  
  string nextFractal;
  
  // iterate over the each character 
  for (unsigned i = 0; i < fractal.length(); i++){
    
    ruleApplied = 0;
    
    // iterate over the ruleset
    for (ruleListIter=ruleList.begin();  ruleListIter!= ruleList.end(); ruleListIter++){
      FractalRule* frule = (FractalRule*)(*ruleListIter);
      
      if (fractal[i] == frule->getKey()){
	nextFractal += frule->getRule();
	ruleApplied = 1;
      } 
    }
    
    // if none of the rules match, copy this letter to the new fractal.
    if (ruleApplied != 1){
      nextFractal += fractal[i];
      ruleApplied = 1;
    }
  }  
  
  fractal = nextFractal;
  
  
}


void TreeFractal::frame(){
  
  for (int i = 0; i < 12; i++){
    angles[i] +=noteValues[i]*4.0;
    angles[i] *= .80;
  }
  
}

void TreeFractal::setAttColor(int angleIndex){
  
  if (noteValues[angleIndex] > .05){
    float* color= note_color_table.getValue(angleIndex%12);
    attColor[0] = ((1-noteValues[angleIndex]) + noteValues[angleIndex] * color[0]);
    attColor[1] = ((1-noteValues[angleIndex]) + noteValues[angleIndex] * color[1]);
    attColor[2] = ((1-noteValues[angleIndex]) + noteValues[angleIndex] * color[2]);
    
    glColor3fv(attColor);
  }
  
}

void TreeFractal::drawFlower(){
  spiralFlower.draw();
}

void TreeFractal::drawLeaf(){
  glBegin(GL_POLYGON);
  glVertex3f(.05, 0, 0);
  glVertex3f(0, .05, 0);
  glVertex3f(.05, .05, 0);
  glEnd();
}

void TreeFractal::setRotationAngleAndVector(char in_symbol, float in_angle, float x, float y, float z){
  setRotationAngle(in_symbol, in_angle);
  setRotationVector(in_symbol, x, y, z);
}

void TreeFractal::setRotationAngle(char symbol, float value){
  switch(symbol){
  case('+'):
    rotAngles[0] = value;
    break;
  case('-'):
    rotAngles[1] = value;
    break;
  case('('):
    rotAngles[2] = value;
    break;
  case(')'):
    rotAngles[3] = value;
    break;
  case('*'):
    rotAngles[4] = value;
    break;
  case('&'):
    rotAngles[5] = value;
    break;
  default:
    cout << "Unknown angle " << endl;
    break;
  }
}

void TreeFractal::setRotationVector(char symbol, float x, float y, float z){
  switch(symbol){
    
  case('+'):
    rotVectors[0][0] = x;
    rotVectors[0][1] = y;
    rotVectors[0][2] = z;
    break;
    
  case('-'):
    rotVectors[1][0] = x;
    rotVectors[1][1] = y;
    rotVectors[1][2] = z;
    break;
    
  case('('):
    rotVectors[2][0] = x;
    rotVectors[2][1] = y;
    rotVectors[2][2] = z;
    break;
    
  case(')'):
    rotVectors[3][0] = x;
    rotVectors[3][1] = y;
    rotVectors[3][2] = z;
    break;
    
  case('*'):
    rotVectors[4][0] = x;
    rotVectors[4][1] = y;
    rotVectors[4][2] = z;
    break;
    
  case('&'):
    rotVectors[5][0] = x;
    rotVectors[5][1] = y;
    rotVectors[5][2] = z;
    break;
    
  default:
    break;
  }
}


void TreeFractal::draw(){
  
  int stackdepth = 1;
  float baseScale = .04;
  float scaledTravelDistance = 1;
  int travelDistance =2;
  float scaledWidth = baseScale;
  int iterAngle = 0;
  float turnAngle;

  glLineWidth(1);
  glColor3f(1.0, 1.0, 1.0);
   
  glPushMatrix();

  for (unsigned i = 0; i < fractal.length(); i++){

    switch (fractal[i]){
      
    case 'F':

      glBegin(GL_POLYGON);
      glNormal3f(0, 0, 1);
      glVertex3f(scaledWidth, 0.0, 0.0);
      glVertex3f(scaledWidth, scaledTravelDistance, 0.0);
      glVertex3f(-scaledWidth, scaledTravelDistance, 0.0);
      glVertex3f(-scaledWidth, 0.0, 0.0);
      glEnd();
      
      glTranslatef(0, scaledTravelDistance, 0);

      break;
      
    case '+': // about z axis
      iterAngle = fractalAngleIndex[i];
      turnAngle = rotAngles[0]+angles[iterAngle];
      glRotatef(turnAngle, rotVectors[0][0], rotVectors[0][1], rotVectors[0][2]);
      setAttColor(iterAngle);
      break;
      
    case '-': // reverse about z axis
      iterAngle = fractalAngleIndex[i];
      turnAngle = rotAngles[1]+angles[iterAngle];
      glRotatef(-turnAngle, rotVectors[1][0], rotVectors[1][1], rotVectors[1][2]);
      setAttColor(iterAngle);
      break;
      
    case '(': // about Y axis
      iterAngle = fractalAngleIndex[i];
      turnAngle = rotAngles[2]+angles[iterAngle];
      glRotatef(turnAngle, rotVectors[2][0], rotVectors[2][1], rotVectors[2][2]);
      setAttColor(iterAngle);
      break;
      
    case ')': // reverse about Y axis
      iterAngle = fractalAngleIndex[i];
      turnAngle = rotAngles[3]+angles[iterAngle];
      glRotatef(-turnAngle, rotVectors[3][0], rotVectors[3][1], rotVectors[3][2]);
      setAttColor(iterAngle);
      break;
      
    case '*':  // about x axis
      iterAngle = fractalAngleIndex[i];
      turnAngle = rotAngles[4]+angles[iterAngle];
      glRotatef(turnAngle, rotVectors[4][0], rotVectors[4][1], rotVectors[4][2]);
      setAttColor(iterAngle);
      break;
      
    case '&': // reverse about x axis
      iterAngle = fractalAngleIndex[i];
      turnAngle = rotAngles[5]+angles[iterAngle];
      glRotatef(-turnAngle, rotVectors[5][0], rotVectors[5][1], rotVectors[5][2]);
      setAttColor(iterAngle);
      break;
      
    case '[':
      stackdepth++;
      scaledTravelDistance = travelDistance/pow(1.3,stackdepth);
      //      cout << "scaledTravelDistance = " << scaledTravelDistance << endl;
      scaledWidth = baseScale/pow(1.3, stackdepth);
      glPushMatrix();
      break;
      
    case ']':
      stackdepth--;
      scaledTravelDistance = travelDistance/pow(1.3,stackdepth);
      //cout << "scaledTravelDistance = " << scaledTravelDistance << endl;
      scaledWidth = baseScale/pow(1.3, stackdepth);
      glPopMatrix();
      break;
      
    case 'L':
      glPushMatrix();
      drawFlower();
      glPopMatrix();
      break;
      
    case 'E':
      glPushMatrix();
      drawLeaf();
      glPopMatrix();
      break;
      
      case'?':
	glRotatef(randomAngleIndex[i], randomAngleIndex[i]+512, 
		  randomAngleIndex[i]+256, randomAngleIndex[i]*128);
      
    default:
      //				cout << "no render rule for " << fractal[i] << endl;
      break;
    }
  }
  
  glPopMatrix();
}

