/*var seedSlider = document.getElementById('sliderRange')
seedSlider.onChange = function(){
	return this.value;
}

var citiesSlider = document.getElementById('nRange')
citiesSlider.onChange = function(){
	return this.value;
}*/

var Seed = 21//seedChange(11);
var totalCities = 25//citiesChange(9);
// Sketch One
var randomSearch = function( p ) { // p could be any variable name
  var cities = [];
  var recordDistance;
  var bestEver;
  var localSeed = Seed;
  var localCities = totalCities;
  p.setup = function() {
    p.createCanvas(400, 400);
    p.setupPoints(Seed, totalCities);
    }

    p.setupPoints = function(rngSeed, numCities){
    	p.randomSeed(rngSeed);
    	for (var i = 0; i < numCities; i++) {
    	var v = p.createVector(p.random(p.width), p.random(p.height/2));
    	cities[i] = v;
    }

    var d = p.calcDistance(cities);
    recordDistance = d;
    bestEver = cities.slice();
    }
  

  p.draw = function() {
  	if(localSeed != Seed){
  		p.setupPoints(Seed, localCities);
  		localSeed = Seed;
  	}
  	if(localCities != totalCities){
  		setupPoints(localSeed, totalCities);
  		localCities = totalCities;
  	}

    p.background(0);
    
    p.fill(255);
    p.stroke(255);
    for (var i = 0; i < cities.length; i++) {
      p.ellipse(cities[i].x, cities[i].y, 8, 8);
    }

  p.stroke(255, 0, 255);
  p.strokeWeight(4);
  p.noFill();
  p.beginShape();
  for (var i = 0; i < cities.length; i++) {
    p.vertex(bestEver[i].x, bestEver[i].y);
  }
  p.endShape();

  p.stroke(255);
  p.translate(0 , p.height/2);
  p.strokeWeight(1);
  p.noFill();
  p.beginShape();
  for (var i = 0; i < cities.length; i++) {
    p.vertex(cities[i].x, cities[i].y);
  }
  p.endShape();

  var i = p.floor(p.random(cities.length));
  var j = p.floor(p.random(cities.length));
  p.swap(cities, i, j);

  var d = p.calcDistance(cities);
  if (d < recordDistance) {
    recordDistance = d;
    bestEver = cities.slice();
  }
}

p.swap = function(a, i, j) {
  var temp = a[i];
  a[i] = a[j];
  a[j] = temp;
}


p.calcDistance = function(points) {
  var sum = 0;
  for (var i = 0; i < points.length - 1; i++) {
    var d = p.dist(points[i].x, points[i].y, points[i + 1].x, points[i + 1].y);
    sum += d;
  }
  return sum;
}
};

var myp5 = new p5(randomSearch, 'c1');

// Sketch Two
var lexicographicOrder = function( q ) { 
  var cities = [];
  var order = [];
  var totalPermutations;
  var count = 0;
  var recordDistance;
  var bestEver;

  q.setup = function() {
    q.randomSeed(Seed);
    q.createCanvas(400, 400);
    for (var i = 0; i < totalCities; i++) {
      var v = q.createVector(q.random(q.width), q.random(q.height / 2));
      cities[i] = v;
      order[i] = i;
    }

    var d = q.calcDistance(cities, order);
    recordDistance = d;
    bestEver = order.slice();
  }

  q.draw = function() {
    q.background(0);
  
    q.fill(255);
    for (var i = 0; i < cities.length; i++) {
      q.ellipse(cities[i].x, cities[i].y, 8, 8);
    }

    q.stroke(255, 0, 255);
    q.strokeWeight(4);
    q.noFill();
    q.beginShape();
    for (var i = 0; i < order.length; i++) {
      var n = bestEver[i];
      q.vertex(cities[n].x, cities[n].y);
    }
    q.endShape();


    q.translate(0, q.height / 2);
    q.stroke(255);
    q.strokeWeight(1);
    q.noFill();
    q.beginShape();
    for (var i = 0; i < order.length; i++) {
      var n = order[i];
      q.vertex(cities[n].x, cities[n].y);
    }
    q.endShape();

    var d = q.calcDistance(cities, order);
    if (d < recordDistance) {
      recordDistance = d;
      bestEver = order.slice();
    }
   
    q.nextOrder();
  }

  q.swap = function(a, i, j) {
    var temp = a[i];
    a[i] = a[j];
    a[j] = temp;
  }


q.calcDistance = function(points, order) {
  var sum = 0;
  for (var i = 0; i < order.length - 1; i++) {
    var cityAIndex = order[i];
    var cityA = points[cityAIndex];
    var cityBIndex = order[i + 1];
    var cityB = points[cityBIndex];
    var d = q.dist(cityA.x, cityA.y, cityB.x, cityB.y);
    sum += d;
  }
  return sum;
}

// This is my lexical order algorithm

  q.nextOrder = function() {

  // STEP 1 of the algorithm
  // https://www.quora.com/How-would-you-explain-an-algorithm-that-generates-permutations-using-lexicographic-ordering
  var largestI = -1;
  for (var i = 0; i < order.length - 1; i++) {
    if (order[i] < order[i + 1]) {
      largestI = i;
    }
  }
  if (largestI == -1) {
    q.noLoop();
  }

  // STEP 2
  var largestJ = -1;
  for (var j = 0; j < order.length; j++) {
    if (order[largestI] < order[j]) {
      largestJ = j;
    }
  }

  // STEP 3
  q.swap(order, largestI, largestJ);

  // STEP 4: reverse from largestI + 1 to the end
  var endArray = order.splice(largestI + 1);
  endArray.reverse();
  order = order.concat(endArray);
}

};
var myp5 = new p5(lexicographicOrder, 'c2');

