var Segment = Class.extend({	
	
  //Constructor	
  init: function(map, renderFactory, significantPoints, renderOnlyPointArcs, postSegmentDelay){
	this.map = map;
	
	this.renderFactory = renderFactory;

	//array of significant points that shouldn't be disposed of during direction routing
	this.significantPoints = significantPoints;
	
	//array of arrays of points used only for rendering (direction results)
	this.renderOnlyPointArcs = renderOnlyPointArcs;
	
	//millisecond delay before next segment begins
	this.postSegmentDelay = postSegmentDelay; 
	
	//reference to polyline rendering this segment on the map
	this.polyline = null;
	
	//generate our initial poly line
	this.updatePolyline();
  },
  
  detach : function() {
	  this.polyline.setMap(null);
  },
  
  setMouseMoveHandler : function(f) {
	  this.mouseMoveHandler = f;      
      google.maps.event.addListener(this.polyline, 'mousemove', this.mouseMoveHandler);                      
  },
  
  getPointClosestDataTo : function(latLng) {	   
	  var lastPoint = null;      
      var closestDistance = Number.MAX_VALUE;    
      var closestSignificantPointIndex = null;
      var closestRenderOnlyPointArcIndex = null;
	  
      function updateClosest(point, significantPointIndex, renderOnlyPointArcIndex)
      {
         var distance = distanceFromPointToLineSegment(lastPoint, point, latLng); //bugbug function from edit.js
         
         var good = false;
         
         if(distance < closestDistance) {
            closestDistance = distance;                      
            closestSignificantPointIndex = significantPointIndex;      
            closestRenderOnlyPointArcIndex = renderOnlyPointArcIndex;
            good = true;
         }
                           
         lastPoint = point;
      }      
            
      var segment = this;
                  
      $.each(segment.significantPoints, function(significantPointIndex, significantPoint) {    	      	  
          if(significantPointIndex > 0) {
             $.each(segment.renderOnlyPointArcs[significantPointIndex], function(renderOnlyPointArcIndex, renderOnlyPoint) {           
                updateClosest(renderOnlyPoint, significantPointIndex, renderOnlyPointArcIndex);                                
             }); 
          }
                    
          updateClosest(significantPoint, significantPointIndex, null);          
      });  
      
      return {    	  
    	  closestDistance : closestDistance,
    	  closestSignificantPoint : this.significantPoints[closestSignificantPointIndex],
    	  closestSignificantPointIndex : closestSignificantPointIndex,
    	  closestRenderOnlyPointArc : this.renderOnlyPointArcs[closestSignificantPointIndex],
    	  closestRenderOnlyPointArcIndex : closestRenderOnlyPointArcIndex    	  
      };
  },
  
  getSignificantPoint : function(index) {
	  return this.significantPoints[index];
  },
   
  getSignificantPoints : function() {
	  return this.significantPoints.slice(0);
  },
  
  getRenderOnlyPointArc : function(index) {
	  return this.renderOnlyPointArcs[index];
  },
  
  setRenderOnlyPointArc : function(index, arc) {
	  this.renderOnlyPointArcs[index] = arc;
  },
  
  //Returns a single list of all points, significant & otherwise
  getPoints : function() {
	  var points = [];
   
	  var segment = this;
	  $.each(this.significantPoints, function(index, point) {				  
		  points = points.concat(segment.renderOnlyPointArcs[index], [point]);
	  });
   	  
	  return points;
  },
  
  //have to have "onComplete" callback due to google returning async
  appendSignificantPoint : function(point, interpolateRoad, onComplete) {
	  var length = this.significantPoints.push(point);
	  this.renderOnlyPointArcs.push([]);
	  this.changePositionOfSignificantPoint(length - 1, point, interpolateRoad, onComplete);
  },
  
  removeLastSignificantPoint : function() {
	  var lastPoint = this.significantPoints.pop();
	  this.renderOnlyPointArcs.pop();
	  this.updatePolyline();
	  return lastPoint;
  },
  
  insertSignficantPointAtIndex : function(significantPointIndex, latLng, onComplete) {
	  this.significantPoints.splice(significantPointIndex, 0, latLng);
	  this.renderOnlyPointArcs.splice(significantPointIndex, 0, []);
	  onComplete(latLng);
	  this.updatePolyline();
  },
  
  removeSignificantPointAtIndex : function(significantPointIndex, onComplete) {
	  var removedPoint = this.significantPoints.splice(significantPointIndex, 1)[0];
	  this.renderOnlyPointArcs.splice(significantPointIndex, 1);	  
	  onComplete(removedPoint);
	  this.updatePolyline();	  
  },
    
  changePositionOfSignificantPoint : function(significantPointIndex, latLng, interpolateRoad, onComplete) {	  
	  
	  //because google callback loses 'this' context 
      var thisSegment = this; 
	  
	  function newOnComplete() {		  		  		  		  
		  onComplete(thisSegment.significantPoints[significantPointIndex], significantPointIndex);
		  thisSegment.updatePolyline();		  
	  }
	 	  
	  //if there is only one point, or we don't care about interpolatedRoads, force it
	  if(!interpolateRoad || this.significantPoints.length == 1) {
		  this.significantPoints[significantPointIndex] = latLng;
		  this.renderOnlyPointArcs[significantPointIndex] = [];
		  if(significantPointIndex < this.significantPoints.length - 1)
			  this.renderOnlyPointArcs[significantPointIndex + 1] = [];
			  
		  newOnComplete();
	  } 
	  else { //there are at least 2 points, and we want interpolated roads
		  
		   var origin = null;
           var destination = null;
           var waypoints = [];

           if(significantPointIndex == 0) { //first point being changed
              origin = latLng;
              destination = this.significantPoints[significantPointIndex + 1];
           }
           else if(significantPointIndex == this.significantPoints.length - 1) { //last point being changed
              destination = latLng;
              origin = this.significantPoints[significantPointIndex - 1];               
           }
           else { //something in the middle
              origin = this.significantPoints[significantPointIndex - 1];
              destination = this.significantPoints[significantPointIndex + 1];
              waypoints.push({
                 location : latLng,
                 stopover : true //split route in two
              });                              
           }
           		  
		   new google.maps.DirectionsService().route(
			  {
				  origin : origin,
	              destination : destination,
	              waypoints : waypoints,
		          travelMode : google.maps.DirectionsTravelMode.WALKING
			  }, 
			  function(result, status) {
				  if (status == google.maps.DirectionsStatus.OK) {    
					  
					  function legToRenderArc(leg) {
						  var points = [];
						  $.each(leg.steps, function(stepIndex, step) {     
							  points = points.concat(step.path);
	                      });
						  
						  return points;
					  }
					  
					  if(significantPointIndex == 0) { //first point modified
						  var renderArc = legToRenderArc(result.routes[0].legs[0]);
						  var newFirstPoint = renderArc.shift(); //grab our new first point
												  
						  thisSegment.significantPoints[0] = newFirstPoint;
						  thisSegment.renderOnlyPointArcs[1] = renderArc;
					  }
					  else if(significantPointIndex == thisSegment.significantPoints.length - 1) { //last point being changed
						  var renderArc = legToRenderArc(result.routes[0].legs[0]);
						  var newEndPoint = renderArc.pop(); //grab our new end point;						 
						  
						  thisSegment.significantPoints[significantPointIndex] = newEndPoint;
						  thisSegment.renderOnlyPointArcs[significantPointIndex] = renderArc;
					  }
					  else { //something in the middle
						  var leadingRenderArc = legToRenderArc(result.routes[0].legs[0]);
						  var trailingRenderArc = legToRenderArc(result.routes[0].legs[1]);
						  
						  thisSegment.significantPoints[significantPointIndex] = latLng;
						  thisSegment.renderOnlyPointArcs[significantPointIndex] = leadingRenderArc;
						  thisSegment.renderOnlyPointArcs[significantPointIndex + 1] = trailingRenderArc;
					  }					  					
				  } 
				  
				  newOnComplete();				  
			 }
		 );  
	  }
  },
  
  serialize : function() {	  
	  var result = "";
	  var segment = this;	    	      	     	  	  
	  var lastPoint = null;  
	  $.each(segment.significantPoints, function(significantPointIndex, significantPoint){    
		  var points = segment.renderOnlyPointArcs[significantPointIndex].concat(significantPoint);		  
		  $.each(points, function(pointIndex, point){
			  var latitude = point.lat();
			  var longitude = point.lng();
			  var deltaTime = point.deltaTime ? point.deltaTime : ""; //must be "empty" not zero or null. Empty means "we don't know, you interpolate, server!"
			  
			  var deltaPause = "0";
			  var type = "T";
			  
			  if(significantPointIndex == segment.significantPoints.length - 1 && pointIndex == points.length - 1) {//end of segment
				  type = "E"; //E == end of segment			
			  }		
			  else if(significantPointIndex == 0 && pointIndex == 0 && segment.postSegmentDelay) { //beginning of segment				  
				  deltaPause = segment.postSegmentDelay;
			  }
			  else if(pointIndex == points.length - 1) { //significant point
				  type = "M";
			  }
			  			  			  
			  var deltaDistance = (lastPoint != null ? parseFloat(distHaversine(lastPoint, point)) : 0);
		 
			  result += type + "," +
			  	latitude + "," + 
			  	longitude + "," +
			  	deltaTime + "," + 
			  	deltaPause + "," + 
			  	deltaDistance + ";";	
			  
			  lastPoint = point;
		  });  
	  });
	  
	  return result;
  },
  
  //PRIVATE METHODS:  
  //bugbug we may not need wholesale path reset and can tackle this more tactically
  updatePolyline : function() {
	  if(!this.polyline) {	             
		  this.polyline = new google.maps.Polyline(this.renderFactory.createActivityPolyOptions());
          this.polyline.setMap(this.map);
          
          if(this.mouseMoveHandler) {
              google.maps.event.addListener(this.polyline, 'mousemove', this.mouseMoveHandler);                
          }
       }
	             
       this.polyline.setPath(this.getPoints());  
  }
});
