//Use of L-systems
// Alphabet: 
//  F = move forward and draw a line,
//  f = move forward without drawing,
//  r = turn facing right
//  l = turn facing left
//  u = turn facing down
//  t = turn facing up
//  d = push
//  b = pop
var LSYSTEM_OLD = {
  stack : [],
  pushState : function(state){
    this.stack.push(state)
  },
  popState : function(){
    var state = this.stack.splice(this.stack.length-1, 1);
    return state[0];
  },
  
  path : [],
  baseStep : 0,
  angle : 0,
  rule : "",
  axiom : "",
  depth : 0,
  instructions : "",
  center : 0,
  limits: {},
  lines : undefined,
  
  toDegrees : function(angle){
    return angle * (180 / Math.PI);
  },

  toRadians : function(angle){
    return angle * (Math.PI / 180);
  },
  
  interpret : function(baseStep, angle, axiom, rule, depth){
    this.baseStep = baseStep;
    this.angle = angle;
    this.rule = rule;
    this.axiom = axiom;
    this.depth = depth;

    var rep = new RegExp('F', 'g');
    this.instructions = new String(this.axiom);
    
    for(var i = 0; i < this.depth; ++i){
      this.instructions = this.instructions.replace(rep, this.rule);
    }
    
    this.formPath();
  },
  
  findCenter : function(){
    var x = (this.limits.max.x + this.limits.min.x)/2;
    var y = (this.limits.max.y + this.limits.min.y)/2;
    var z = (this.limits.max.z + this.limits.min.z)/2;
    
    return {x: x, y: y, z: z};
  },
    
  findLimits : function(currentState){
    
    if(currentState.x > this.limits.max.x){
      this.limits.max.x = currentState.x;
    }
    if(currentState.y > this.limits.max.y){
      this.limits.max.y = currentState.y;
    }
    if(currentState.z > this.limits.max.z){
      this.limits.max.z = currentState.z;
    }
    if(currentState.x < this.limits.min.x){
      this.limits.min.x = currentState.x;
    }
    if(currentState.y < this.limits.min.y){
      this.limits.min.y = currentState.y;
    }
    if(currentState.z < this.limits.min.z){
      this.limits.min.z = currentState.z;
    }
  },
  
  moveForward : function(currentState){
    var posX = this.baseStep * Math.sin(this.toRadians(currentState.angleZ)) * Math.cos(this.toRadians(currentState.angleX));
    var posY = this.baseStep * Math.cos(this.toRadians(currentState.angleZ)) * Math.cos(this.toRadians(currentState.angleX));
    var posZ = this.baseStep * Math.sin(this.toRadians(currentState.angleX)) * Math.cos(this.toRadians(currentState.angleZ));
    currentState.x -=posX;
    currentState.y += posY;
    currentState.z +=posZ;
    
    return currentState;
  },
  
  formPath : function(){
    var currentState = {x: 0, y: 0, z: 0, angleZ: 0.0, angleX: 0.0};
    var baseStep = this.baseStep;
    
    this.limits = {
      max: { x: currentState.x, y: currentState.y, z: currentState.z},
      min: { x: currentState.x, y: currentState.y, z: currentState.z}
    };
    
    //forming the path to draw from instructions
    for(var i = 0; i < this.instructions.length; ++i){
      var letter = this.instructions[i];
      switch(letter){
        case 'd':
          this.pushState({
            x: currentState.x,
            y: currentState.y,
            z: currentState.z,
            angleZ: currentState.angleZ,
            angleX: currentState.angleX
          });
          break;
        case 'b':
          currentState = this.popState();
          break;
        case 'l':
          currentState.angleZ += this.angle;
          break;
        case 'r':
          currentState.angleZ -= this.angle;
          break;
        case 'u':
          currentState.angleX += this.angle;
          break;
        case 't':
          currentState.angleX -= this.angle;
          break;
        case 'F':
          var fromx = currentState.x;
          var fromy = currentState.y;
          var fromz = currentState.z;
          
          currentState = this.moveForward(currentState);
          
          this.path.push({
            fromx : fromx,
            fromy : fromy,
            fromz : fromz,
            tox: Math.round(currentState.x),
            toy: Math.round(currentState.y),
            toz: Math.round(currentState.z)
          });
          
          this.findLimits(currentState);
          break;
          
        case 'f':
          currentState = this.moveForward(currentState);
          this.findLimits(currentState);
          break;
      }
    }
    this.center = this.findCenter();
  },
  
  center : function(){
    for(var i = 0; i < this.path.length; ++i){
      var points = this.path[i];
      
      points.tox -=this.center;
      points.toy -=this.center;
      points.toz -=this.center;
      points.fromx -=this.center;
      points.fromy -=this.center;
      points.fromz -=this.center;
      
    }
    
    return this.path;
  },
  
  //At the moment line creation does not generate pauses to lines even if command f was used.
  createConnectedLine : function(lineMaterial){ 
    var geometry = new THREE.Geometry();
    geometry.dynamic = true;
    this.line = new THREE.Line( geometry, lineMaterial );
    
    geometry.vertices.push(
      new THREE.Vector3(this.path[0].fromx, this.path[0].fromy, this.path[0].fromz));
    
    for(var k = 0; k < this.path.length; ++k){
      var points = this.path[k];
      geometry.vertices.push(new THREE.Vector3(points.tox, points.toy, points.toz));
    }
    geometry.verticesNeedUpdate = true;
    this.line.updateMatrix();
    return this.line;
  },
  
};
