import “../arrays/range”; import “../math/trigonometry”; import “layout”;

d3.layout.chord = function() {

var chord = {},
    chords,
    groups,
    matrix,
    n,
    padding = 0,
    sortGroups,
    sortSubgroups,
    sortChords;

function relayout() {
  var subgroups = {},
      groupSums = [],
      groupIndex = d3.range(n),
      subgroupIndex = [],
      k,
      x,
      x0,
      i,
      j;

  chords = [];
  groups = [];

  // Compute the sum.
  k = 0, i = -1; while (++i < n) {
    x = 0, j = -1; while (++j < n) {
      x += matrix[i][j];
    }
    groupSums.push(x);
    subgroupIndex.push(d3.range(n));
    k += x;
  }

  // Sort groups…
  if (sortGroups) {
    groupIndex.sort(function(a, b) {
      return sortGroups(groupSums[a], groupSums[b]);
    });
  }

  // Sort subgroups…
  if (sortSubgroups) {
    subgroupIndex.forEach(function(d, i) {
      d.sort(function(a, b) {
        return sortSubgroups(matrix[i][a], matrix[i][b]);
      });
    });
  }

  // Convert the sum to scaling factor for [0, 2pi].
  // TODO Allow start and end angle to be specified.
  // TODO Allow padding to be specified as percentage?
  k = (2 * π - padding * n) / k;

  // Compute the start and end angle for each group and subgroup.
  // Note: Opera has a bug reordering object literal properties!
  x = 0, i = -1; while (++i < n) {
    x0 = x, j = -1; while (++j < n) {
      var di = groupIndex[i],
          dj = subgroupIndex[di][j],
          v = matrix[di][dj],
          a0 = x,
          a1 = x += v * k;
      subgroups[di + "-" + dj] = {
        index: di,
        subindex: dj,
        startAngle: a0,
        endAngle: a1,
        value: v
      };
    }
    groups[di] = {
      index: di,
      startAngle: x0,
      endAngle: x,
      value: (x - x0) / k
    };
    x += padding;
  }

  // Generate chords for each (non-empty) subgroup-subgroup link.
  i = -1; while (++i < n) {
    j = i - 1; while (++j < n) {
      var source = subgroups[i + "-" + j],
          target = subgroups[j + "-" + i];
      if (source.value || target.value) {
        chords.push(source.value < target.value
            ? {source: target, target: source}
            : {source: source, target: target});
      }
    }
  }

  if (sortChords) resort();
}

function resort() {
  chords.sort(function(a, b) {
    return sortChords(
        (a.source.value + a.target.value) / 2,
        (b.source.value + b.target.value) / 2);
  });
}

chord.matrix = function(x) {
  if (!arguments.length) return matrix;
  n = (matrix = x) && matrix.length;
  chords = groups = null;
  return chord;
};

chord.padding = function(x) {
  if (!arguments.length) return padding;
  padding = x;
  chords = groups = null;
  return chord;
};

chord.sortGroups = function(x) {
  if (!arguments.length) return sortGroups;
  sortGroups = x;
  chords = groups = null;
  return chord;
};

chord.sortSubgroups = function(x) {
  if (!arguments.length) return sortSubgroups;
  sortSubgroups = x;
  chords = null;
  return chord;
};

chord.sortChords = function(x) {
  if (!arguments.length) return sortChords;
  sortChords = x;
  if (chords) resort();
  return chord;
};

chord.chords = function() {
  if (!chords) relayout();
  return chords;
};

chord.groups = function() {
  if (!groups) relayout();
  return groups;
};

return chord;

};