var geneticSearch = function( p ) {
  var cities = [];
  var popSize = 500;
  var population = [];
  var fitness = [];
  var recordDistance = Infinity;
  var bestEver;
  var currentBest;
  var statusP;

  p.setup = function(){
    p.randomSeed(Seed);
    p.createCanvas(400, 400);
    var order = [];
    for (var i = 0; i < totalCities; i++) {
      var v = p.createVector(p.random(p.width), p.random(p.height / 2));
      cities[i] = v;
      order[i] = i;
    }

    for (var i = 0; i < popSize; i++) {
      population[i] = p.shuffle(order);
    }
    statusP = p.createP('').style('font-size', '32pt');
  }

  p.draw = function() {
  p.background(0);
  p.stroke(255);
  p.fill(255);
  for (var i = 0; i < cities.length; i++) {
      p.ellipse(cities[i].x, cities[i].y, 8, 8);
    }
  
  p.calculateFitness();
  p.normalizeFitness();
  p.nextGeneration();

  p.noFill();
  p.strokeWeight(4);
  p.beginShape();
  for (var i = 0; i < bestEver.length; i++) {
    var n = bestEver[i];
    p.stroke(255, 0, 255);
    p.vertex(cities[n].x, cities[n].y);
    //p.stroke(255);
    //p.ellipse(cities[n].x, cities[n].y, 8, 8);
  }
  p.endShape();

  p.translate(0, p.height / 2);
  p.stroke(255);
  p.strokeWeight(1);
  p.noFill();
  p.beginShape();
  for (var i = 0; i < currentBest.length; i++) {
    var n = currentBest[i];
    p.vertex(cities[n].x, cities[n].y);
    //p.ellipse(cities[n].x, cities[n].y, 8, 8);
  }
  p.endShape();
}

  p.swap = function(a, i, j) {
    var temp = a[i];
    a[i] = a[j];
    a[j] = temp;
  }

  p.calcDistance = function(points, order) {
    var sum = 0;
    for (var i = 0; i < order.length - 1; i++) {
      var cityAIndex = order[i];
      var cityA = points[cityAIndex];
      var cityBIndex = order[i + 1];
      var cityB = points[cityBIndex];
      var d = p.dist(cityA.x, cityA.y, cityB.x, cityB.y);
      sum += d;
    }
    return sum;
  }

  p.calculateFitness = function() {
  var currentRecord = Infinity;
  for (var i = 0; i < population.length; i++) {
    var d = p.calcDistance(cities, population[i]);
    if (d < recordDistance) {
      recordDistance = d;
      bestEver = population[i];
    }
    if (d < currentRecord) {
      currentRecord = d;
      currentBest = population[i];
    }

    fitness[i] = 1 / (p.pow(d, 8) + 1);
  }
}

  p.normalizeFitness = function() {
    var sum = 0;
    for (var i = 0; i < fitness.length; i++) {
      sum += fitness[i];
    }
    for (var i = 0; i < fitness.length; i++) {
      fitness[i] = fitness[i] / sum;;
    }
  }

p.nextGeneration = function() {
  var newPopulation = [];
  for (var i = 0; i < population.length; i++) {
    var orderA = p.pickOne(population, fitness);
    var orderB = p.pickOne(population, fitness);
    var order = p.crossOver(orderA, orderB);
    p.mutate(order, 0.01);
    newPopulation[i] = order;
  }
  population = newPopulation;

}

p.pickOne = function(list, prob) {
  var index = 0;
  var r = p.random(1);

  while (r > 0) {
    r = r - prob[index];
    index++;
  }
  index--;
  return list[index].slice();
}

  p.crossOver = function(orderA, orderB) {
    var start = p.floor(p.random(orderA.length));
    var end = p.floor(p.random(start + 1, orderA.length));
    var neworder = orderA.slice(start, end);
    // var left = totalCities - neworder.length;
    for (var i = 0; i < orderB.length; i++) {
      var city = orderB[i];
      if (!neworder.includes(city)) {
        neworder.push(city);
      }
    }
    return neworder;
  }


  p.mutate = function(order, mutationRate) {
    for (var i = 0; i < totalCities; i++) {
      if (p.random(1) < mutationRate) {
        var indexA = p.floor(p.random(order.length));
        var indexB = (indexA + 1) % totalCities;
        p.swap(order, indexA, indexB);
      }
    }
  }
}
var myp5 = new p5(geneticSearch, 'c3');
