(function() {
  var global, hexProjection, hexTopology, mousedown, mousemove, mouseup, redraw;

  global = {};

  window.main = function() {
    var height, radius, svg, width;
    width = 960;
    height = 500;
    radius = 20;
    global.mousing = 0;
    global.color = 0;
    global.hex_topology = hexTopology(radius, width, height);
    global.path_generator = d3.geo.path().projection(hexProjection(radius));
    global.color_scale = d3.scale.category10();
    svg = d3.select('body').append('svg').attr('width', width).attr('height', height);
    svg.append('g').attr('class', 'hexagon').selectAll('path').data(global.hex_topology.objects.hexagons.geometries).enter().append('path').attr('d', function(d) {
      return global.path_generator(topojson.feature(global.hex_topology, d));
    }).on('mousedown', mousedown).on('mousemove', mousemove).on('mouseup', mouseup);
    svg.append('path').datum(topojson.mesh(global.hex_topology, global.hex_topology.objects.hexagons)).attr('class', 'mesh').attr('d', global.path_generator);
    return global.border = svg.append('path').attr('class', 'border').call(redraw);
  };

  /* create the hex mesh TopoJSON
  */

  hexTopology = function(radius, width, height) {
    var arcs, dx, dy, geometries, i, j, m, n, q, x, y;
    dx = radius * 2 * Math.sin(Math.PI / 3);
    dy = radius * 1.5;
    m = Math.ceil((height + radius) / dy) + 1;
    n = Math.ceil(width / dx) + 1;
    geometries = [];
    arcs = [];
    for (j = -1; -1 <= m ? j <= m : j >= m; -1 <= m ? j++ : j--) {
      for (i = -1; -1 <= n ? i <= n : i >= n; -1 <= n ? i++ : i--) {
        y = j * 2;
        x = (i + (j & 1) / 2) * 2;
        arcs.push([[x, y - 1], [1, 1]], [[x + 1, y], [0, 1]], [[x + 1, y + 1], [-1, 1]]);
      }
    }
    q = 3;
    for (j = 0; 0 <= m ? j < m : j > m; 0 <= m ? j++ : j--) {
      for (i = 0; 0 <= n ? i < n : i > n; 0 <= n ? i++ : i--) {
        geometries.push({
          type: 'Polygon',
          arcs: [[q, q + 1, q + 2, ~(q + (n + 2 - (j & 1)) * 3), ~(q - 2), ~(q - (n + 2 + (j & 1)) * 3 + 2)]]
        });
        q += 3;
      }
      q += 6;
    }
    return {
      transform: {
        translate: [0, 0],
        scale: [1, 1]
      },
      objects: {
        hexagons: {
          type: 'GeometryCollection',
          geometries: geometries
        }
      },
      arcs: arcs
    };
  };

  /* define a custom projection to make hexagons appear regular
  */

  hexProjection = function(radius) {
    var dx, dy;
    dx = radius * 2 * Math.sin(Math.PI / 3);
    dy = radius * 1.5;
    return {
      stream: function(stream) {
        return {
          point: (function(x, y) {
            return stream.point(x * dx / 2, (y - (2 - (y & 1)) / 3) * dy / 2);
          }),
          lineStart: (function() {
            return stream.lineStart();
          }),
          lineEnd: (function() {
            return stream.lineEnd();
          }),
          polygonStart: (function() {
            return stream.polygonStart();
          }),
          polygonEnd: (function() {
            return stream.polygonEnd();
          })
        };
      }
    };
  };

  /* user interaction callbacks
  */

  mousemove = function(d) {
    if (global.mousing) {
      d3.select(this).style('fill', global.color_scale(d.fill = global.color));
      return global.border.call(redraw);
    }
  };

  mousedown = function(d) {
    global.mousing = d.fill ? -1 : +1;
    return mousemove.apply(this, arguments);
  };

  mouseup = function() {
    mousemove.apply(this, arguments);
    global.mousing = 0;
    /* cycle through 6 colors
    */
    return global.color = (global.color + 1) % 6;
  };

  /* redraw borders
  */

  redraw = function(border) {
    return border.attr('d', global.path_generator(topojson.mesh(global.hex_topology, global.hex_topology.objects.hexagons, function(a, b) {
      return a.fill !== b.fill;
    })));
  };

}).call(this);