/* 
    ===============================================================
    Circle Selection component
    ===============================================================
    */
    function circleSelector(svg) {
      var that = this;
      var circleCenter, circleOuter; //control points
      var circleSelected = false; //have we completed the circle?
      var dragging = false; //track whether we are dragging
      var active = false; // user can turn on/off this behavior
      var container = svg; // the container we render our points in

      // this will likely be overriden by leaflet projection
      var project = d3.geo.mercator();
      var unproject = d3.geo.mercator().invert;

      //we expose events on our component
      var dispatch = d3.dispatch("update", "clear");
      
      // The user provides an svg element to listen on events
      svg.on("mouseup.circle", function() {
        if(!active) return;
        if(dragging && circleSelected) return;
        
        var p = d3.mouse(this);
        var ll = unproject([p[0],p[1]])
        
        if(circleCenter) {
          // if we already have the circle's center and the circle
          // is finished selecting, another click means destroy the circle
          if(!circleSelected) {
            // Set the outer point
            circleOuter = ll;
            circleSelected = true;
          }
        } else {
          // We set the center to the initial click
          circleCenter = ll;
          circleOuter = ll;
        }
        // we let the user know 
        update()
      })
      svg.on("mousemove.circle", function() {
        if(!active) return;
        if(circleSelected) return;
        // we draw a guideline for where the next point would go.
        var p = d3.mouse(this);
        var ll = unproject([p[0],p[1]])
        circleOuter = ll;
        update();
      })
      
      var drag = d3.behavior.drag()
        .on("drag", function(d,i) {
          if(!active) return;
          if(circleSelected) {
            dragging = true;
            var p = d3.mouse(svg.node());
            var ll = unproject([p[0],p[1]])
            if(i) {
              circleOuter = ll;
            } else {
              var dlat = circleCenter.lat - ll.lat;
              var dlng = circleCenter.lng - ll.lng;
              circleCenter = ll;
              circleOuter.lat -= dlat;
              circleOuter.lng -= dlng;
            }
            update();
          } else {
            return false;
          }
        })
        .on("dragend", function(d) {
          // kind of a dirty hack...
          setTimeout(function() {
            dragging = false;
          },100)
        })
      
      function update(g) {
        if(g) container = g;
        if(!circleCenter || !circleOuter) return;
        var dist = distance(circleCenter, circleOuter)
        var circleLasso = container.selectAll("circle.lasso").data([dist])
        circleLasso.enter().append("circle").classed("lasso", true)
        .on("click", function() {
          if(!active) return;
          // start over
          circleCenter = null;
          circleOuter = null;
          circleSelected = false;
          container.selectAll("circle.lasso").remove();
          container.selectAll("circle.control").remove();
          container.selectAll("line.lasso").remove();
          dispatch.clear();
        })
        circleLasso
        .attr({
          cx: project(circleCenter).x,
          cy: project(circleCenter).y,
          r: dist
        })
        .style({
          stroke: "#010",
          fill: "#010",
          "fill-opacity": 0.1
        })
        
        var line = container.selectAll("line.lasso").data([circleOuter])
        line.enter().append("line").classed("lasso", true)
        
        if(!circleSelected && circleCenter || dragging) {
          line.attr({
            x1: project(circleCenter).x,
            y1: project(circleCenter).y,
            x2: project(circleOuter).x,
            y2: project(circleOuter).y
          })
          .style({
            stroke: "#111",
            "stroke-dasharray": "5 5"
          })
        } else {
          line.remove();
        }
        
        var controls = container.selectAll("circle.control")
        .data([circleCenter, circleOuter])
        controls.enter().append("circle").classed("control", true)
        controls.attr({
          cx: function(d) { return project(d).x},
          cy: function(d) { return project(d).y},
          r: 8,
          stroke: "#010",
          fill: "#b7feb7",
          "fill-opacity":0.9
        })
        .style({
          "cursor": active ? "move" : null
        })
        .call(drag)
        
        dispatch.update();
      }
      this.update = update;
      this.projection = function(val) {
        if(!val) return project;
        project = val;
        return this;
      }
      this.inverseProjection = function(val) {
        if(!val) return unproject;
        unproject = val;
        return this;
      }
      this.activate = function(val) {
        active = val;
        return this;
      }
      this.distance = function(ll) {
        if(!ll) ll = circleOuter;
        return distance(circleCenter, ll)
      }
      
      function distance(ll0, ll1) {
        var p0 = project(ll0)
        var p1 = project(ll1)
        var dist = Math.sqrt((p1.x - p0.x)*(p1.x - p0.x) + (p1.y - p0.y)*(p1.y-p0.y))
        return dist;
      }
      
      d3.rebind(this, dispatch, "on")
      return this;
    }