/*
 * L.Path is a base class for rendering vector paths on a map. It's inherited by Polyline, Circle, etc.
 */

L.Path = L.Class.extend({
	includes: [L.Mixin.Events],
	
	statics: (function() {
		var svgns = 'http://www.w3.org/2000/svg',
			ce = 'createElementNS';
		
		return {
			SVG_NS: svgns,
			SVG: !!(document[ce] && document[ce](svgns, 'svg').createSVGRect),
			
			// how much to extend the clip area around the map view 
			// (relative to its size, e.g. 0.5 is half the screen in each direction)
			CLIP_PADDING: 0.5
		};
	})(),
	
	options: {
		stroke: true,
		color: '#0033ff',
		weight: 5,
		opacity: 0.5,
		
		fill: false,
		fillColor: null, //same as color by default
		fillOpacity: 0.2,
		
		clickable: true,
		
		updateOnMoveEnd: false
	},
	
	initialize: function(options) {
		L.Util.setOptions(this, options);
	},
	
	onAdd: function(map) {
		this._map = map;
		
		this._initElements();
		this._initEvents();
		this.projectLatlngs();
		this._updatePath();

		map.on('viewreset', this.projectLatlngs, this);
		
		this._updateTrigger = this.options.updateOnMoveEnd ? 'moveend' : 'viewreset';
		map.on(this._updateTrigger, this._updatePath, this);
	},
	
	onRemove: function(map) {
		map._pathRoot.removeChild(this._container);
		map.off('viewreset', this._projectLatlngs, this);
		map.off(this._updateTrigger, this._updatePath, this);
	},
	
	projectLatlngs: function() {
		// do all projection stuff here
	},
	
	getPathString: function() {
		// form path string here
	},
	
	setStyle: function(style) {
		L.Util.setOptions(this, style);
		if (this._path) {
			this._updateStyle();
		}
	},
	
	_initElements: function() {
		this._initRoot();
		this._initPath();
		this._initStyle();
	},
	
	_initRoot: function() {
		if (!this._map._pathRoot) {
			this._map._pathRoot = this._createElement('svg');
			this._map._panes.overlayPane.appendChild(this._map._pathRoot);

			this._map.on('moveend', this._updateSvgViewport, this);
			this._updateSvgViewport();
		}
	},
	
	_updateSvgViewport: function() {
		this._updateViewport();
		
		var vp = this._map._pathViewport,
			min = vp.min,
			max = vp.max,
			width = max.x - min.x,
			height = max.y - min.y,
			root = this._map._pathRoot,
			pane = this._map._panes.overlayPane;
	
		// Hack to make flicker on drag end on mobile webkit less irritating
		// Unfortunately I haven't found a good workaround for this yet
		if (L.Browser.mobileWebkit) { pane.removeChild(root); }
		
		L.DomUtil.setPosition(root, min);
		root.setAttribute('width', width);
		root.setAttribute('height', height);
		root.setAttribute('viewBox', [min.x, min.y, width, height].join(' '));
		
		if (L.Browser.mobileWebkit) { pane.appendChild(root); }
	},
	
	_updateViewport: function() {
		var p = L.Path.CLIP_PADDING,
			size = this._map.getSize(),
			//TODO this._map._getMapPanePos()
			panePos = L.DomUtil.getPosition(this._map._mapPane), 
			min = panePos.multiplyBy(-1).subtract(size.multiplyBy(p)),
			max = min.add(size.multiplyBy(1 + p * 2));
		
		this._map._pathViewport = new L.Bounds(min, max);
	},
	
	_initPath: function() {
		this._container = this._createElement('g');
		
		this._path = this._createElement('path');
		this._container.appendChild(this._path);
		
		this._map._pathRoot.appendChild(this._container);
	},
	
	_initStyle: function() {
		if (this.options.stroke) {
			this._path.setAttribute('stroke-linejoin', 'round');
			this._path.setAttribute('stroke-linecap', 'round');
		}
		if (this.options.fill) {
			this._path.setAttribute('fill-rule', 'evenodd');
		} else {
			this._path.setAttribute('fill', 'none');
		}
		this._updateStyle();
	},
	
	_updateStyle: function() {
		if (this.options.stroke) {
			this._path.setAttribute('stroke', this.options.color);
			this._path.setAttribute('stroke-opacity', this.options.opacity);
			this._path.setAttribute('stroke-width', this.options.weight);
		}
		if (this.options.fill) {
			this._path.setAttribute('fill', this.options.fillColor || this.options.color);
			this._path.setAttribute('fill-opacity', this.options.fillOpacity);
		}
	},
	
	_updatePath: function() {
		var str = this.getPathString();
		if (!str) {
			// fix webkit empty string parsing bug
			str = 'M0 0';
		}
		this._path.setAttribute('d', str);
	},
	
	_createElement: function(name) {
		return document.createElementNS(L.Path.SVG_NS, name);
	},
	
	// TODO remove duplication with L.Map
	_initEvents: function() {
		if (this.options.clickable) {
			if (!L.Path.VML) {
				this._path.setAttribute('class', 'leaflet-clickable');
			}
			
			L.DomEvent.addListener(this._container, 'click', this._onMouseClick, this);

			var events = ['dblclick', 'mousedown', 'mouseover', 'mouseout'];
			for (var i = 0; i < events.length; i++) {
				L.DomEvent.addListener(this._container, events[i], this._fireMouseEvent, this);
			}
		}
	},
	
	_onMouseClick: function(e) {
		if (this._map.dragging && this._map.dragging.moved()) { return; }
		this._fireMouseEvent(e);
	},
	
	_fireMouseEvent: function(e) {
		if (!this.hasEventListeners(e.type)) { return; }
		this.fire(e.type, {
			latlng: this._map.mouseEventToLatLng(e),
			layerPoint: this._map.mouseEventToLayerPoint(e)
		});
		L.DomEvent.stopPropagation(e);
	},
	
	_redraw: function() {
		this.projectLatlngs();
		this._updatePath();
	}
});