/**
 * Canvas shapes used by Network
 */
if (typeof CanvasRenderingContext2D !== 'undefined') {

  /**
   * Draw a circle shape
   */
  CanvasRenderingContext2D.prototype.circle = function (x, y, r) {
    this.beginPath();
    this.arc(x, y, r, 0, 2 * Math.PI, false);
    this.closePath();
  };

  /**
   * Draw a square shape
   * @param {Number} x horizontal center
   * @param {Number} y vertical center
   * @param {Number} r   size, width and height of the square
   */
  CanvasRenderingContext2D.prototype.square = function (x, y, r) {
    this.beginPath();
    this.rect(x - r, y - r, r * 2, r * 2);
    this.closePath();
  };

  /**
   * Draw a triangle shape
   * @param {Number} x horizontal center
   * @param {Number} y vertical center
   * @param {Number} r   radius, half the length of the sides of the triangle
   */
  CanvasRenderingContext2D.prototype.triangle = function (x, y, r) {
    // http://en.wikipedia.org/wiki/Equilateral_triangle
    this.beginPath();

    // the change in radius and the offset is here to center the shape
    r *= 1.15;
    y += 0.275 * r;

    var s = r * 2;
    var s2 = s / 2;
    var ir = Math.sqrt(3) / 6 * s;      // radius of inner circle
    var h = Math.sqrt(s * s - s2 * s2); // height


    this.moveTo(x, y - (h - ir));
    this.lineTo(x + s2, y + ir);
    this.lineTo(x - s2, y + ir);
    this.lineTo(x, y - (h - ir));
    this.closePath();


  };

  /**
   * Draw a triangle shape in downward orientation
   * @param {Number} x horizontal center
   * @param {Number} y vertical center
   * @param {Number} r radius
   */
  CanvasRenderingContext2D.prototype.triangleDown = function (x, y, r) {
    // http://en.wikipedia.org/wiki/Equilateral_triangle
    this.beginPath();

    // the change in radius and the offset is here to center the shape
    r *= 1.15;
    y -= 0.275 * r;

    var s = r * 2;
    var s2 = s / 2;
    var ir = Math.sqrt(3) / 6 * s;      // radius of inner circle
    var h = Math.sqrt(s * s - s2 * s2); // height

    this.moveTo(x, y + (h - ir));
    this.lineTo(x + s2, y - ir);
    this.lineTo(x - s2, y - ir);
    this.lineTo(x, y + (h - ir));
    this.closePath();
  };

  /**
   * Draw a star shape, a star with 5 points
   * @param {Number} x horizontal center
   * @param {Number} y vertical center
   * @param {Number} r   radius, half the length of the sides of the triangle
   */
  CanvasRenderingContext2D.prototype.star = function (x, y, r) {
    // http://www.html5canvastutorials.com/labs/html5-canvas-star-spinner/
    this.beginPath();

    // the change in radius and the offset is here to center the shape
    r *= 0.82;
    y += 0.1 * r;

    for (var n = 0; n < 10; n++) {
      var radius = (n % 2 === 0) ? r * 1.3 : r * 0.5;
      this.lineTo(
        x + radius * Math.sin(n * 2 * Math.PI / 10),
        y - radius * Math.cos(n * 2 * Math.PI / 10)
      );
    }

    this.closePath();
  };

  /**
   * Draw a Diamond shape
   * @param {Number} x horizontal center
   * @param {Number} y vertical center
   * @param {Number} r   radius, half the length of the sides of the triangle
   */
  CanvasRenderingContext2D.prototype.diamond = function (x, y, r) {
    // http://www.html5canvastutorials.com/labs/html5-canvas-star-spinner/
    this.beginPath();

    this.lineTo(x, y + r);
    this.lineTo(x + r, y);
    this.lineTo(x, y - r);
    this.lineTo(x - r, y);


    this.closePath();
  };

  /**
   * http://stackoverflow.com/questions/1255512/how-to-draw-a-rounded-rectangle-on-html-canvas
   */
  CanvasRenderingContext2D.prototype.roundRect = function (x, y, w, h, r) {
    var r2d = Math.PI / 180;
    if (w - ( 2 * r ) < 0) {
      r = ( w / 2 );
    } //ensure that the radius isn't too large for x
    if (h - ( 2 * r ) < 0) {
      r = ( h / 2 );
    } //ensure that the radius isn't too large for y
    this.beginPath();
    this.moveTo(x + r, y);
    this.lineTo(x + w - r, y);
    this.arc(x + w - r, y + r, r, r2d * 270, r2d * 360, false);
    this.lineTo(x + w, y + h - r);
    this.arc(x + w - r, y + h - r, r, 0, r2d * 90, false);
    this.lineTo(x + r, y + h);
    this.arc(x + r, y + h - r, r, r2d * 90, r2d * 180, false);
    this.lineTo(x, y + r);
    this.arc(x + r, y + r, r, r2d * 180, r2d * 270, false);
    this.closePath();
  };

  /**
   * http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas
   *
   * Postfix '_vis' added to discern it from standard method ellipse().
   */
  CanvasRenderingContext2D.prototype.ellipse_vis = function (x, y, w, h) {
    var kappa = .5522848,
      ox = (w / 2) * kappa, // control point offset horizontal
      oy = (h / 2) * kappa, // control point offset vertical
      xe = x + w,           // x-end
      ye = y + h,           // y-end
      xm = x + w / 2,       // x-middle
      ym = y + h / 2;       // y-middle

    this.beginPath();
    this.moveTo(x, ym);
    this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
    this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
    this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
    this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
    this.closePath();
  };


  /**
   * http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas
   */
  CanvasRenderingContext2D.prototype.database = function (x, y, w, h) {
    var f = 1 / 3;
    var wEllipse = w;
    var hEllipse = h * f;

    var kappa = .5522848,
      ox = (wEllipse / 2) * kappa, // control point offset horizontal
      oy = (hEllipse / 2) * kappa, // control point offset vertical
      xe = x + wEllipse,           // x-end
      ye = y + hEllipse,           // y-end
      xm = x + wEllipse / 2,       // x-middle
      ym = y + hEllipse / 2,       // y-middle
      ymb = y + (h - hEllipse / 2),  // y-midlle, bottom ellipse
      yeb = y + h;                 // y-end, bottom ellipse

    this.beginPath();
    this.moveTo(xe, ym);

    this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
    this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);

    this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
    this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);

    this.lineTo(xe, ymb);

    this.bezierCurveTo(xe, ymb + oy, xm + ox, yeb, xm, yeb);
    this.bezierCurveTo(xm - ox, yeb, x, ymb + oy, x, ymb);

    this.lineTo(x, ym);
  };


  /**
   * Draw an arrow at the end of a line with the given angle.
   */
  CanvasRenderingContext2D.prototype.arrowEndpoint = function (x, y, angle, length) {
    // tail
    var xt = x - length * Math.cos(angle);
    var yt = y - length * Math.sin(angle);

    // inner tail
    var xi = x - length * 0.9 * Math.cos(angle);
    var yi = y - length * 0.9 * Math.sin(angle);

    // left
    var xl = xt + length / 3 * Math.cos(angle + 0.5 * Math.PI);
    var yl = yt + length / 3 * Math.sin(angle + 0.5 * Math.PI);

    // right
    var xr = xt + length / 3 * Math.cos(angle - 0.5 * Math.PI);
    var yr = yt + length / 3 * Math.sin(angle - 0.5 * Math.PI);

    this.beginPath();
    this.moveTo(x, y);
    this.lineTo(xl, yl);
    this.lineTo(xi, yi);
    this.lineTo(xr, yr);
    this.closePath();
  };

  /**
   * Draw an circle an the end of an line with the given angle.
   */
  CanvasRenderingContext2D.prototype.circleEndpoint = function (x, y, angle, length) {
    var radius = length * 0.4;
    var xc = x - radius * Math.cos(angle);
    var yc = y - radius * Math.sin(angle);
    this.circle(xc, yc, radius);
  };

  /**
   * Sets up the dashedLine functionality for drawing
   * Original code came from http://stackoverflow.com/questions/4576724/dotted-stroke-in-canvas
   * @author David Jordan
   * @date 2012-08-08
   */
  CanvasRenderingContext2D.prototype.dashedLine = function (x, y, x2, y2, pattern) {
    this.beginPath();
    this.moveTo(x, y);

    var patternLength = pattern.length;
    var dx = (x2 - x);
    var dy = (y2 - y);
    var slope = dy / dx;
    var distRemaining = Math.sqrt(dx * dx + dy * dy);
    var patternIndex = 0;
    var draw = true;
    var xStep = 0;
    var dashLength = pattern[0];

    while (distRemaining >= 0.1) {
      dashLength = pattern[patternIndex++ % patternLength];
      if (dashLength > distRemaining) {
        dashLength = distRemaining;
      }

      xStep = Math.sqrt(dashLength * dashLength / (1 + slope * slope));
      xStep = dx < 0 ? -xStep : xStep;
      x += xStep;
      y += slope * xStep;

      if (draw === true) {this.lineTo(x,y);}
      else               {this.moveTo(x,y);}

      distRemaining -= dashLength;
      draw = !draw;
    }
  };


}
