Hex = function(q, r, s) {
  this.q = q;
  this.r = r;
  this.s = s != undefined ? s : -q - r;
};

Hex.add = function(a, b) {
  return new Hex(a.q + b.q, a.r + b.r, a.s + b.s);
};

Hex.subtract = function(a, b) {
  return new Hex(a.q - b.q, a.r - b.r, a.s - b.s);
};

Hex.multiply = function(a, b) {
  return new Hex(a.q * b.q, a.r * b.r, a.s * b.s);
};

Hex.prototype.equals = function(that) {
  return this.q == that.q && this.r == that.r && this.s == that.s;
};

Hex.prototype.length = function(hex) {
  return (Math.abs(hex.q) + Math.abs(hex.r) + Math.abs(hex.s)) / 2;
};

Hex.distance = function(a, b) {
  return Hex.subtract(a, b).length();
};

var DIRECTIONS = [
  new Hex(1, 0, -1), new Hex(1, -1, 0), new Hex(0, -1, 1),
  new Hex(-1, 0, 1), new Hex(-1, 1, 0), new Hex(0, 1, -1)
];

Orientation = function(f0, f1, f2, f3, b0, b1, b2, b3, startAngle) {
  this.f0 = f0;
  this.f1 = f1;
  this.f2 = f2;
  this.f3 = f3;
  this.b0 = b0;
  this.b1 = b1;
  this.b2 = b2;
  this.b3 = b3;
  this.startAngle = startAngle;
};

Point = function(x, y) {
  this.x = x;
  this.y = y;
};

LAYOUT_POINTY = new Orientation(
    Math.sqrt(3.0), Math.sqrt(3.0) / 2.0, 0.0, 3.0 / 2.0,
    Math.sqrt(3.0) / 3.0, -1.0 / 3.0, 0.0, 2.0 / 3.0,
    0.5);
LAYOUT_FLAT = new Orientation(
    3.0 / 2.0, 0.0, Math.sqrt(3.0) / 2.0, Math.sqrt(3.0),
    2.0 / 3.0, 0.0, -1.0 / 3.0, Math.sqrt(3.0) / 3.0,
    0.0);

Layout = function(orientation, size, origin) {
  this.orientation = orientation;
  this.size = size;
  this.origin = origin;
};

Layout.prototype.hexToPixel = function(h) {
  var m = this.orientation;
  var x = (m.f0 * h.q + m.f1 * h.r) * this.size.x;
  var y = (m.f2 * h.q + m.f3 * h.r) * this.size.y;
  return new Point(x + this.origin.x, y + this.origin.y);
};

Layout.prototype.pixelToHex = function(p) {
  var m = orientation;
  var pt = Point((p.x - origin.x) / size.x,
                 (p.y - origin.y) / size.y);
  var q = M.b0 * pt.x + M.b1 * pt.y;
  var r = M.b2 * pt.x + M.b3 * pt.y;
  return new Hex(q, r, -q - r);
};

Layout.prototype.hexCornerOffset = function(corner) {
  var size = this.size;
  var angle = 2.0 * Math.PI * (corner + this.orientation.startAngle) / 6;
  return new Point(size.x * Math.cos(angle), size.y * Math.sin(angle));
};

Layout.prototype.polygonCorners = function(h) {
  var corners = [];
  var center = this.hexToPixel(h);
  for (var i = 0; i < 6; i++) {
    var offset = this.hexCornerOffset(i);
    corners.push(new Point(center.x + offset.x, center.y + offset.y));
  }
  return corners;
};

function flattenPoints(pointList) {
  var coords = [];
  for (var i = 0; i < pointList.length; i++) {
    var point = pointList[i];
    coords.push(point.x);
    coords.push(point.y);
    coords.push(0);  // z plane is flat
  }
  return coords;
}
