
//= depends: zapi, geo/parser, script_src_request

/**
 * ZAPI.Geo.JSON reads in a JSON object in ZAPI's super special internal format
 * and parses through it looking for placemarks and such. The JSON format is
 * approximately equivalent to the extended KML format we use in
 * ZAPI.Geo.KML.
 *
 * @name ZAPI.Geo.JSON
 * @constructor
 * @extends ZAPI.GeoParser
 */
ZAPI.namespace('Geo').JSON = Class.create(ZAPI.Geo.Parser,
	/** @scope ZAPI.Geo.JSON.prototype */
	{
		initialize: function(options) {
			options = $H({
			}).merge(options);

			this.bounds = undefined;
			this.json = undefined;
			this.options = options;
		},

		_parse: function(options) {
			if (!options.get('disableEvents')) {
				if (Event.fire(document, 'ZAPI.Geo.JSON:beforeParse', { '_this': this }).stopped) {
					return false;
				}
			}

			this._parseDocument(this.json['document'], options);

			if (!options.get('disableEvents')) {
				Event.fire(document, 'ZAPI.Geo.JSON:afterParse', { '_this': this });
			}
		},

		_parseDocument: function(d, options) {
			var folders = d['folders'];
			for (var i = 0; i < folders.length; i++) {
				this._parseFolder(folders[i], options);
			}
		},

		_parseFolder: function(f, options) {
			var folderId = f['id'];
			var placemarks = f['placemarks'];
			var info = this._parseFolderInfo(f,options);
			for (var i = 0; i < placemarks.length; i++) {
				this._parsePlacemark(placemarks[i], folderId, options);
			}
		},

		_parseFolderInfo: function(f, options) {
			var info = {
				'id': f['id'],
				'properties': {}
			};

			$H(f).each(function(e) {
				if (e[0] != 'placemarks') {
					this[e[0]] = e[1];
				}
			}, info['properties']);

			if (!options.get('disableEvents')) {
				Event.fire(document, 'ZAPI.Geo.JSON:createFolder', { 'info': info });
			}

			return info;
		},

		_parsePlacemark: function(p, folderId, options) {
			if (p['features']) {
				var info = this._parsePlacemarkInfo(p, folderId, options);
				if (Object.isArray(p['features'])) {
					for (var i = 0; i < p['features'].length; i++) {
						this._parseFeatureType(p['features'][i]).apply(this, [ p['features'][i], info, options ]);
					}
				}
				else {
					this._parseFeatureType(p['features']).apply(this, [ p['features'], info, options ]);
				}
			}
		},

		_parseFeatureType: function(f, options) {
			switch (f['type']) {
				case 'point':
					return this._parsePoint;
				case 'polygon':
					return this._parsePolygon;
				case 'lineString':
					return this._parseLineString;
				default:
					throw "feature type must be one of point, polygon or lineString.";
			}
		},

		_parsePlacemarkInfo: function(p, folderId, options) {
			var info = {
				'id': p['id'],
				'folder': folderId,
				'properties': {}
			};

			$H(p).each(function(e) {
				if (e[0] != 'features') {
					this[e[0]] = e[1];
				}
			}, info['properties']);

			if (!options.get('disableEvents')) {
				Event.fire(document, 'ZAPI.Geo.JSON:createPlacemark', { 'info': info });
			}

			return info;
		},

		_parsePoint: function(p, info, options) {
			var latLng = [ parseFloat(p['lat']), parseFloat(p['lng']) ];

			this._extendBounds(latLng[0], latLng[1]);

			if (options.get('createMarker')) {
				options.get('createMarker')(latLng, info);
			}
			else if (!options.get('disableEvents')) {
				Event.fire(
					document,
					'ZAPI.Geo.JSON:createMarker', {
						'_this': this,
						'latLng': latLng,
						'info': info
					}
				);
			}
		},

		_parsePolygon: function(p, info, options) {
			var polylines = p['polylines'];

			for (var i = 0; i < polylines.length; i++) {
				var polyline = p['polylines'][i];
				this._extendBounds(polyline['bounds']['sw'][1], polyline['bounds']['sw'][0]);
				this._extendBounds(polyline['bounds']['ne'][1], polyline['bounds']['ne'][0]);
			}
			info['options'] = p['options'];

			if ((info['encoded'] = !!p['encoded'])) {
				if (options.get('createPolygon')) {
					options.get('createPolygon')(polylines, info);
				}
				else if (!options.get('disableEvents')) {
					Event.fire(
						document,
						'ZAPI.Geo.JSON:createPolygon', {
							'_this': this,
							'polylines': polylines,
							'info': info
						}
					);
				}
			}
			else {
				for (var i = 0; i < polylines.length; i++) {
					if (options.get('createPolygon')) {
						options.get('createPolygon')(polylines[i]['points'], info);
					}
					else if (!options.get('disableEvents')) {
						Event.fire(
							document,
							'ZAPI.Geo.JSON:createPolygon', {
								'_this': this,
								'polylines': polylines[i]['points'],
								'info': info
							}
						);
					}
				}
			}
		},

		_parseLineString: function(p, options) {
		},

		parse: function(options) {
			this._parse($H(options));
		},

		parseUrl: function(url, options, parseOptions, requestOptions) {
			options = $H({
				'clearBounds': true
			}).merge(options);

			parseOptions = $H({
			}).merge(parseOptions);

			requestOptions = $H({
			}).merge(requestOptions);

			if ($C(options.get('clearBounds'))) {
				this.clearBounds();
			}

			if (requestOptions.get('parameters')) {
				if (url.indexOf('?') < 0) {
					url += '?';
				}
				else {
					url += '&';	
				}
			}

			var req = new ZAPI.ScriptSrcRequest(
				url + (requestOptions.get('parameters') || ''), {
					'onSuccess': function(e) {
						this.json = e;
						this._parse(parseOptions);
					}.bind(this)
				}
			);
		}
	}
);
