// vim: sw=4:ts=4:nu:nospell

/**
 * HyperCities Marker Overlay Object
 *
 * @author    Chen-Kuei (James) Lee
 * @copyright (c) 2008, by HyperCities Tech Team
 * @date      2009-07-07
 * @version   0.7
 *
 */

// Create Namespace
var GHCMarker = function($map, $dom, $opts) {
    this._id        = "[GHCMarker] ";
	this._map       = $map;
	this._dom       = $dom;
	this._opts      = $opts || {};

	// Global variable for inline style
	this._icon      = null;
	this._lineStyle = null;
	this._polyStyle = null;

	// Global variable for Marker
	this._type      = null;
	this._data      = {};
	this._marker    = null;
	this._polyline  = null;
	this._polygon   = null;
	this._points    = [];
	this._listener  = [];
	this._added     = false;
	this._bounds    = new GLatLngBounds();

	// Global variable for Image Preload
	this._images    = [];
	this._imageNum  = 0;

	// Constant for marker 
	this.MARKER     = 0;
	this.POLYLINE   = 1;
	this.POLYGON    = 2;

	// Create marker for given dom
	this._parseDom();
};

// Prototyping GOverlay interface 
GHCMarker.prototype = new GOverlay();

/**
 * Utility to get tag value as a string
 */
GHCMarker.prototype._getTagValue = function($parentDom, $tag) {
	var str = "",
		tempNode = null,
		nodeList = $parentDom.getElementsByTagName($tag);

    if (nodeList.length > 0) {
        tempNode = nodeList[0].firstChild;
        // iterate through the "nextSibling" for extra-long nodes
        while (tempNode !== null) {
            str += tempNode.nodeValue;
            tempNode = tempNode.nextSibling;
        }
    }
    return jQuery.trim(str);
};

GHCMarker.prototype._createHCIcon = function() {

	var HCMarkerPath = 'images/HCMarker.png',
		HCShadowPath = 'images/HCMarkerShadow.png',
		HCIcon       = new GIcon(G_DEFAULT_ICON, HCMarkerPath);

	HCIcon.iconSize = new GSize(40, 50);
	HCIcon.iconAnchor = new GPoint(20, 25);
	HCIcon.shadow = HCShadowPath;
	HCIcon.shadowSize = new GSize(53, 36);
	HCIcon.infoWindowAnchor = new GPoint(20, 6);
	HCIcon.imageMap = [0,0, 50,0, 50,50, 0,50];

	return HCIcon;
};

GHCMarker.prototype._createIcon = function($href) {

	var style = {}, proxyLink, iconImage, linkDomain;

	if ( typeof($href) === "string") {
		linkDomain = $href.split(/\/+/g)[1];

		// We don't need to proxy icon provided by Google map or ATS
		if ( linkDomain === "maps.google.com" || linkDomain === "gis.ats.ucla.edu" ) {
			proxyLink = $href;
		}
		else {
			proxyLink = "./imageProxy.php?url=" + encodeURIComponent($href);
		}

		// Preload the image and detect the size
		iconImage = new Image();
		iconImage.onload = function () { 
			style.iconSize = new GSize(this.width, this.height); 
			style.imageMap = [0,0, this.width,0, this.width,this.height, 0,this.height];
		};
		iconImage.src = proxyLink;

		style = new GIcon(G_DEFAULT_ICON, proxyLink);
		style.iconSize = new GSize(32,32);
		style.shadowSize = new GSize(59,32);
		style.dragCrossAnchor = new GPoint(2,8);
		style.iconAnchor = new GPoint(16,16);
		style.imageMap = [0,0, 32,0, 32,32, 0,32];
		style.type = "IconStyle";
  	    style.href = $href;

		// Try to guess the shadow image
		if ( $href.indexOf("/red.png")>-1 
			|| $href.indexOf("/blue.png")>-1 
			|| $href.indexOf("/green.png")>-1 
			|| $href.indexOf("/yellow.png")>-1 
			|| $href.indexOf("/lightblue.png")>-1 
			|| $href.indexOf("/purple.png")>-1 
			|| $href.indexOf("/pink.png")>-1 
			|| $href.indexOf("/orange.png")>-1 
			|| $href.indexOf("-dot.png")>-1 ) {
			style.shadow = "http://maps.google.com/mapfiles/ms/micons/msmarker.shadow.png";
		}
		else if ( $href.indexOf("-pushpin.png")>-1
				|| $href.indexOf("/pause.png")>-1 
				|| $href.indexOf("/go.png")>-1    
				|| $href.indexOf("/stop.png")>-1 ) {
			style.shadow = "http://maps.google.com/mapfiles/ms/micons/pushpin_shadow.png";
		}
		else if ( proxyLink.indexOf(".jpg") ) {
			style.shadow = ""; 
		}
		else {
			style.shadow = proxyLink.replace(".png",".shadow.png");
		}
	}

	return style;
};

