var vows = require("vows"),
    load = require("../load"),
    assert = require("../assert");

var suite = vows.describe("d3.svg.arc");

suite.addBatch({
  "arc": {
    topic: load("svg/arc").expression("d3.svg.arc"),

    "innerRadius defaults to a function accessor": function(arc) {
      var a = arc().outerRadius(100).startAngle(0).endAngle(Math.PI / 2);
      assert.pathEqual(a({innerRadius: 0}), "M0,-100A100,100 0 0,1 100,0L0,0Z");
      assert.pathEqual(a({innerRadius: 50}), "M0,-100A100,100 0 0,1 100,0L50,0A50,50 0 0,0 0,-50Z");
    },
    "innerRadius can be defined as a constant": function(arc) {
      var a = arc().outerRadius(100).startAngle(0).endAngle(Math.PI / 2);
      assert.pathEqual(a.innerRadius(0)(), "M0,-100A100,100 0 0,1 100,0L0,0Z");
      assert.pathEqual(a.innerRadius(50)(), "M0,-100A100,100 0 0,1 100,0L50,0A50,50 0 0,0 0,-50Z");
    },
    "innerRadius can be defined as a function of data or index": function(arc) {
      var a = arc().innerRadius(f).outerRadius(100).startAngle(0).endAngle(Math.PI / 2), o = {}, t = {}, dd, ii, tt;
      function f(d, i) { dd = d; ii = i; tt = this; return 42; }
      assert.pathEqual(a.call(t, o, 2), "M0,-100A100,100 0 0,1 100,0L42,0A42,42 0 0,0 0,-42Z");
      assert.equal(dd, o, "expected data, got {actual}");
      assert.equal(ii, 2, "expected index, got {actual}");
      assert.equal(tt, t, "expected this, got {actual}");
    },

    "outerRadius defaults to a function accessor": function(arc) {
      var a = arc().innerRadius(0).startAngle(0).endAngle(Math.PI / 2);
      assert.pathEqual(a({outerRadius: 50}), "M0,-50A50,50 0 0,1 50,0L0,0Z");
      assert.pathEqual(a({outerRadius: 100}), "M0,-100A100,100 0 0,1 100,0L0,0Z");
    },
    "outerRadius can be defined as a constant": function(arc) {
      var a = arc().innerRadius(0).startAngle(0).endAngle(Math.PI / 2);
      assert.pathEqual(a.outerRadius(50)(), "M0,-50A50,50 0 0,1 50,0L0,0Z");
      assert.pathEqual(a.outerRadius(100)(), "M0,-100A100,100 0 0,1 100,0L0,0Z");
    },
    "outerRadius can be defined as a function of data or index": function(arc) {
      var a = arc().innerRadius(0).outerRadius(f).startAngle(0).endAngle(Math.PI / 2), o = {}, t = {}, dd, ii, tt;
      function f(d, i) { dd = d; ii = i; tt = this; return 42; }
      assert.pathEqual(a.call(t, o, 2), "M0,-42A42,42 0 0,1 42,0L0,0Z");
      assert.equal(dd, o, "expected data, got {actual}");
      assert.equal(ii, 2, "expected index, got {actual}");
      assert.equal(tt, t, "expected this, got {actual}");
    },

    "startAngle defaults to a function accessor": function(arc) {
      var a = arc().innerRadius(0).outerRadius(100).endAngle(Math.PI / 2);
      assert.pathEqual(a({startAngle: 0}), "M0,-100A100,100 0 0,1 100,0L0,0Z");
      assert.pathEqual(a({startAngle: -Math.PI}), "M0,100A100,100 0 1,1 100,0L0,0Z");
    },
    "startAngle can be defined as a constant": function(arc) {
      var a = arc().innerRadius(0).outerRadius(100).endAngle(Math.PI / 2);
      assert.pathEqual(a.startAngle(0)(), "M0,-100A100,100 0 0,1 100,0L0,0Z");
      assert.pathEqual(a.startAngle(-Math.PI)(), "M0,100A100,100 0 1,1 100,0L0,0Z");
    },
    "startAngle can be defined as a function of data or index": function(arc) {
      var a = arc().innerRadius(0).outerRadius(100).startAngle(f).endAngle(Math.PI / 2), o = {}, t = {}, dd, ii, tt;
      function f(d, i) { dd = d; ii = i; tt = this; return Math.PI; }
      assert.pathEqual(a.call(t, o, 2), "M0,100A100,100 0 0,0 100,0L0,0Z");
      assert.equal(dd, o, "expected data, got {actual}");
      assert.equal(ii, 2, "expected index, got {actual}");
      assert.equal(tt, t, "expected this, got {actual}");
    },

    "endAngle defaults to a function accessor": function(arc) {
      var a = arc().innerRadius(0).outerRadius(100).startAngle(0);
      assert.pathEqual(a({endAngle: Math.PI / 2}), "M0,-100A100,100 0 0,1 100,0L0,0Z");
      assert.pathEqual(a({endAngle: 3 * Math.PI / 2}), "M0,-100A100,100 0 1,1 -100,0L0,0Z");
    },
    "endAngle can be defined as a constant": function(arc) {
      var a = arc().innerRadius(0).outerRadius(100).startAngle(0);
      assert.pathEqual(a.endAngle(Math.PI / 2)(), "M0,-100A100,100 0 0,1 100,0L0,0Z");
      assert.pathEqual(a.endAngle(3 * Math.PI / 2)(), "M0,-100A100,100 0 1,1 -100,0L0,0Z");
    },
    "endAngle can be defined as a function of data or index": function(arc) {
      var a = arc().innerRadius(0).outerRadius(100).startAngle(0).endAngle(f), o = {}, t = {}, dd, ii, tt;
      function f(d, i) { dd = d; ii = i; tt = this; return Math.PI / 2; }
      assert.pathEqual(a.call(t, o, 2), "M0,-100A100,100 0 0,1 100,0L0,0Z");
      assert.equal(dd, o, "expected data, got {actual}");
      assert.equal(ii, 2, "expected index, got {actual}");
      assert.equal(tt, t, "expected this, got {actual}");
    },

    "arc is drawn counter-clockwise if endAngle is less than startAngle": function(arc) {
      var a = arc().innerRadius(0).outerRadius(100);
      assert.pathEqual(a.startAngle(2 * Math.PI).endAngle(Math.PI / 2)(), "M0,-100A100,100 0 1,0 100,0L0,0Z");
      assert.pathEqual(a.startAngle(-Math.PI).endAngle(Math.PI)(), "M0,100A100,100 0 1,1 0,-100A100,100 0 1,1 0,100Z");
      var a = arc().innerRadius(50).outerRadius(100);
      assert.pathEqual(a.startAngle(2 * Math.PI).endAngle(Math.PI / 2)(), "M0,-100A100,100 0 1,0 100,0L50,0A50,50 0 1,1 0,-50Z");
      assert.pathEqual(a.startAngle(-Math.PI).endAngle(Math.PI)(), "M0,100A100,100 0 1,1 0,-100A100,100 0 1,1 0,100M0,50A50,50 0 1,0 0,-50A50,50 0 1,0 0,50Z");
    },

    "angles are defined in radians, with zero at 12 o'clock": function(arc) {
      var a = arc().innerRadius(0).outerRadius(100);
      assert.pathEqual(a.startAngle(0).endAngle(Math.PI / 2)(), "M0,-100A100,100 0 0,1 100,0L0,0Z");
      assert.pathEqual(a.startAngle(Math.PI / 2).endAngle(2 * Math.PI)(), "M100,0A100,100 0 1,1 0,-100L0,0Z");
    },
    "radii are defined in local coordinates (typically pixels)": function(arc) {
      var a = arc().startAngle(0).endAngle(Math.PI / 2);
      assert.pathEqual(a.innerRadius(0).outerRadius(100)(), "M0,-100A100,100 0 0,1 100,0L0,0Z");
      assert.pathEqual(a.innerRadius(100).outerRadius(200)(), "M0,-200A200,200 0 0,1 200,0L100,0A100,100 0 0,0 0,-100Z");
    },
    "draws a circle when inner radius is zero and angle is approximately 2π": function(arc) {
      var a = arc().innerRadius(0).outerRadius(100);
      assert.pathEqual(a.startAngle(0).endAngle(2 * Math.PI - 1e-9)(), "M0,100A100,100 0 1,1 0,-100A100,100 0 1,1 0,100Z");
      assert.pathEqual(a.startAngle(Math.PI + 1e-9).endAngle(3 * Math.PI - 1e-9)(), "M0,100A100,100 0 1,1 0,-100A100,100 0 1,1 0,100Z");
    },
    "draws a circle when inner radius is zero and angle is greater than 2π": function(arc) {
      var a = arc().innerRadius(0).outerRadius(100);
      assert.pathEqual(a.startAngle(0).endAngle(7)(), "M0,100A100,100 0 1,1 0,-100A100,100 0 1,1 0,100Z");
      assert.pathEqual(a.startAngle(1).endAngle(8)(), "M0,100A100,100 0 1,1 0,-100A100,100 0 1,1 0,100Z");
    },
    "draws a circular sector when inner radius is zero and angle is less than 2π": function(arc) {
      var a = arc().innerRadius(0).outerRadius(100);
      assert.pathEqual(a.startAngle(0).endAngle(Math.PI / 2)(), "M0,-100A100,100 0 0,1 100,0L0,0Z");
    },
    "draws an annulus when inner radius is non-zero and angle is approximately 2π": function(arc) {
      var a = arc().innerRadius(100).outerRadius(200);
      assert.pathEqual(a.startAngle(0).endAngle(2 * Math.PI - 1e-9)(), "M0,200A200,200 0 1,1 0,-200A200,200 0 1,1 0,200M0,100A100,100 0 1,0 0,-100A100,100 0 1,0 0,100Z");
      assert.pathEqual(a.startAngle(Math.PI + 1e-9).endAngle(3 * Math.PI - 1e-9)(), "M0,200A200,200 0 1,1 0,-200A200,200 0 1,1 0,200M0,100A100,100 0 1,0 0,-100A100,100 0 1,0 0,100Z");
    },
    "draws an annulus when inner radius is non-zero and angle is greater than 2π": function(arc) {
      var a = arc().innerRadius(100).outerRadius(200);
      assert.pathEqual(a.startAngle(0).endAngle(7)(), "M0,200A200,200 0 1,1 0,-200A200,200 0 1,1 0,200M0,100A100,100 0 1,0 0,-100A100,100 0 1,0 0,100Z");
      assert.pathEqual(a.startAngle(-1).endAngle(6)(), "M0,200A200,200 0 1,1 0,-200A200,200 0 1,1 0,200M0,100A100,100 0 1,0 0,-100A100,100 0 1,0 0,100Z");
    },
    "draws an annular sector when both radii are non-zero and angle is less than 2π": function(arc) {
      var a = arc().innerRadius(100).outerRadius(200);
      assert.pathEqual(a.startAngle(0).endAngle(Math.PI / 2)(), "M0,-200A200,200 0 0,1 200,0L100,0A100,100 0 0,0 0,-100Z");
    },

    "computes the centroid as mid-radius and mid-angle": function(arc) {
      var c = arc().innerRadius(0).outerRadius(100).startAngle(0).endAngle(2 * Math.PI).centroid();
      assert.inDelta(c[0], 0, 1e-6);
      assert.inDelta(c[1], 50, 1e-6);
      var c = arc().innerRadius(100).outerRadius(200).startAngle(Math.PI).endAngle(2 * Math.PI).centroid();
      assert.inDelta(c[0], -150, 1e-6);
      assert.inDelta(c[1], 0, 1e-6);
    }
  }
});

suite.export(module);
