/**
 * Particle Network Animation https://codepen.io/franky/pen/LGMWPK
 * Inspiration: https://github.com/JulianLaval/canvas-particle-network
 */

class ParticleNetworkAnimation {
  constructor(element, options) {
    this.$el = element;

    this.container = element;
    //this.canvas = document.createElement('canvas');
    this.canvas = element;
    this.sizeCanvas();
    //this.container.appendChild(this.canvas);
    this.ctx = this.canvas.getContext('2d');
    this.particleNetwork = new ParticleNetwork(this, options);

    this.bindUiActions();

    return this;
  }

  bindUiActions() {
    window.addEventListener('resize', () => {
      this.forceUpdate();
    });
  }
  sizeCanvas() {
    this.canvas.width = this.container.offsetWidth;
    this.canvas.height = this.container.offsetHeight;
  }
  forceUpdate() {
    this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
    this.sizeCanvas();
    this.particleNetwork.createParticles();
  }
}

class Particle{
  constructor(parent, x, y) {
    this.network = parent;
    this.canvas = parent.canvas;
    this.ctx = parent.ctx;
    this.particleColor = returnRandomArrayitem(this.network.options.particleColors);
    this.radius = getLimitedRandom(1.5, 2.5);
    this.opacity = 0;
    this.x = x || Math.random() * this.canvas.width;
    this.y = y || Math.random() * this.canvas.height;
    this.velocity = {
      x: (Math.random() - 0.5) * parent.options.velocity,
      y: (Math.random() - 0.5) * parent.options.velocity
    };
  }

  update(){
    if (this.opacity < 1) {
      this.opacity += 0.01;
    } else {
      this.opacity = 1;
    }
    // Change dir if outside map
    if (this.x > this.canvas.width + 100 || this.x < -100) {
      this.velocity.x = -this.velocity.x;
    }
    if (this.y > this.canvas.height + 100 || this.y < -100) {
      this.velocity.y = -this.velocity.y;
    }

    // Update position
    this.x += this.velocity.x;
    this.y += this.velocity.y;
  }
  draw(){
    // Draw particle
    this.ctx.beginPath();
    this.ctx.fillStyle = this.particleColor;
    this.ctx.globalAlpha = this.opacity;
    this.ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
    this.ctx.fill();
  }

}

class ParticleNetwork{
  constructor(parent, options = {
    velocity: 2, // the higher the faster
    density: 10000, // the lower the denser
    netLineDistance: 200,
    netLineColor: '#929292',
    particleColors: ['#aaa'] // ['#6D4E5C', '#aaa', '#FFC458' ]
  }) {
    this.options = options;
    this.canvas = parent.canvas;
    this.ctx = parent.ctx;

    this.init();
  }

  init(){
    // Create particle objects
    this.createParticles(false);

    // Update canvas
    this.animationFrame = requestAnimationFrame(this.update.bind(this));

    this.bindUiActions();
  }

  createParticles(isInitial) {
    // Initialise / reset particles
    let me = this;
    this.particles = [];
    let quantity = this.canvas.width * this.canvas.height / this.options.density;

    if (isInitial) {
      let counter = 0;
      clearInterval(this.createIntervalId);
      this.createIntervalId = setInterval(function() {
        if (counter < quantity - 1) {
          // Create particle object
          this.particles.push(new Particle(this));
        }
        else {
          clearInterval(me.createIntervalId);
        }
        counter++;
      }.bind(this), 250);
    }
    else {
      // Create particle objects
      for (let i = 0; i < quantity; i++) {
        this.particles.push(new Particle(this));
      }
    }
  }

  createInteractionParticle() {
    // Add interaction particle
    this.interactionParticle = new Particle(this);
    this.interactionParticle.velocity = {
      x: 0,
      y: 0
    };
    this.particles.push(this.interactionParticle);
    return this.interactionParticle;
  }

  removeInteractionParticle() {
    // Find it
    let index = this.particles.indexOf(this.interactionParticle);
    if (index > -1) {
      // Remove it
      this.interactionParticle = undefined;
      this.particles.splice(index, 1);
    }
  }

