function startAddShape(STATE) {
  STATE.setMode('add-shape');
  STATE.maxZ += 1;
  document.body.style.cursor = 'crosshair';
  document.body.classList.add('show-crosshair');

  var itemID = Math.random();
  var shapeAnnotation = STATE.createShapeAnnotation({
    id: itemID,
    color: STATE.annotationDefaults.shape,
    shapeType: STATE.currentShapeType,
    width: 50,
    height: 50,
    angle: 0,
    zIndex: STATE.maxZ,
    preview: true,
  });
  var newElement = shapeAnnotation.element;
  newElement.style.width = '50px';
  newElement.style.height = '50px';

  var OFFSET = 0;
  var isDown = false;
  var isDrawing = false;

  function setTranslate(xPos, yPos, el) {
    STATE.xOffset[itemID] = xPos;
    STATE.yOffset[itemID] = yPos;
    el.style.transform = 'translate3d(' + xPos + 'px, ' + yPos + 'px, 0)';
  }

  setTranslate(
    STATE.mouse.clientX + OFFSET,
    STATE.mouse.clientY + OFFSET,
    newElement
  );

  document.querySelector('#draw-canvas').appendChild(newElement);
  handleMouseMove({
    clientX: STATE.mouse.clientX,
    clientY: STATE.mouse.clientY,
  });

  function handleKeydown(e) {
    // Key escape pressed
    if (e.keyCode === 27) {
      stopDrawShape(e, true);
      STATE.skipNextCancelOperation = true;
      finishDrawing();
    }
  }

  function handleMouseDown(e) {
    e.preventDefault();

    isDown = true;
    startPoint = {
      x: e.clientX,
      y: e.clientY,
    };

    startDrawRectangle(e);
  }

  function handleMouseMove(e) {
    if (isDown) {
      currentPoint = {
        x: e.clientX,
        y: e.clientY,
      };
    } else {
      if (STATE.currentShapeType === 'line') {
        drawLine(
          e,
          {
            x: e.clientX,
            y: e.clientY,
          },
          {
            x: e.clientX + 50,
            y: e.clientY + 50,
          }
        );
      } else {
        // move newElement follow the cursor
        setTranslate(
          STATE.mouse.clientX + OFFSET,
          STATE.mouse.clientY + OFFSET,
          newElement
        );
      }
    }

    if (isDrawing) {
      draw(e);
    }
  }

  function drawLine(e, beginPoint, userEndPoint) {
    var top = beginPoint.y;
    var left = beginPoint.x;

    var endPoint = userEndPoint;

    var width = Math.sqrt(
      Math.pow(endPoint.x - beginPoint.x, 2) +
        Math.pow(endPoint.y - beginPoint.y, 2)
    );
    var height = 0;

    // when holding shift, make sure the line is always 45 degree multiple
    if (e.shiftKey) {
      var angle = Math.atan2(
        endPoint.y - beginPoint.y,
        endPoint.x - beginPoint.x
      );
      var angleDegree = (angle * 180) / Math.PI;

      var angleDegreeRounded = Math.round(angleDegree / 45) * 45;

      var angleRounded = (angleDegreeRounded * Math.PI) / 180;

      width = Math.sqrt(
        Math.pow(endPoint.x - beginPoint.x, 2) +
          Math.pow(endPoint.y - beginPoint.y, 2)
      );

      endPoint = {
        x: beginPoint.x + width * Math.cos(angleRounded),
        y: beginPoint.y + width * Math.sin(angleRounded),
      };
    }

    // use the top border as the line
    newElement.style.transform =
      'translate3d(' +
      left +
      'px, ' +
      top +
      'px, 0) ' +
      'rotateZ(' +
      Math.atan2(endPoint.y - beginPoint.y, endPoint.x - beginPoint.x) +
      'rad)';
    var angle = Math.atan2(endPoint.y - beginPoint.y, endPoint.x - beginPoint.x);
    newElement.style.width = width + 'px';
    newElement.style.height = height + 'px';

    STATE.xOffset[itemID] = left;
    STATE.yOffset[itemID] = top;
    shapeAnnotation.width = width;
    shapeAnnotation.height = height;
    shapeAnnotation.angle = angle;
  }

  function draw(e) {
    if (!newElement) {
      return;
    }

    if (STATE.currentShapeType === 'line') {
      drawLine(e, startPoint, currentPoint);
    } else {
      var top = Math.min(startPoint.y, currentPoint.y);
      var left = Math.min(startPoint.x, currentPoint.x);

      var width = Math.abs(startPoint.x - currentPoint.x);
      var height = Math.abs(startPoint.y - currentPoint.y);

      if (STATE.currentShapeType === 'oval') {
        if (width < 50) {
          width = 50;
        }

        if (height < 50) {
          height = 50;
        }
      } else {
        if (width < 25) {
          width = 25;
        }

        if (height < 25) {
          height = 25;
        }
      }

      // when holding shift, make the width and height equal based on mouse position
      if (e.shiftKey) {
        var distance = Math.sqrt(
          Math.pow(startPoint.x - currentPoint.x, 2) +
            Math.pow(startPoint.y - currentPoint.y, 2)
        );

        var newWidth = Math.sqrt((distance * distance) / 2);
        var newHeight = newWidth;

        if (currentPoint.x > startPoint.x && currentPoint.y > startPoint.y) {
          top = startPoint.y;
          left = startPoint.x;
        } else if (
          currentPoint.x < startPoint.x &&
          currentPoint.y < startPoint.y
        ) {
          top = startPoint.y - newHeight;
          left = startPoint.x - newWidth;
        } else if (
          currentPoint.x > startPoint.x &&
          currentPoint.y < startPoint.y
        ) {
          top = startPoint.y - newHeight;
          left = startPoint.x;
        } else if (
          currentPoint.x < startPoint.x &&
          currentPoint.y > startPoint.y
        ) {
          top = startPoint.y;
          left = startPoint.x - newWidth;
        }

        width = newWidth;
        height = newHeight;
      }

      setTranslate(left, top, newElement);
      newElement.style.width = width + 'px';
      newElement.style.height = height + 'px';
      shapeAnnotation.width = width;
      shapeAnnotation.height = height;
      shapeAnnotation.angle = 0;
    }
  }

  function handleMouseUp(e) {
    isDown = false;
    stopDrawShape(e);
    finishDrawing();
  }

  function startDrawRectangle(e) {
    if (!isDrawing) {
      isDrawing = true;
    }
  }

  function stopDrawShape(e, cancel) {
    if (isDrawing) {
      isDrawing = false;

      if (cancel) {
        // remove `newElement` out of its parent
        STATE.removeAnnotation(String(itemID));
      } else {
        newElement.classList.remove('preview');
        newElement.classList.add('draggable');
        if (STATE.selectAnnotation) {
          STATE.selectAnnotation(String(itemID));
        }
        STATE.markDocumentChanged(true);
        newElement = null;
      }
    }
  }

  function finishDrawing() {
    STATE.setMode('free');
    document.body.style.cursor = 'auto';
    document.body.classList.remove('show-crosshair');
    if (newElement) {
      STATE.removeAnnotation(String(itemID));
    }

    document.removeEventListener('keydown', handleKeydown);
    document.removeEventListener('mousedown', handleMouseDown);
    document.removeEventListener('mousemove', handleMouseMove);
    document.removeEventListener('mouseup', handleMouseUp);
  }

  document.addEventListener('keydown', handleKeydown);
  document.addEventListener('mousedown', handleMouseDown);
  document.addEventListener('mousemove', handleMouseMove);
  document.addEventListener('mouseup', handleMouseUp);

  return { cancel: finishDrawing };
}
