var w = 960
  , h = 500

  , svg = d3.select("#chart").append("svg:svg")
      .attr("width", w)
      .attr("height", h)
  , defs = svg.append('svg:defs')

  , pref = 'file:' === location.protocol ? '' : 'http://bl.ocks.org/d/1306472/'
  , load = new Image // preload the images
  , surl = load.src = pref + 'sprites.png'

  , char_radius = 33
  , char_img_width = 80
  , sprite_w = 1025
  , sprite_h = 208

  , nodes, force
  , data, items;

svg.append("svg:rect")
    .attr("width", w)
    .attr("height", h);

d3.json(pref + 'recettear-items.json', function(json) { items = json; });
d3.json(pref + 'recettear-data.json', function(json) {  data  = json;
  var offs = data.sprites
    , n    = data.characters.length + 1
    ;

  svg.selectAll('circle.character')
      .data(data.characters)
    .enter()
      .append('svg:circle')
      .attr('cx', function(d, i) { return Math.round((i + 1) * (w / n)); })
      .attr('cy', function(d, i) {
        return Math.round((i & 1) * (h / 3) + (h / 3));
      })
      .attr('r', char_img_width >> 1)
      .attr('fill', function(d) { return 'url(#'+ d.id +')'; })
      .attr('xlink:href', function(d) { return '#'+ d.name; })
      .attr('class', 'character')
      .each(function(d) {
        defSprite(surl, sprite_w, sprite_h, offs[d.id], d.id);
      });

  start();
});

function defSprite(url, w, h, o, id) {
  defs.append('svg:pattern')
    .attr('id', id)
    .attr('width', o.w)
    .attr('height', o.h)
    .append('svg:svg')
      .attr('width', o.w)
      .attr('height', o.h)
      .attr('viewBox', [o.x, o.y, o.w, o.h].join(' '))
      .append('svg:image')
        .attr('width', w)
        .attr('height', h)
        .attr('xlink:href', url);
}

function color(i) {
  return data.characters[i].color;
}

function start() {
  nodes = d3.range(8).map(function(i) {
    return { type: Math.random() * 8 | 0
           , radius: char_radius
           , fixed: true
           , type: i
           , x: (i + 1) * (w / 9)
           , y: (i & 1) * (h / 3) + (h / 3)
           };
      });
//  color = d3.scale.category10();

  force = d3.layout.force()
      .gravity(0)
      .charge(0)
      .nodes(nodes)
      .size([w, h]);

  force.start();

  force.on("tick", function(e) {
    var q = d3.geom.quadtree(nodes),
        k = e.alpha * .1,
        i = 0,
        n = nodes.length,
        o, c;

    while (++i < n) {
      o = nodes[i];
      if (o.fixed) continue;
      c = nodes[o.type];
      o.x += (c.x - o.x) * k;
      o.y += (c.y - o.y) * k;
      q.visit(collide(o));
    }

    svg.selectAll("circle.fly")
        .attr("cx", function(d) { return d.x; })
        .attr("cy", function(d) { return d.y; });
  });

  var p0;
  svg.on("mousemove", function() {
    var p1   = d3.svg.mouse(this)
      , node = { radius: Math.random() * 12 + 4
               , type:   Math.random() * 8 | 0
               , x: p1[0]
               , y: p1[1]
               , px: (p0 || (p0 = p1))[0]
               , py: p0[1]
               };
    p0 = p1;

    svg.append("svg:circle")
        .data([node])
        .attr('class', 'fly')
        .attr("cx", function(d) { return d.x; })
        .attr("cy", function(d) { return d.y; })
        .attr("r", function(d) { return d.radius - 2; })
        .style("fill", function(d) {return color(d.type);})
        .transition()
          .delay(3000)
          .attr("r", 1e-6)
          .each("end", function() { nodes.splice(8, 1); })
          .remove();

    nodes.push(node);
    force.resume();
  });
}


function collide(node) {
  var r = node.radius + 16,
      nx1 = node.x - r,
      nx2 = node.x + r,
      ny1 = node.y - r,
      ny2 = node.y + r;
  return function(quad, x1, y1, x2, y2) {
    if (quad.point && (quad.point !== node)) {
      var x = node.x - quad.point.x,
          y = node.y - quad.point.y,
          l = Math.sqrt(x * x + y * y),
          r = node.radius + quad.point.radius;
      if (l < r) {
        l = (l - r) / l * .5;
        node.px += x * l;
        node.py += y * l;
      }
    }
    return x1 > nx2
        || x2 < nx1
        || y1 > ny2
        || y2 < ny1;
  };
}