#include <FastLED.h>
#include <math.h>

// nombre de LEDs
#define NUM_LEDS 144
// pin sur lequel est connecté le fil data
#define DATA_PIN 3

// intensité des LEDs
#define BRIGHTNESS 20

#define MAX_RAND 1000

// densités initiales, > 1 pour l'initialisation
double rhom = 1.1 ;
double rhop = 1.1 ;

// probabilité pour la particule centrale (rouge) de sauter à droite
double pr = 0.5 ;

// pin pour la densité à gauche (potentiomètre gauche)
const int PinRhoL = A0;
int potPinRhoL ;

// pin pour la densité à droite (potentiomètre droit)
const int PinRhoR = A4;
int potPinRhoR ;


// pin pour le biais = proba d'aller à droite (potentiomètre central)
const int PinBias = A2;
int potPinBias ;

// tableau des couleurs de chaque LED
CRGB leds[NUM_LEDS];

// occupation numbers of each site
// the tracer does not occupy a site
int occup[NUM_LEDS];

int pos_tracer = NUM_LEDS/2;

// nb of particles
int npart = 0 ;

int pospart[NUM_LEDS] ;

// generate random numbers with exponential law
// this uses the cumulative
double RandExp(double lambda){

  double t = random(MAX_RAND)/(MAX_RAND+1.);

  return -log(1.-t)/lambda;
}

double Unif(){

  return random(MAX_RAND)/(MAX_RAND+1.);
}

void initAll() {

  npart = 0 ;
  pos_tracer = NUM_LEDS/2;

// initialise the system with density rhom on the left
  for(int i=0; i<pos_tracer; i++)
  {
    if(Unif() < rhom){
      occup[i] = 1 ;
    }
    else{
      occup[i] = 0 ;
    }
    // if site occupied, color on
    // else, dark
    leds[i] = CHSV(117, 255*occup[i], 127*occup[i]);
  
    pospart[npart] = i ;
    
    npart += occup[i] ;

  }

  pospart[npart] = pos_tracer ;
  npart += 1 ;
  occup[pos_tracer] = 0 ;

  // and density rhop on the right
  for(int i=pos_tracer+1; i<NUM_LEDS; i++)
  {
    if(Unif() < rhop){
      occup[i] = 1 ;
    }
    else{
      occup[i] = 0 ;
    }
    // if site occupied, color on
    // else, dark
    leds[i] = CHSV(117, 255*occup[i], 127*occup[i]);
    
    pospart[npart] = i ;
    
    npart += occup[i] ;
  }

  // light up the tracer
  leds[pos_tracer] = CHSV(0, 255, 127);

  // light up the series of LEDs
  FastLED.show();
  
}

void setup() {
  
  FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);

  // limit power usage
  FastLED.setMaxPowerInVoltsAndMilliamps(5,1000); 

  // set brightness
  FastLED.setBrightness(BRIGHTNESS);

  // initialise random number generator with the noise of pin 0
  randomSeed(analogRead(0));

  // initAll() ;
}

void loop() {

  potPinRhoL = analogRead(PinRhoL) ;
  double RhoL = map(potPinRhoL, 0, 1023, 0, 10)/10. ;

  potPinRhoR = analogRead(PinRhoR) ;
  double RhoR = map(potPinRhoR, 0, 1023, 0, 10)/10. ;
 
  potPinBias = analogRead(PinBias) ;
  double Bias = map(potPinBias, 0, 1023, 0, 5)/10. ;
  pr = 0.5 + Bias ;

  if( abs(RhoL - rhom) > 0.01 || abs(RhoR - rhop) > 0.01 ){
    rhom = RhoL ;
    rhop = RhoR ;
    initAll() ;
  }



  // pick the time to wait, in seconds
  // for the test, it is deterministic
  double dt = RandExp(npart/0.1) ;
  // convert to milliseconds

  int dT = (int) (1000*dt) ;

  delay(dT) ;

  

  // pick the particle to affect
  int part = random(npart);
  int pos = pospart[part] ;
  int new_pos ;

  // tracer or not ?
    if(pos == pos_tracer)
    {
      // compute the new position
      // initially, go to the left
      new_pos = (pos_tracer - 1 + NUM_LEDS) % NUM_LEDS ;
      // unless go to the right
      if(Unif() < pr){
        new_pos = (pos_tracer + 1) % NUM_LEDS ;
      }

      // check if new site is empty
      if(occup[new_pos] == 0)
      {
        leds[pos_tracer] = CRGB::Black;
        leds[new_pos] = CHSV(0, 255, 127);

        pos_tracer = new_pos ;
        pospart[part] = new_pos ;
      }
    }
    else
    {
      // new site to move, with periodic boundaries
      new_pos = (pos + (2*random(2) - 1) + NUM_LEDS) % NUM_LEDS ;

      // check if new site is empty
      if(occup[new_pos] == 0 && pos_tracer != new_pos)
      {
    
      leds[pos] = CRGB::Black;
      leds[new_pos] = CHSV(117, 255, 127);

      occup[pos] = 0 ;
      occup[new_pos] = 1 ;

      pospart[part] = new_pos ;
      }
    }
  
    // light up the series of LEDs
    FastLED.show();
  
  
}