  update() {
    if (this.canvas) {

      this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
      this.ctx.globalAlpha = 1;

      // Draw connections
      for (let i = 0; i < this.particles.length; i++) {
        for (let j = this.particles.length - 1; j > i; j--) {
          let distance, p1 = this.particles[i], p2 = this.particles[j];

          // check very simply if the two points are even a candidate for further measurements
          distance = Math.min(Math.abs(p1.x - p2.x), Math.abs(p1.y - p2.y));
          if (distance > this.options.netLineDistance) {
            continue;
          }

          // the two points seem close enough, now let's measure precisely
          distance = Math.sqrt(
            Math.pow(p1.x - p2.x, 2) +
            Math.pow(p1.y - p2.y, 2)
          );
          if (distance > this.options.netLineDistance) {
            continue;
          }

          this.ctx.beginPath();
          this.ctx.strokeStyle = this.options.netLineColor;
          this.ctx.globalAlpha = (this.options.netLineDistance - distance) / this.options.netLineDistance * p1.opacity * p2.opacity;
          this.ctx.lineWidth = 0.7;
          this.ctx.moveTo(p1.x, p1.y);
          this.ctx.lineTo(p2.x, p2.y);
          this.ctx.stroke();
        }
      }

      // Draw particles
      this.particles.forEach(k => {
        k.update();
        k.draw();
      })

      if (this.options.velocity !== 0) {
        this.animationFrame = requestAnimationFrame(this.update.bind(this));
      }

    }
    else {
      cancelAnimationFrame(this.animationFrame);
    }
  }

  bindUiActions() {
    // Mouse / touch event handling
    this.spawnQuantity = 3;
    this.mouseIsDown = false;
    this.touchIsMoving = false;

    this.onMouseMove = function(e) {
      if (!this.interactionParticle) {
        this.createInteractionParticle();
      }
      this.interactionParticle.x = e.offsetX;
      this.interactionParticle.y = e.offsetY;
    }.bind(this);

    this.onTouchMove = function(e) {
      e.preventDefault();
      this.touchIsMoving = true;
      if (!this.interactionParticle) {
        this.createInteractionParticle();
      }
      this.interactionParticle.x = e.changedTouches[0].clientX;
      this.interactionParticle.y = e.changedTouches[0].clientY;
    }.bind(this);

    this.onMouseDown = function() {
      this.mouseIsDown = true;
      let counter = 0;
      let quantity = this.spawnQuantity;
      let intervalId = setInterval(function() {
        if (this.mouseIsDown) {
          if (counter === 1) {
            quantity = 1;
          }
          for (let i = 0; i < quantity; i++) {
            if (this.interactionParticle) {
              this.particles.push(new Particle(this, this.interactionParticle.x, this.interactionParticle.y));
            }
          }
        }
        else {
          clearInterval(intervalId);
        }
        counter++;
      }.bind(this), 50);
    }.bind(this);

    this.onTouchStart = function(e) {
      e.preventDefault();
      setTimeout(function() {
        if (!this.touchIsMoving) {
          for (let i = 0; i < this.spawnQuantity; i++) {
            this.particles.push(new Particle(this, e.changedTouches[0].clientX, e.changedTouches[0].clientY));
          }
        }
      }.bind(this), 200);
    }.bind(this);

    this.onMouseUp = function() {
      this.mouseIsDown = false;
    }.bind(this);

    this.onMouseOut = function() {
      this.removeInteractionParticle();
    }.bind(this);

    this.onTouchEnd = function(e) {
      e.preventDefault();
      this.touchIsMoving = false;
      this.removeInteractionParticle();
    }.bind(this);

    this.canvas.addEventListener('mousemove', this.onMouseMove);
    this.canvas.addEventListener('touchmove', this.onTouchMove);
    //this.canvas.addEventListener('mousedown', this.onMouseDown);
    this.canvas.addEventListener('touchstart', this.onTouchStart);
    this.canvas.addEventListener('mouseup', this.onMouseUp);
    this.canvas.addEventListener('mouseout', this.onMouseOut);
    this.canvas.addEventListener('touchend', this.onTouchEnd);
  }

  unbindUiActions() {
    if (this.canvas) {
      this.canvas.removeEventListener('mousemove', this.onMouseMove);
      this.canvas.removeEventListener('touchmove', this.onTouchMove);
      this.canvas.removeEventListener('mousedown', this.onMouseDown);
      this.canvas.removeEventListener('touchstart', this.onTouchStart);
      this.canvas.removeEventListener('mouseup', this.onMouseUp);
      this.canvas.removeEventListener('mouseout', this.onMouseOut);
      this.canvas.removeEventListener('touchend', this.onTouchEnd);
    }
  }
}


let getLimitedRandom = function(min, max, roundToInteger) {
  let number = Math.random() * (max - min) + min;
  if (roundToInteger) {
    number = Math.round(number);
  }
  return number;
};

let returnRandomArrayitem = function(array) {
  return array[Math.floor(Math.random()*array.length)];
};

export {
  ParticleNetworkAnimation
};
