// https://github.com/1wheel/swoopy-drag Copyright (c) 2016 Adam Pearce

(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('d3')) :
  typeof define === 'function' && define.amd ? define(['exports', 'd3'], factory) :
  (factory((global.d3 = global.d3 || {}),global.d3));
}(this, function (exports,d3) { 'use strict';

  function swoopyDrag(){
    var x = function(d){ return d }
    var y = function(d){ return d }

    var annotations = []
    var annotationSel

    var draggable = false

    var dispatch = d3.dispatch('drag')

    var textDrag = d3.drag()
        .on('drag', function(d){
          var x = d3.event.x
          var y = d3.event.y
          d.textOffset = [x, y].map(Math.round)

          d3.select(this).call(translate, d.textOffset)

          dispatch.call('drag')
        })
        .subject(function(d){ return {x: d.textOffset[0], y: d.textOffset[1]} })

    var circleDrag = d3.drag()
        .on('drag', function(d){
          var x = d3.event.x
          var y = d3.event.y
          d.pos = [x, y].map(Math.round)

          var parentSel = d3.select(this.parentNode)

          var path = ''
          var points = parentSel.selectAll('circle').data()
          if (points[0].type == 'A'){
            path = calcCirclePath(points)
          } else{
            points.forEach(function(d){ path = path + d.type  + d.pos })
          }

          parentSel.select('path').attr('d', path).datum().path = path
          d3.select(this).call(translate, d.pos)

          dispatch.call('drag')
        })
        .subject(function(d){ return {x: d.pos[0], y: d.pos[1]} })


    var rv = function(sel){
      annotationSel = sel.html('').selectAll('g')
          .data(annotations).enter()
        .append('g')
          .call(translate, function(d){ return [x(d), y(d)] })

      var textSel = annotationSel.append('text')
          .call(translate, ƒ('textOffset'))
          .text(ƒ('text'))

      annotationSel.append('path')
          .attr('d', ƒ('path'))

      if (!draggable) return

      annotationSel.style('cursor', 'pointer')
      textSel.call(textDrag)

      annotationSel.selectAll('circle').data(function(d){
        var points = []

        if (~d.path.indexOf('A')){
          //handle arc paths seperatly -- only one circle supported
          var pathNode = d3.select(this).select('path').node()
          var l = pathNode.getTotalLength()

          points = [0, .5, 1].map(function(d){
            var p = pathNode.getPointAtLength(d*l)
            return {pos: [p.x, p.y], type: 'A'}
          })
        } else{
          var i = 1
          var type = 'M'
          var commas = 0

          for (var j = 1; j < d.path.length; j++){
            var curChar = d.path[j]
            if (curChar == ',') commas++
            if (curChar == 'L' || curChar == 'C' || commas == 2){
              points.push({pos: d.path.slice(i, j).split(','), type: type})
              type = curChar
              i = j + 1
              commas = 0
            }
          }

          points.push({pos: d.path.slice(i, j).split(','), type: type})
        }

        return points
      }).enter().append('circle')
          .attr('r', 8)
          .attr('fill', 'rgba(0,0,0,0)')
          .attr('stroke', '#333')
          .attr('stroke-dasharray', '2 2')
          .call(translate, ƒ('pos'))
          .call(circleDrag)

      dispatch.call('drag')
    }


    rv.annotations = function(_x){
      if (typeof(_x) == 'undefined') return annotations
      annotations = _x
      return rv
    }
    rv.x = function(_x){
      if (typeof(_x) == 'undefined') return x
      x = _x
      return rv
    }
    rv.y = function(_x){
      if (typeof(_x) == 'undefined') return y
      y = _x
      return rv
    }
    rv.draggable = function(_x){
      if (typeof(_x) == 'undefined') return draggable
      draggable = _x
      return rv
    }
    rv.on = function() {
      var value = dispatch.on.apply(dispatch, arguments);
      return value === dispatch ? rv : value;
    }

    return rv

    //convert 3 points to an Arc Path 
    function calcCirclePath(points){
      var a = points[0].pos
      var b = points[2].pos
      var c = points[1].pos

      var A = dist(b, c)
      var B = dist(c, a)
      var C = dist(a, b)

      var angle = Math.acos((A*A + B*B - C*C)/(2*A*B))
      
      //calc radius of circle
      var K = .5*A*B*Math.sin(angle)
      var r = A*B*C/4/K
      r = Math.round(r*1000)/1000

      //large arc flag
      var laf = +(Math.PI/2 > angle)

      //sweep flag
      var saf = +((b[0] - a[0])*(c[1] - a[1]) - (b[1] - a[1])*(c[0] - a[0]) < 0) 

      return ['M', a, 'A', r, r, 0, laf, saf, b].join(' ')
    }

    function dist(a, b){
      return Math.sqrt(
        Math.pow(a[0] - b[0], 2) +
        Math.pow(a[1] - b[1], 2))
    }


    //no jetpack dependency 
    function translate(sel, pos){
      sel.attr('transform', function(d){
        var posStr = typeof(pos) == 'function' ? pos(d) : pos
        return 'translate(' + posStr + ')' 
      }) 
    }

    function ƒ(str){ return function(d){ return d[str] } } 
  }

  exports.swoopyDrag = swoopyDrag;

  Object.defineProperty(exports, '__esModule', { value: true });

}));