(function () {
  function fillCanvas (element, draw) {
    var canvas = document.createElement("canvas");
    var ctx = canvas.getContext("2d");

    var windowToCanvas;
    function resize () {
      var vw = element.clientWidth;
      var vh = element.clientHeight;
      canvas.width = vw;
      canvas.height = vh;

      var vpMin = Math.min(vw, vh);

      // cartesion coordinates [0, 100] on both x and y axes
      ctx.setTransform(
        vpMin / 100, 0,
        0, -vpMin / 100,
        (vw - vpMin) / 2, (vh - vpMin) / 2 + vh
      );
      windowToCanvas = ctx.getTransform().inverse();
    }
    new ResizeObserver(resize).observe(element);
    resize();

    function render () {
      draw(ctx, canvas.width, canvas.height, windowToCanvas);
      requestAnimationFrame(render);
    }
    render();

    element.appendChild(canvas);
  }

  const Vec2 = {
    add: (v1, v2) => [v1[0] + v2[0], v1[1] + v2[1]],
    sub: (v1, v2) => [v1[0] - v2[0], v1[1] - v2[1]],
    scale: (s, v) => [s * v[0], s * v[1]],
    magnitude: (v) => Math.sqrt(v[0] * v[0] + v[1] * v[1]),
    distance: (v1, v2) => Vec2.magnitude(Vec2.sub(v1, v2)),
    normalize: (v) => Vec2.scale(1 / Vec2.magnitude(v), v),
    transform: (m, v) => {
      return [
        m.a * v[0] + m.c * v[1] + m.e,
        m.b * v[1] + m.d * v[1] + m.f
      ];
    },
    dot: (v1, v2) => [v1[0] * v2[0] + v1[1] * v2[1]],
    triangleArea: (p1, p2, p3) => {
      // Cramers rule for signed area (necessary to get negative lambdas)
      var [x1, y1] = p1;
      var [x2, y2] = p2;
      var [x3, y3] = p3;

      return (x1 * y2 - x1 * y3 - x2 * y1 + x3 * y1 + x2 * y3 - x3 * y2) / 2;
    }
  };

  var p1 = [30, 20];
  var p2 = [10, 80];
  var p3 = [80, 50];

  var ptr = null;
  window.addEventListener("mousemove", function (e) {
    ptr = [e.clientX, e.clientY];
  });

  fillCanvas(document.querySelector(".canvas"), function (ctx, vw, vh, w2c) {
    var origin = Vec2.transform(w2c, [0, 0]);
    var omega = Vec2.transform(w2c, [vw, vh]);
    ctx.fillStyle = "black";
    ctx.fillRect(origin[0], origin[1], omega[0] - origin[0], omega[1] - origin[1])

    ctx.lineCap = "round";
    ctx.lineJoin = "round";

    var inside = false;
    if (ptr !== null) {
      var p = Vec2.transform(w2c, ptr);
      var a = Vec2.triangleArea(p1, p2, p3);
      var lambda1 = Vec2.triangleArea(p, p2, p3) / a;
      var lambda2 = Vec2.triangleArea(p1, p, p3) / a;
      var lambda3 = Vec2.triangleArea(p1, p2, p) / a;

      document.querySelector("#lambda1").innerHTML = formatLambda(lambda1);
      document.querySelector("#lambda2").innerHTML = formatLambda(lambda2);
      document.querySelector("#lambda3").innerHTML = formatLambda(lambda3);

      if (lambda1 > 0 && lambda2 > 0 && lambda3 > 0) {
        inside = true;
      }
    }

    ctx.beginPath();
    ctx.moveTo(p1[0], p1[1]);
    ctx.lineTo(p2[0], p2[1]);
    ctx.lineTo(p3[0], p3[1]);
    ctx.closePath();
    ctx.strokeStyle = inside ? "white" :  "red";
    ctx.stroke();

    if (ptr !== null) {
      ctx.beginPath();
      ctx.arc(p[0], p[1], 10, 0, Math.PI * 2);
      ctx.strokeStyle = "green";
      ctx.stroke();

      ctx.beginPath();
      ctx.moveTo(p1[0], p1[1]);
      ctx.lineTo(p[0], p[1]);
      ctx.strokeStyle = "white";
      ctx.stroke();

      ctx.beginPath();
      ctx.moveTo(p2[0], p2[1]);
      ctx.lineTo(p[0], p[1]);
      ctx.strokeStyle = "white";
      ctx.stroke();

      ctx.beginPath();
      ctx.moveTo(p3[0], p3[1]);
      ctx.lineTo(p[0], p[1]);
      ctx.strokeStyle = "white";
      ctx.stroke();
    }


    function formatLambda (lambda) {
      return String(Math.floor(lambda * 100) / 100);
    }
  });
})();
