# //= require material_raingular/d3/ring_chart/directive

### TODO: Massive refactor required after change to coffeescropt.

The watch function is really more of a hack than an actual
fix to the fact that we aren't responsive to data changes

### ### ************************************************************************

Custom D3 ring chart

Most everything here is going to be happening outside the AngularJS

context so scope.$apply() will be necessary for forcing angular to go into it's digest loop when changes are made to the scope.

dataset: Constructed from a data service passed-in by directive caller.

Result is ************************************************************************ ###

class RingChartModel extends AngularLinkModel

# Default attributes to use in template if attribute not set.
# Currently this only affects the internal relative positioning.
defaults:
  width: 200
  height: 200

initialize: ->
  @d3 = window.d3
  @$mrD3RingWidgetController = @$controller
  @$scope.title ||= "Some title from data-set."
  @_addRings()
  @$scope.$watchCollection @_dataset, (newVal,oldVal) =>
    @_addRings() if newVal != oldVal
#TODO: Replace with data factory that generates JSON objects.
_dataset: => @$scope.$eval(@$scope.ringData)
_addRings: ->
  # Add ring to ring list on RingWidgetController
  @$mrD3RingWidgetController.refresh()
  @$mrD3RingWidgetController.addRing(@$scope)

  @w = @defaults.width
  @h = @defaults.height

  # Get sum and calc percent.
  @toPercent = 1/@_dataset().reduce(@_getSum)*100
  @outerRadius = Math.min(@w,@h)/2
  @innerRadius = Math.min(@w,@h)/3

  @cornerRadius = @outerRadius*0.05
  arc = @d3.arc()
    .innerRadius(@innerRadius)
    .outerRadius(@outerRadius)
    .cornerRadius(@cornerRadius)
    .padAngle(0.03)

  @arcDataset = @d3.pie()(@_dataset()) # Convert array of number into arc object data

  # Normalize arc dataset
  @_rotateArcs(@arcDataset, 0)
  # Make first data element centered on rhs of ring graph
  @rotateAngle = @_calcCenterRotation(@arcDataset[0])
  # Go through and rotate all arcs.
  @_rotateArcs(@arcDataset, @rotateAngle)
  # Easy colors accessible via a 10-step ordinal scale
  @color = @d3.scaleOrdinal(@d3.schemeCategory10)

  # Select SVG element from template
  @svg = @d3.select(@$element[0])
    .select("div")
    .select("svg")

  #Set up groups
  @svg.selectAll("g").remove()
  @arcs = @svg.append("g")
    .attr("class", "rings")
    .selectAll("g.arc")
    .data(@arcDataset)
    .enter()
    .append("g")
    .attr("class", "arc")
    .classed("selected", (d, i) -> i==0)
    .attr("transform", "translate(" + @outerRadius + "," + @outerRadius + ")")
    .on("dblclick", @_doubleClickArc)
    .on("mouseover", @_percentOnHover)
    .on("mouseout", @_setInitialCenterText)

  @arcs.append("title")
    .text (d,i) ->
      d.value + " Hours " + (if i == 0 then "Billable" else "Non-billable")

  @_setInitialCenterText()

  #Draw arc paths
  @arcs.append("path")
      .attr "fill", (d, i) => @color(i)
      .attr("d", arc)

_calcCenterRotation: (datum) ->
  # Make selected data element centered on rhs of ring graph
  elementAngle = datum["endAngle"] - datum["startAngle"]
  elementCenterAngle = datum["startAngle"] + elementAngle/2
  rotateAngle = 2*Math.PI - elementCenterAngle + Math.PI/2
  return rotateAngle
### rotateArcs: Takes a positive angle in radians and and d3ArcDataset and
mutates the data. ###
_rotateArcs: (d3ArcData, radRotation) ->
  radRotation = Math.abs(radRotation)
  for data in d3ArcData
    dTheta = data["endAngle"] - data["startAngle"]
    data["startAngle"] = (data["startAngle"] + radRotation)%(Math.PI*2)
    data["endAngle"]   =  data["startAngle"] + dTheta

_doubleClickArc: (datum) =>
  rings = @d3.select(@$element[0])
    .select("g.rings")
  @svg.selectAll("g.arc")
    .classed("selected", false)
  @d3.select(this)
    .classed("selected", true)
  rings.transition().duration(1000)
    .attrTween("transform", tween)

  tween = =>
    rotateAngle = @_calcCenterRotation(datum)*180/Math.PI
    interpolateFrom = this.getAttribute("transform") || "rotate(0, 100, 100)";
    return @d3.interpolateString(interpolateFrom, "rotate(" + rotateAngle +  ", 100, 100)")

# Add initial center percent text
_setInitialCenterText: =>
  @svg.select("text.center")
    .remove()
  data = @svg.select("g.arc.selected").datum();
  @svg.append("text")
    .attr("class", "center")
    .attr("x", @outerRadius)
    .attr("y", @outerRadius)
    .attr("dy", "0.3em")
    .text((Math.round(data.value*@toPercent) || 0) + "%")

# percent
_percentOnHover: (d) =>
  @svg.select("text.center")
    .remove()
  @svg.append("text")
    .attr("class", "center")
    .attr("x", @outerRadius)
    .attr("y", @outerRadius)
    .attr("dy", "0.3em")
    .text(Math.round(d.value * @toPercent) + "%")

_getSum: (total, num) ->
  return total + num

@register(MaterialRaingular.d3.Directives.MrD3Ring)