/**
 * Marker Collection to make group process (sort, filters, ...)
 * This object add automatically:
 * - an index for each markers inside index attribute
 * - a MapOverlay which contain multipoint HTML if necessary
 *
 * @author shulard@gmail.com
 * @version 1.0 - 2010-05-05
 * @copy Agence Interactive 2010
 */
var MapMarkerCollection = new Class({
	
	Implements: [Events, Options],

	options:
	{
		//Specific marker filters parameters
		filters:
		{
			//Position filters
			position:
			{
				//Window parameter 
				window:
				{
					latitude: 0.0005,
					longitude: 0.0005
				}
			}
		},

		behaviour:
		{
			multipoints:
			{
				container: null,
				element: null,
				token: "##CONTENT##",
				icon: null,
				event: null
			}
		},

		autoExtendBounds: true,
		startIndex: 1
	},
	
	/**
	 * MapMarkerCollection Constructor
	 * @param Hash options
	 * @return void
	 */
	initialize: function( options )
	{
		this.setOptions(options);

		this.iIndex = this.options.startIndex; //Index of markers inside current collection

		this.aShowingMarkers = new Array();
		this.aChildrenMarkers = new Array();
	},

	/**
	 * add a marker to current collection
	 * Apply no filter on it
	 * @param MapMarker oMarker
	 * @return void
	 */
	pushMarkerWithoutFilter: function ( oMarker, iIndex )
	{
		if( !oMarker instanceof MapMarker )
			throw( 'MapMarkerCollection::pushMarkerWithoutFilter -> Given Marker is not a valid MapMarker Object...');

		if( $chk(iIndex) )
			this.iIndex = iIndex;

		//Add specific data to Marker Object
		oMarker.CollectionData = {
			index: this.iIndex,
			childrenIndex: null
		};
		this.iIndex ++;
		this.aShowingMarkers.push( oMarker );
	},

	/**
	 * add a marker to current collection
	 * Filter when marker is at the exact position than another one
	 * @param MapMarker oMarker
	 * @return void
	 */
	pushMarkerWithExactPositionFilter: function ( oMarker, iIndex )
	{
		if( !oMarker instanceof MapMarker )
			throw( 'MapMarkerCollection::pushMarkerWithExactPositionFilter -> Given Marker is not a valid MapMarker Object...');

		if( $chk(iIndex) )
			this.iIndex = iIndex;

		//Add specific data to Marker Object
		oMarker.CollectionData = {
			index: this.iIndex,
			childrenIndex: null,
			parentIndex: null
		};

		var mExists = false;
		//For each markers already pushed
		this.aShowingMarkers.each(
			function( oCurrentShow, sIndex )
			{
				if( $chk(oCurrentShow) )
				{
					var oCurrentCoord = oCurrentShow.oGPSCoord;
					var oMarkerCoord = oMarker.oGPSCoord;
					//If there is another one which have the same Geoloc than given Marker
					if(
						oCurrentCoord.iLatitude == oMarkerCoord.iLatitude &&
						oCurrentCoord.iLongitude == oMarkerCoord.iLongitude
					)
						//Index is saved
						mExists = sIndex;
				}
			}
		);

		//If index is not false
		if( mExists != false )
		{
			//If this marker already have a children array
			var oData = this.aShowingMarkers[mExists].CollectionData;
			oMarker.CollectionData.parentIndex = oData.index;
			
			if( oData.childrenIndex != null )
				this.aChildrenMarkers[oData.childrenIndex].push( oMarker );
			else
			{
				//Else build a new Children array and add all markers inside
				this.aChildrenMarkers.push( new Array( this.aShowingMarkers[mExists], oMarker ) );
				
				//Update children index to simply retrieve data
				oData.childrenIndex = this.aChildrenMarkers.length - 1;
				oData.parentIndex = oData.index;
				this.aShowingMarkers[mExists].CollectionData = oData;
			}
		}
		else
		{
			//Else simply add Markers to marker list to show
			this.aShowingMarkers[this.iIndex] = oMarker;
		}

		this.iIndex ++;
	},

	/**
	 * add a marker to current collection
	 * Filter when marker is inside the position window than another one
	 * @param MapMarker oMarker
	 * @return void
	 */
	pushMarkerWithWindowPositionFilter: function ( oMarker, iIndex )
	{
		if( !oMarker instanceof MapMarker )
			throw( 'MapMarkerCollection::pushMarkerWithWindowPositionFilter -> Given Marker is not a valid MapMarker Object...');

		if( $chk(iIndex) )
			this.iIndex = iIndex;

		//Add specific data to Marker Object
		oMarker.CollectionData = {
			index: this.iIndex,
			childrenIndex: null
		};
		
		var mExists = false;
		var sLongitude = oMarker.oGPSCoord.iLongitude;
		var sLatitude = oMarker.oGPSCoord.iLatitude;
		//For each markers already pushed
		this.aShowingMarkers.each(
			function( oCurrentShow, sIndex )
			{
				if( $chk(oCurrentShow) )
				{
					//If there is another one which have the same Geoloc than given Marker
					if(
						mExists == false &&
						oCurrentShow.oGPSCoord.iLatitude > ( sLatitude - this.options.filters.position.window.latitude.toFloat() ) &&
						oCurrentShow.oGPSCoord.iLatitude < ( sLatitude + this.options.filters.position.window.latitude.toFloat() ) &&
						oCurrentShow.oGPSCoord.iLongitude > ( sLongitude - this.options.filters.position.window.longitude.toFloat() ) &&
						oCurrentShow.oGPSCoord.iLongitude < ( sLongitude + this.options.filters.position.window.longitude.toFloat() )
					)
						//Index is saved
						mExists = sIndex;
				}
			}.bind(this)
		);

		//If index is not false
		if( mExists != false )
		{
			//If this marker already have a children array
			var oData = this.aShowingMarkers[mExists].CollectionData;
			oMarker.CollectionData.parentIndex = oData.index;
			
			if( oData.childrenIndex != null )
				this.aChildrenMarkers[oData.childrenIndex].push( oMarker );
			else
			{
				//Else build a new Children array and add all markers inside
				this.aChildrenMarkers.push( new Array( this.aShowingMarkers[mExists], oMarker ) );

				//Update children index to simply retrieve data
				oData.childrenIndex = this.aChildrenMarkers.length - 1;
				oData.parentIndex = oData.index;
				this.aShowingMarkers[mExists].CollectionData = oData;
			}
		}
		else
		{
			//Else simply add Markers to marker list to show
			this.aShowingMarkers[this.iIndex] = oMarker;
		}
		
		this.iIndex ++;
	},

	/**
	 * Get Marker collection
	 * @return Array
	 */
	getCollection: function()
	{
		return this.aShowingMarkers;
	},

	/**
	 * Get Children Marker Collection
	 * @return Array
	 */
	getChildrenCollection: function( iIndex )
	{
		if( !$defined(iIndex) )
			throw('MapMarkerCollection::getChildrenCollection -> Index is not defined...')
		
		return this.aChildrenMarkers[iIndex];
	},

	/**
	 * Get Parent Marker of a Children collection
	 * @return Array
	 */
	getParent: function( iIndex )
	{
		if( !$defined(iIndex) )
			throw('MapMarkerCollection::getParent -> Index is not defined...')
		
		return this.aShowingMarkers[iIndex];
	},

	/**
	 * Get Marker from is index
	 * @return MapMarker
	 */
	getMarkerByIndex: function( iIndex )
	{
		if( !$defined(iIndex) )
			throw('MapMarkerCollection::getMarkerByIndex -> Index is not defined...')

		var oReturn = null;

		var i;
		var iLength = this.optioins.startIndex + this.aShowingMarkers.length;
		for( i = this.optioins.startIndex; i < iLength; i++ )
		{
			if( this.aShowingMarkers[i].CollectionData.childrenIndex != null )
			{
				var aChildren = this.aChildrenMarkers[this.aShowingMarkers[i].CollectionData.childrenIndex];

				var j;
				for( j = O; j < aChildren.length; j ++ )
				{
					if( aChildren[j].CollectionData.index == iIndex )
						oReturn = aChildren[j];
				}
			}
			else
			{
				if( this.aShowingMarkers[i].CollectionData.index == iIndex )
					oReturn = this.aShowingMarkers[i];
			}
		}
		
		return oReturn;
	},

	getAllValidMarkers: function()
	{
		var aTotalMarkers = new Array();
		this.aShowingMarkers.each( function( oShowableMarker )
		{
			if( $chk(oShowableMarker) )
			{
				if( oShowableMarker.CollectionData.childrenIndex == null )
					aTotalMarkers.push( oShowableMarker );
				else
				{
					var aChildren = this.getChildrenCollection( oShowableMarker.CollectionData.childrenIndex );
					if( aChildren )
					{
						aChildren.each( function( oNotShowableChildren )
						{
							aTotalMarkers.push( oNotShowableChildren );
						});
					}
				}
			}
		}.bind(this))

		return aTotalMarkers;
	},

	/**
	 * Show all markers
	 * @param google.Map oMap
	 * @return void
	 */
	showMarkers: function( oMapManager )
	{
		//Get number of Markers
		this.iEventCounter = 0;
		this.aShowingMarkers.each( function(oItem)
		{
			if( $chk(oItem) )
				this.iEventCounter++;
		}.bind(this));

		//For each showable Marker
		var i = this.options.startIndex;
		var iLength = this.options.startIndex + this.aShowingMarkers.length;
		for( i; i < iLength; i++ )
		{
			//If current marker exists in array
			if( $chk(this.aShowingMarkers[i]) )
			{
				//If there are Children Markers
				if(
					this.aShowingMarkers[i].CollectionData.childrenIndex != null &&
					this.aChildrenMarkers[this.aShowingMarkers[i].CollectionData.childrenIndex].length > 1
				)
				{
					//Init a new MultiplePoint Marker
					this.initMultiplePointsMarker(i);
				}

				//Take current Marker and if this is a valid one
				var oMarker = this.aShowingMarkers[i];
				if( oMarker != null && oMarker instanceof MapMarker )
				{
					//Add en appended Event to control next step
					oMarker.addEvent(
						'MarkerAppended',
						function MarkerAppend( oMarker )
						{
							if( this.options.autoExtendBounds )
								oMapManager.extendBounds( oMarker.getGMarker() );
							
							//When a Marker has been appended a counter is decremented
							oMarker.removeEvent( 'MarkerAppended', MarkerAppend );
							this.iEventCounter--;
							//When counter is down all Marker have been appended and an event is disptached
							if( this.iEventCounter == 0 )
								this.fireEvent('AllMarkerAppended');
						}.bind(this, oMarker)
					);

					//Build valid GMarker and Extend Bounds to center automatically Maps
					oMarker.initMarker( oMapManager.getMap() );
				}
			}
		}
	},

	/**
	 * Clear All Showable Markers
	 * @return void
	 */
	clearMarkers: function()
	{
		this.aShowingMarkers.each(
			function( oMarker )
			{
				if( $chk( oMarker ) )
				{
					if( oMarker instanceof MapMarker )
						oMarker.getGMarker().setMap(null);
					else
						throw('MapMarkerCollection::clearMarkers -> Current Marker is not a valid MapMarker');
				}
			}
		);
	},

	/**
	 * Init HTML on multiple Point when there are more than 1 marker at the same position
	 * @param Array aCurrentMarker
	 * @return String
	 */
	initMultiplePointsMarker: function( mIndex )
	{
		if(
			this.options.behaviour.multipoints.container != null &&
			this.options.behaviour.multipoints.element != null &&
			this.options.behaviour.multipoints.token != null
		)
		{
			//Build a new Valid Marker to control MultiPoint Info Overlay
			oMarker = new MapMarker(
				{
					oGPSCoord : new Hash ({
						iLatitude: this.aShowingMarkers[mIndex].oGPSCoord.iLatitude,
						iLongitude: this.aShowingMarkers[mIndex].oGPSCoord.iLongitude
					}),

					oIcon:
					{
						sIconPath: this.options.behaviour.multipoints.icon
					}
				}
			);

			//If specific events are defined on multiple point marker, function is run on Marker
			if( $chk( this.options.behaviour.multipoints.event ) )
				this.options.behaviour.multipoints.event.bind(this).run(oMarker);

			// Insertion des liens
			var sElement = "";
			this.aChildrenMarkers[this.aShowingMarkers[mIndex].CollectionData.childrenIndex].each(
				function(oMarker)
				{
					sElement += this.options.behaviour.multipoints.element.replace( 
						new RegExp(this.options.behaviour.multipoints.token, "g"),
						oMarker.CollectionData.index
					);
				}.bind(this)
			);

			var oOverlay = new MapMarkerOverlay();
			oOverlay.setContent(
				this.options.behaviour.multipoints.container.replace(
					this.options.behaviour.multipoints.token,
					sElement
				)
			);

			oMarker.CollectionData = {};
			oMarker.CollectionData.multiPointOverlay = oOverlay;
			oMarker.CollectionData.index = null;
			oMarker.CollectionData.childrenIndex = this.aShowingMarkers[mIndex].CollectionData.childrenIndex;
			oMarker.CollectionData.parentIndex = null;

			this.aShowingMarkers[mIndex] = oMarker;
		}
		else
			throw('MapMarkerCollection::initMultiplePointsMarker -> No HTML Content given to build multipoints...');
	}
});
