
L.Polyline = L.Path.extend({
	initialize: function(latlngs, options) {
		L.Path.prototype.initialize.call(this, options);
		this._latlngs = latlngs;
	},
	
	options: {
		// how much to simplify the polyline on each zoom level
		// more = better performance and smoother look, less = more accurate
		smoothFactor: 1.0,
		noClip: false,
		
		updateOnMoveEnd: true
	},
	
	projectLatlngs: function() {
		this._originalPoints = [];
		
		for (var i = 0, len = this._latlngs.length; i < len; i++) {
			this._originalPoints[i] = this._map.latLngToLayerPoint(this._latlngs[i]);
		}
	},
	
	getPathString: function() {
		for (var i = 0, len = this._parts.length, str = ''; i < len; i++) {
			str += this._getPathPartStr(this._parts[i]);
		}
		return str;
	},
	
	getLatLngs: function() {
		return this._latlngs;
	},
	
	setLatLngs: function(latlngs) {
		this._latlngs = latlngs;
		this._redraw();
		return this;
	},
	
	addLatLng: function(latlng) {
		this._latlngs.push(latlng);
		this._redraw();
		return this;
	},
	
	spliceLatLngs: function(index, howMany) {
		var removed = [].splice.apply(this._latlngs, arguments);
		this._redraw();
		return removed;
	},
	
	closestLayerPoint: function(p) {
		var minDistance = Infinity, parts = this._parts, p1, p2, minPoint = null;

		for (var j = 0, jLen = parts.length; j < jLen; j++) {
			var points = parts[j];
			for (var i = 1, len = points.length; i < len; i++) {
				p1 = points[i-1];
				p2 = points[i];
				var point = L.LineUtil._sqClosestPointOnSegment(p, p1, p2);
				if (point._sqDist < minDistance) {
					minDistance = point._sqDist;
					minPoint = point;
				}
			}			
		}
		if (minPoint) minPoint.distance = Math.sqrt(minDistance);
		return minPoint;
	},
	
	getBounds: function() {
	    var b = new L.LatLngBounds();
	    var latLngs = this.getLatLngs();
	    for (var i = 0, len = latLngs.length; i < len; i++) {
	        b.extend(latLngs[i]);
	    }
	    return b;
	},

	_getPathPartStr: function(points) {
		var round = L.Path.VML;
		
		for (var j = 0, len2 = points.length, str = '', p; j < len2; j++) {
			p = points[j];
			if (round) p._round();
			str += (j ? 'L' : 'M') + p.x + ' ' + p.y;
		}
		return str;
	},
	
	_clipPoints: function() {
		var points = this._originalPoints,
			len = points.length,
			i, k, segment;

		if (this.options.noClip) {
			this._parts = [points];
			return;
		}
		
		this._parts = [];
		
		var parts = this._parts,
			vp = this._map._pathViewport,
			lu = L.LineUtil;
		
		for (i = 0, k = 0; i < len - 1; i++) {
			segment = lu.clipSegment(points[i], points[i+1], vp, i);
			if (!segment) continue;
			
			parts[k] = parts[k] || [];
			parts[k].push(segment[0]);
			
			// if segment goes out of screen, or it's the last one, it's the end of the line part
			if ((segment[1] != points[i+1]) || (i == len - 2)) {
				parts[k].push(segment[1]);
				k++;  
			}
		}
	},
	
	// simplify each clipped part of the polyline
	_simplifyPoints: function() {
		var parts = this._parts,
			lu = L.LineUtil;
		
		for (var i = 0, len = parts.length; i < len; i++) {
			parts[i] = lu.simplify(parts[i], this.options.smoothFactor);
		}
	},
	
	_updatePath: function() {
		this._clipPoints();
		this._simplifyPoints();
		
		L.Path.prototype._updatePath.call(this);
	}
});