GHCMarker.prototype._createMarker = function($point) {
//	HyperCities.debug(this._id + "[A2] Create Marker");

	var marker, markerOptions, markerIcon, self = this;

	// Get Marker's Icon
	if ( this._icon instanceof GIcon ) {
		markerIcon = this._icon; 
	}
	else {
		markerIcon = this._createHCIcon();
	}

	if ( $point instanceof GLatLng ) {
		// Create GMarker instance
		markerOptions = { title : this._data.name,
						  icon  : markerIcon
						};

		marker = new GMarker($point, markerOptions);
		marker.id = this._data.id;
		marker.title = this._data.name;

		// Add Event Listener
		this._listener.push( GEvent.addListener(marker, "click", function() {
				var currentMode = HyperCities.session.get("mode");
				
				if ( currentMode === HyperCities.config.MODE_NARRATIVE ) {
					HyperCities.narrativePanel.showInfoWindow(marker.id);
				}
				else {
					HyperCities.collectionList.showInfoWindow(marker.id);
				}
				return false;
			}));

		this._listener.push( GEvent.addListener(marker, "mouseover", function() {
				var currentMode = HyperCities.session.get("mode");
				
				if ( currentMode === HyperCities.config.MODE_NARRATIVE ) {
					HyperCities.narrativePanel.highlightItem(marker.id);
				}
				else {
					HyperCities.collectionList.highlightItem(marker.id);
				}
				return false;
			}));

		this._listener.push( GEvent.addListener(marker, "mouseout", function(latLng) {
				var currentMode = HyperCities.session.get("mode");
				
				if ( currentMode === HyperCities.config.MODE_NARRATIVE ) {
					HyperCities.narrativePanel.unHighlightItem(marker.id);
				}
				else {
					HyperCities.collectionList.unHighlightItem(marker.id);
				}
				return false;
			}));

		this._marker = marker;
	}
};

GHCMarker.prototype._createPolyline = function($points) {
//	HyperCities.debug(this._id + "[A2] Create Polyline");
	
	var polyline, lineStyle = {}, self = this;

	// Get lineStyl
	if ( this._lineStyle !== null ) {
		lineStyle.width = this._lineStyle.width;
		lineStyle.color = this._lineStyle.color;
		lineStyle.opacity = this._lineStyle.opacity;
	}
	else {
		lineStyle.width = 5;
		lineStyle.color = "#0000ff";
		lineStyle.opacity = 0.45;
	}

	// Create GPolyline instance
	polyline = new GPolyline($points, lineStyle.color, lineStyle.width, lineStyle.opacity);
	polyline.id = this._data.id;
	polyline.title = this._data.name;

	// Add Event Listener
	this._listener.push( GEvent.addListener(polyline, "click", function() {
			HyperCities.collectionList.showInfoWindow(polyline.id);
		}));

	this._polyline = polyline;
};

GHCMarker.prototype._createPolygon = function($points) {
//	HyperCities.debug(this._id + "[A2] Create Polygon");
	
	var polygon, polyStyle = {}, self = this;

	// Get polyStyle
	if ( this._polyStyle !== null ) {
		polyStyle.width = this._polyStyle.width;
		polyStyle.color = this._polyStyle.color;
		polyStyle.opacity = this._polyStyle.opacity;
		polyStyle.fillColor = this._polyStyle.fillColor;
		polyStyle.fillOpacity = this._polyStyle.fillOpacity;
	}
	else {
		polyStyle.width = 5;
		polyStyle.color = "#0000ff";
		polyStyle.opacity = 0.45;
		polyStyle.fillColor = "#0055ff";
		polyStyle.fillOpacity = 0.25;
	}

	// Create GPolyline instance
	polygon = new GPolygon($points, polyStyle.color, polyStyle.width, polyStyle.opacity, polyStyle.fillColor, polyStyle.fillOpacity);
	polygon.id = this._data.id;
	polygon.title = this._data.name;

	// Add Event Listener
	this._listener.push( GEvent.addListener(polygon, "click", function() {
			HyperCities.collectionList.showInfoWindow(polygon.id);
		}));

	this._polygon = polygon;
};

