class genome
{
  creature parent=null;
  gene strain[];
  int strainLength;
  String strainKey;
  genome(String filename,creature p)
  {
    parent=p;
    String lines[] = loadStrings(filename); 
    strainLength=parent.amount;
    strain=new gene[strainLength];     
    for(int i=0;i<strain.length;i++)
    {
      String parse[]=split(lines[2+i]);
      float repeat=Float.parseFloat(parse[0]);
      float heading=Float.parseFloat(parse[1]);
      float distance=Float.parseFloat(parse[2]);
      float neuronWeight=Float.parseFloat(parse[3]);
      float neuronPower=Float.parseFloat(parse[4]);
      boolean bidirectional=boolean(Float.parseFloat(parse[5]));
      float rkey=Float.parseFloat(parse[6]);
      strain[i]=new gene(i,int(repeat),int(heading),distance,neuronWeight,neuronPower,bidirectional,int(rkey));      
    } 
    for(int s=0;s<strain.length;s++)
    {
      String parse[]=split(lines[2+s]);    
      int child=int(Float.parseFloat(parse[7]));      
      strain[s].childID=child;
      if(child!=-1)
      {
        strain[s].child=strain[child];
        strain[s].child.parent=strain[s];
      }
    
    }
  }
  genome(genome g,creature p)
  {
    parent=p;
    strainLength=g.strainLength;
    strain=new gene[strainLength];
    //    System.arraycopy(g.strain,0,strain,0,strain.length);
    for(int i=0;i<strain.length;i++)
    {
      strain[i]=new gene(g.strain[i]);
    }
    strainKey=g.strainKey;
  }
  genome(int sl,creature p)
  {
    strainKey="";
    parent=p;
    strainLength=sl;
    strain=new gene[strainLength];
    for(int i=0;i<strainLength;i++)
    {

      //INITIAL CONDITIONS FOR RANDOM GENES
      float rkey=random(4);
      int repeat=int(random(parent.maxRepeat));
      float neuronWeight;
      if(random(100)>50)
        neuronWeight=1;
      else
        neuronWeight=-1;
      float neuronPower=random(0,2);
      boolean bidirectional=false;
      if(random(100)>50)
        bidirectional=true;
      float heading=random(-360,360);
      float distance=random(p.minDistance,p.cohesionDistance);
      strain[i]=new gene(i,repeat,int(heading),distance,neuronWeight,neuronPower,bidirectional,int(rkey));
      if(random(100)>30&&!strain[i].isLightSensor)
      {
        strain[i].isNeuron=false;
      }

    }
    int maxConnections=int(strainLength*.8);
    for(int s=0;s<maxConnections;s++)
    {
      int r=int(random(strainLength));
      do
      {
        r=int(random(strainLength));
        boolean repeat=true;
        gene temp;
        temp=new gene(strain[s]);
        temp.child=strain[r];
        do
        {
          if(temp.child.child==null)
          {
            repeat=false;
          }
          else
          {
            if(temp.child.child.id==s)
            {
              r=s;
              break;
            }
            temp.child=temp.child.child;
          }
        }
        while(repeat);
      }
      while(r==s);
      strain[s].child=strain[r];
      strain[s].childID=r;
      strain[s].child.parent=strain[s];
    }


  }
  void buildKey()
  {
    strainKey="";
    strainKey+="name @ ";    
    float pa=parent.amount;
    float pcd=parent.cohesionDistance;
    float pmd=parent.minDistance;
    float pcl=parent.collisionDistance;
    strainKey+=pa+" "+pcd+" "+pmd+" "+pcl+" @ ";
    for(int i=0;i<strain.length;i++)
    {
      int ci=strain[i].childID;    
      int r=strain[i].repeat;
      float h=strain[i].heading;
      int rkey=strain[i].rkey;
      float nw=strain[i].neuronWeight;
      float d=strain[i].distance;
      float np=strain[i].neuronPower;
      int bd=int(strain[i].bidirectional);                               
      strainKey+=r+" "+h+" "+d+" "+nw+" "+np+" "+bd+" "+rkey+" "+ci+" @ ";
//      strain[i]=new gene(i,repeat,int(heading),distance,neuronWeight,neuronPower,bidirectional,int(rkey));      
    }
//    println("Current Creature: "+strainKey);
  }
  gene returnChild(int id)
  {
    if(id>0&&id<strainLength)
    {
      return strain[id];
    }
    else return null;
  }
  void mutateAngle(gene g)
  {
    g.heading+=random(-6,6);
//    g.heading=random(360);
  }
  void mutateDistance(gene g)
  {
    g.distance+=random(-.1,.1);
    if(g.distance<parent.minDistance)
      g.distance=parent.minDistance;
    if(g.distance>parent.cohesionDistance);
      g.distance=parent.cohesionDistance-.1;
  }
  void mutateRepeat(gene g)
  {
    g.repeat=int(random(parent.maxRepeat));
  }
  void mutateKey(gene g)
  {
    g.rkey=int(random(4));
  }
  void mutateNeurons(gene g)
  {
    int rm=int(random(4));
    switch(rm)
    {
      case 0:
      if(random(100)>50)
        g.neuronWeight=1;
      else
        g.neuronWeight=-1;        
      break;
      case 1:
      if(random(100)>50)
        g.bidirectional=false;
      else
        g.bidirectional=true;
      break;      
      case 2:
        g.neuronPower=random(0,2); 
      break;
    }    
  }
  void mutateChild(gene g)
  {
    if(random(100)>50&&g!=strain[0])
    {
      g.child=null;
      g.childID=-1;
    }
    else
    {
      int rc=int(random(strainLength));

      if(strain[rc].child==null)
      {
        g.child=strain[rc];
        g.childID=rc;
      }        
    }
  }
  void deleteChild(gene g)
  {
    if(g.child==null||g==strain[0])
      return;
    else
    {

    }
  }
  void addGene()
  {
    strainLength++;
    gene temp[]=new gene[strainLength];
    System.arraycopy(strain,0,temp,0,strain.length);
      float rkey=random(4);
      int repeat=int(random(parent.maxRepeat));
      float neuronWeight;
      if(random(100)>50)
        neuronWeight=1;
      else
        neuronWeight=-1;
      float neuronPower=random(0,2);
      boolean bidirectional=false;
      if(random(100)>50)
        bidirectional=true;
      float heading=random(-360,360);
      float distance=random(parent.minDistance,parent.cohesionDistance);      
 
    temp[strainLength-1]=new gene(strainLength-1,repeat,int(heading),distance,neuronWeight,neuronPower,bidirectional,int(rkey));
      if(random(100)>30&&!temp[strainLength-1].isLightSensor)
      {
        temp[strainLength-1].isNeuron=false;
      }       
    strain=temp;
  }
  void garbageCollect()
  {
//    for
  }
  void mutateMorph()
  {
    int rg=int(random(strainLength));   

    while(strain[rg].usedInPhenotype==false)
    {
      rg=int(random(strainLength));
    }
    gene cg=strain[rg];    
    int rm=int(random(5));
    switch(rm)
    {
      case 0:
        mutateAngle(cg);      
      break;
      case 1:
        mutateDistance(cg);      
      break;
      case 2:
        mutateRepeat(cg);      
      break;
      case 3:
        mutateKey(cg);      
      break;
      case 4:
        for(int m=0;m<5;m++)
          mutateChild(cg);      
      break;
    }

//    if(random(100)>60)
//      addGene();
//    buildKey();
  }
  void mutateNS()
  {
    int rg=int(random(strainLength));
    gene cg=strain[rg];
    while(strain[rg].usedInPhenotype==false&&strain[rg].isNeuron==false)
    {
      rg=int(random(strainLength));
      cg=strain[rg];
    }   
    mutateNeurons(cg);    
  }
  void clearTags()
  {
    for(int r=0;r<strain.length;r++)
    {
      strain[r].usedInPhenotype=false;
    }    
  }
}