
let boid;
let chaser;
let boids = [];
let alignSlider;
let separateSlider;
let cohereSlider;
let alignForceSlider;
let separateForceSlider;
let cohereForceSlider;
let showSliders = false;

let defaultAlignForce = .1;
let defaultAlign = 25;
let defaultSeparateForce = .1;
let defaultSeparate = 20;
let defaultCohereForce = 0.3;
let defaultCohere = 60;
function setup() {

  let canvas = createCanvas(400, 400);
  let footer = document.querySelector('article > footer');
  let div = document.createElement('div');
  footer.parentNode.insertBefore(div, footer);

  let image = document.querySelector('.wp-post-image');
  if(image) {
    image.parentNode.removeChild(image);
  }
  canvas.parent(div);

  background(255);
  if(showSliders) {
    alignSlider = createSlider(0, 100, defaultAlign, 1);
    separateSlider = createSlider(0, 100, defaultSeparate, 1);
    cohereSlider = createSlider(0, 100, defaultCohere, 1);
    alignForceSlider = createSlider(0, 3, defaultAlignForce, .1);
    separateForceSlider = createSlider(0, 5, defaultSeparateForce, .1);
    cohereForceSlider = createSlider(0, 5, defaultCohereForce, .1);
  }
  for(let i = 0; i < 500; i++) {
    boids.push(new Boid(random(width), random(height)));
  }
}

function draw() {
  colorMode(HSB);
  background(220, 40, 240);
  stroke(20, 50, 50);
  strokeWeight(.5);
  if(showSliders) {
    text(alignSlider.value(), 5, 15);
    text(alignForceSlider.value() + ' align', 25, 15);
    text(separateSlider.value(), 5, 35);
    text(separateForceSlider.value() + ' separate', 25, 35);
    text(cohereSlider.value(), 5, 55);
    text(cohereForceSlider.value() + ' cohere', 25, 55);
  }
  strokeWeight(2);
  if(showSliders) {
    align(alignSlider.value(), alignForceSlider.value());
    separate(separateSlider.value(), separateForceSlider.value());
    cohere(cohereSlider.value(), cohereForceSlider.value());
  } else {
    align(defaultAlign, defaultAlignForce);
    separate(defaultSeparate, defaultSeparateForce);
    cohere(defaultCohere, defaultCohereForce);
  }
  avoid(20, 1);
  
  boids.forEach((boid) => {
    boid.update();
    boid.draw();
  });
  // fill('red');
  // push();
  //   translate(width/2, height/2);
  //   circle(target.x, target.y, 15);
  // pop();
}

function align(distance, force) {
  boids.forEach((boid) => {
    let targetVel = createVector(0,0);
    let count = 0;
    boids.forEach((aBoid) => {
      if(boid.pos.dist(aBoid.pos) < distance) {
        targetVel.add(aBoid.vel);
        count++;
      }
    });
    targetVel.div(count);
    targetVel.limit(force);
    boid.applyForce(targetVel);
  });
}

function separate(distance, force) {
  boids.forEach((boid) => {
    boids.forEach((aBoid) => {
        if(boid.pos.dist(aBoid.pos) < distance) {
          let direction = p5.Vector.sub(boid.pos, aBoid.pos);
          boid.applyForce(direction.limit(force));
        }
    });
  });
}

function cohere(dist, force) {
    boids.forEach((boid) => {
      let positions = createVector(0,0);
      let count = 0;
      boids.forEach((aBoid) => {
        if(boid.pos.dist(aBoid.pos) < dist && aBoid != boid) {
          positions.add(aBoid.pos);
          count++;
        }
      });
      if(count > 0) {
        let average = positions.div(count);
        let direction = p5.Vector.sub(average, boid.pos);
        boid.applyForce(direction.limit(force));
      }
  });
}

function avoid(dist, force) {
  boids.forEach((boid) => {
    if(boid.pos.x < dist) {
      boid.applyForce(createVector(force, 0));
    }
    if(boid.pos.x > width - dist) {
      boid.applyForce(createVector(-force, 0));
    }
    if(boid.pos.y < dist) {
      boid.applyForce(createVector(0, force));
    }
    if(boid.pos.y > height - dist) {
      boid.applyForce(createVector(0, -force));
    }
  });
}

class Boid {
  constructor(x, y) {
    this.pos = createVector(x, y);
    this.prevPos = createVector(x, y);
    this.vel = createVector(random(-1,1), random(-1,1));
    this.acc = createVector(0, 0);
  }
  
  applyForce(vector) {
    this.acc.add(vector);
  }
  
  update() {
    this.vel.add(this.acc);
    this.vel.limit(4);
    this.prevPos = createVector(this.pos.x, this.pos.y);
    this.pos.add(this.vel);
    this.acc = createVector(0,0);
    this.pos.x = this.pos.x > width ? 0 : this.pos.x;
    this.pos.x = this.pos.x < 0 ? width : this.pos.x;
    this.pos.y = this.pos.y > height ? 0 : this.pos.y;
    this.pos.y = this.pos.y < 0 ? height : this.pos.y;
    if(this.pos.x == 0 || this.pos.x == width || this.pos.y == 0 || this.pos.y == height) { this.prevPos.x = this.pos.x; this.prevPos.y = this.pos.y; }
  }
  
  draw() {
    let lightness = floor(map(this.vel.mag(), 0, 4, 0, 100));
    stroke(40, 50, lightness);
    line(this.prevPos.x, this.prevPos.y, this.pos.x, this.pos.y);
  }
}