GHCMarker.prototype._parseStyle = function($styleDom) {

	var i, len, style = {},
		icons, href,
		lineStyles, width, color, aa, bb, gg, rr, opacity,
		polyStyles, fill, outline, colorMode;

	// Is it a Icon in iconStyle?
	icons = $styleDom.getElementsByTagName("Icon");
	if (icons.length > 0) {
        href = this._getTagValue(icons[0], "href");
		this._icon = this._createIcon(href);
	}

	// is it a LineStyle ?
	lineStyles = $styleDom.getElementsByTagName("LineStyle");
	if (lineStyles.length > 0) {
        width = parseInt(this._getTagValue(lineStyles[0], "width"), 10);
        if (width < 1) {width = 5;}

		color = this._getTagValue(lineStyles[0], "color"),
		colorMode = this._getTagValue(lineStyles[0], "colorMode");
        aa = color.substr(0,2);
        bb = color.substr(2,2);
        gg = color.substr(4,2);
        rr = color.substr(6,2);

		style.type = "LineStyle";
        style.width = width;
		style.color = "#" + rr + gg + bb;
        style.opacity = parseInt(aa,16)/256;

		this._lineStyle = style;
	}

	// Is it a PolyStyle ?
	polyStyles = $styleDom.getElementsByTagName("PolyStyle");
	if (polyStyles.length > 0) {
		if ( polyStyles[0].getElementsByTagName("fill").length === 0 ) {
			fill = 1;
		}
		else {
			fill = parseInt(this._getTagValue(polyStyles[0], "fill"), 10);
		}

		if ( polyStyles[0].getElementsByTagName("outline").length === 0 ) {
			outline = 1;
		}
		else {
			outline = parseInt(this._getTagValue(polyStyles[0], "outline"), 10);
		}

		color = this._getTagValue(polyStyles[0], "color");
		colorMode = this._getTagValue(polyStyles[0], "colorMode");
		aa = color.substr(0,2);
		bb = color.substr(2,2);
		gg = color.substr(4,2);
		rr = color.substr(6,2);

		style.type = "PolyStyle";
        style.fill = fill;
        style.outline = outline;
		style.fillColor = "#" + rr + gg + bb;
        style.fillOpacity = parseInt(aa,16)/256;
		if (!fill) { style.fillOpacity = 0; }
		if (!outline) { style.opacity = 0; }

		this._polyStyle = style;
	}

	// Is it a LabelStyle? Not Implement

	// Is it a ListStyle? Not Implement
	
	// Is it a BalloonStyle? Not Implement
};

/**
 * Dom Parser to Create Marker/Polylines/Polygons from Placemark
 */
