class synapse
{
  neuron n;
  float strength=0;    

  synapse(neuron n)
  {
    this.n=n;
    strength=0;
  }
  synapse(neuron n,float s)
  {
    this.n=n;
    strength=s;
  }
}

class signal
{
  float amount=0;
  float weight=0;
  signal(float a,float w)
  {
    amount=a;
    weight=w;
  }
}

class neuron
{
  int id;                              //the id tag of a neuron

  synapse input[];                     //a list of inputs
  synapse output[];                    //a list of outputs
  float refractMax=3;                  //the refractory rate of a neuron
  float refract=0;                     //the current refraction
  
  float weight;                        //the weight of a node used when calculating output
  float recievedWeight=0;              //the recieved weight from incoming nodes
  float accumulated=-1;                //the accumulation of signals from outputs of other cells to this cell
  float threshold=0;                   //the threshold at which, when passed, this cell will fire to its outputs
  float threshMax=.8;                  //the maximum the threshold can move up (fatigue)
  float threshMin=-.3;                 //the minimum threshold it can move down (sensitive)
  float transmission=.2;               //the size of signal that the neuron sends out
  boolean bidirectional=false;  
  boolean isLightSensor=false;  
  boolean isMuscle=false;
    
  boolean firing=false;
  
  
  
  neuron(int id,float w,float pw)
  {
    this.id=id;
    input=new synapse[0];
    output=new synapse[0];    
    weight=w;
    transmission=pw;
//    accumulated=2;
/*    if(random(100)>50)
      weight=-1;
    else
      weight=1;*/
  }  
  neuron(int id,position p)
  {
    this.id=id;
    input=new synapse[0];
    output=new synapse[0];    
//    accumulated=2;    
//    this.p=new position(p);
    
    if(random(100)>50)
      weight=-1;
    else
      weight=1;
  }
  void update()
  {   
    if(refract>0)
      refract-=1;    
    if(accumulated>threshold && refract==0 && recievedWeight!=0)
    {      
      firing=true;
    }
    else
    {
      firing=false;    
    }  
    accumulated-=.02;
    accumulated=constrain(accumulated,-1,1);
    threshold*=.99;       
    if(threshold<.1)
      threshold-=.005;
    threshold=constrain(threshold,threshMin,threshMax);         
    recievedWeight=0; 
  }
  void updateFire()
  {
    if(firing)
    {
      signal s=new signal(transmission,weight);
      pulse(s);    
      refract=refractMax; 
      threshold=threshMax;
      recievedWeight=0;             
      accumulated=-1;   
    }
       
  }
  void pulse()
  {
    signal s=new signal(transmission,1);
    for(int i=0;i<output.length;i++)
    {
      send(s,output[i].n);
    }
  }
  void pulse(signal s)
  {
    for(int i=0;i<output.length;i++)
    {
      send(s,output[i].n);
    }    
  }
  void send(signal s,neuron n)
  {
//    if(!n.isLightSensor)
      n.recieve(s); 
  }
  void recieve(signal s)
  {
    accumulated+=s.amount;
    recievedWeight+=s.weight;  
  }
  void addInput(neuron n)
  {
    int newAmount=input.length+1;
    synapse temp[]=new synapse[newAmount];
    System.arraycopy(input,0,temp,0,input.length);
    temp[input.length]=new synapse(n);
    input=temp;
  }
  void addOutput(neuron n)
  {
    boolean repeat=false;
    for(int i=0;i<output.length;i++)
    {
      if(output[i].n==n)
        repeat=true;
    }
    if(!repeat)
    {
      int newAmount=output.length+1;
      synapse temp[]=new synapse[newAmount];
      System.arraycopy(output,0,temp,0,output.length);
      temp[output.length]=new synapse(n);
      output=temp;     
      n.addInput(this); 
    }
  }
}