GHCMarker.prototype._parseDom = function() {

	var xml = this._dom, data = {},
		i, len, flink, coords, styles, path,
		text, stuff, results = [], patterns, imageUrl,
		bits, point, pointArray = [];

	data.id = xml.getAttribute("id");
	data.name = this._getTagValue(xml, "name");
	data.desc = this._getTagValue(xml, "description");
	data.visible = (this._getTagValue(xml, "visibility") === '1');
	data.styleId = this._getTagValue(xml, "styleUrl");

	// Try to get "description" from BalloonStyle
	if ( data.desc === "" ) {
        data.desc = this._getTagValue(xml, "text");
        data.desc = data.desc.replace(/\$\[name\]/, data.name);
        data.desc = data.desc.replace(/\$\[geDirections\]/,"");
	}

	// If description is still empty, set it as name
	if ( data.desc === "" ) {
		data.desc = data.name;
	}

	// Add Anchor if description is Link
	if ( data.desc.match(/^http:\/\//i) || data.desc.match(/^https:\/\//i) ) {

		flink = data.desc.split(/(\s)+/);
		len   = flink.length;

		if ( len > 1 ) {
			data.desc = '<a href="' + flink[0] + '">' + flink[0] + '</a>';
			for (i=1; i<len; i++) {
				data.desc += flink[i];
			}
		}
		else {
			data.desc = '<a href="' + data.desc + '">' + data.desc + '</a>';
		}
	}


	// Attempt to preload images in description field
	text = data.desc
	patterns = [/<\s*img/ig, /src\s*=\s*[\'\"]/, /[\'\"]/];

	while ((results[0] = patterns[0].exec(text)) !== null) {
		stuff = text.substr(results[0].index);
		results[1] = patterns[1].exec(stuff);
		if (results[1] !== null) {
			stuff = stuff.substr(results[1].index+results[1][0].length);
			results[2] = patterns[2].exec(stuff);
			if (results[2] !== null) {
				imageUrl = stuff.substr(0,results[2].index);
				this._images[this._imageNum] = new Image();
				this._images[this._imageNum].src = imageUrl;
				this._imageNum++;
			}
		}
	}

	this._data = data;

	// Get Inline Style
	styles = xml.getElementsByTagName("Style");
	len = styles.length;
	for (i=0; i<len; i++) {
		this._parseStyle(styles[i]);
	}

	// TODO:: Get StyleMap 

	// Get Coordinates
	coords = this._getTagValue(xml, "coordinates");
	coords = coords.replace(/\s+/g," "); // tidy the whitespace
	coords = coords.replace(/^ /,"");    // remove possible leading whitespace
	coords = coords.replace(/ $/,"");    // remove possible trailing whitespace
	coords = coords.replace(/, /,",");   // tidy the commas

	path = coords.split(" ");

	len = path.length ;
	// Marker has only one coordinates
	if ( len === 1 || path[1] === "") {
		// Make sure we have Point Tag
		if ( xml.getElementsByTagName("Point").length > 0 ) {

			bits  = path[0].split(",");
			this._points[0] = new GLatLng(parseFloat(bits[1]), parseFloat(bits[0]));
			this._type = this.MARKER;
		}
	} 
	else if ( len > 1 ) {
		// Build the list of points
		for (i=0; i<len; i++) {
			bits  = path[i].split(",");
			point = new GLatLng(parseFloat(bits[1]), parseFloat(bits[0]));

			this._points.push(point);
			this._bounds.extend(point);
		}

		// It's Polyline
		if ( xml.getElementsByTagName("LineString").length > 0 ) {
			this._type = this.POLYLINE;
		}

		// It's Polygon
		if ( xml.getElementsByTagName("Polygon").length > 0 ) {
			this._type = this.POLYGON;
		}
	}
}

/**
 * Called when map.addOverlay() is called. Add Overlay to Map
 */ 
GHCMarker.prototype.initialize = function() {

	switch (this._type) {
		case this.MARKER   : this._createMarker(this._points[0]);
							 if ( this._marker instanceof GMarker ) {
								 this._map.addOverlay(this._marker);
							 };
							 break;

		case this.POLYLINE : this._createPolyline(this._points);
							 if ( this._polyline instanceof GPolyline ) {
								 this._map.addOverlay(this._polyline);
							 }
							 break;

		case this.POLYGON  : this._createPolygon(this._points);
							 if ( this._polygon instanceof GPolygon ) {
								 this._map.addOverlay(this._polygon);
							 }
							 break;
		default            : break;
	}

	this._added = true ;
}

/*
 * Called when map.removeOverlay is called, individually removes each overlay
 */
GHCMarker.prototype.remove = function (map) {

	var len;

	// Remove Overlay
	if ( this._marker instanceof GMarker ) {
		this._map.removeOverlay(this._marker);
	}

	if ( this._polyline instanceof GPolyline ) {
		this._map.removeOverlay(this._polyline);
	}

	if ( this._polygon instanceof GPolygon ) {
		this._map.removeOverlay(this._polygon);
	}

	// Remove Listener
	len = this._listener.length;
	while ( len-- > 0 ) {
		GEvent.removeListener(this._listener[len]);	
	}

	// Clean up variables
	this._map       = null;
	this._dom       = null;
	this._opts      = null;
	this._icon      = null;
	this._lineStyle = null;
	this._polyStyle = null;
	this._type      = null;
	this._data      = {};
	this._marker    = null;
	this._polyline  = null;
	this._polygon   = null;
	this._points    = [];
	this._listener  = [];
	this._added     = false;
	this._bounds    = null;
	this._images    = [];
	this._imageNum  = 0;
}

GHCMarker.prototype.hide = function() {

	if ( !this._added ) {
		return;	
	}

	if ( this._marker instanceof GMarker ) {
		this._marker.hide();
	}
	else if ( this._polyline instanceof GPolyline ) {
		this._polyline.hide();
	}
	else if ( this._polygon instanceof GPolygon ) {
		this._polygon.hide();
	}
}

GHCMarker.prototype.show = function() {

	if ( !this._added ) {
		this.initialize()
	}

	if ( this._marker instanceof GMarker ) {
		this._marker.show();
	}
	else if ( this._polyline instanceof GPolyline ) {
		this._polyline.show();
	}
	else if ( this._polygon instanceof GPolygon ) {
		this._polygon.show();
	}
}

GHCMarker.prototype.getLatLng = function() {

	if ( this._marker instanceof GMarker ) {
		return this._marker.getLatLng();
	}
	else if ( this._polyline instanceof GPolyline ) {
		return this._polyline.getVertex(Math.floor(this._polyline.getVertexCount()/2));	
	}
	else if ( this._polygon instanceof GPolygon ) {
		return this._bounds.getCenter();
	}
	return false;
}

//not implemented
GHCMarker.prototype.redraw = function()
{
	//not implemented as the current functionality does not exactly need the bounds of the window
}

//not implemented
GHCMarker.prototype.copy = function()
{
	//not implemented as the current implementation is just for adding the data to the map
}
