/**
 *  ****************************************************************************
 *  File : design/previmer/javascript/previmer_currentstate.js
 *  vers : 1.1
 *  build: 20070731
 *  Author : Virtualys
 *  ****************************************************************************
 **/

/**
 * ============================================================================= 
 * CurrentState
 * Etat actuel du choix de l'utilisateur.
 * ============================================================================= 
 */ 
CurrentState = function(initState)
{
	// Etats initiaux
	this._initialState = initState;
	// Thème en cours
	this.theme = initState.theme;
	// Objet Application en cours
	this.application = null;
	// Objet Variable en cours
	this.variable = null;
	// Objet ResultType en cours
	this.resultType = null;
	// Objet Area en cours
	this.area = null;
	// ID de la profondeur en cours
	this.depth = null;
	// Objet Date en cours (l'heure est fixée à minuit)
	this.date = null;
	// Décalage en millisecondes dans la journee en h locale indiquant l'heure choisie
	// dans la liste déroulante, 0 si non applicable (heure masquee)
	this.hourOffset = 0;
	// Objet AnimationSettings en cours ou null
	this.animation = null;
	// Item géométrique
	this.geoItem = null;
	// état de l'application Flash
	this.flashState = null;
}
CurrentState.TYPE_RESULT = 0;
CurrentState.TYPE_APP_INFOS = 1;
CurrentState.TYPE_BULLETIN = 2;
CurrentState.TYPE_ANALYSIS = 3;
CurrentState.prototype = {
/**
 * -----------------------------------------------------------------------------
 * Initialise l'état courant à partir de l'application lue depuis le réseau.
 * @param application l'objet application 
 */ 
	'initObjects' : function(application)
	{
		// Initialisation de l'application
		this.application = application;
		// Variable
		if (application._variables[this._initialState.variable])
		{
			this.selectVariable(this._initialState.variable);
		}
		else
		{
			for (var variable in application._variables)
			{
				this.selectVariable(variable);
				break;
			}
		}
		// Type de résultat
		if (this.variable.resultTypes[this._initialState.resultType])
		{
			this.selectResultType(this._initialState.resultType);
		}
		else
		{
			for (rType in this.variable.resultTypes)
			{
				this.selectResultType(rType);
				break;
			}
		}
		// Emprise / GeoItem
		if (application.getArea(this._initialState.area))
		{
			this.selectArea(this._initialState.area);
		}
		else
		{
			this.selectArea(application._rootArea._id);
		}
		if (this._initialState.resultType != 'map')
		{
			if (this.resultType.geoItems[this.area._id + "_" + this._initialState.geoID])
			{
				this.selectGeoItem(this.area._id + "_" + this._initialState.geoID);
			}else
			{
		      this.selectGeoItem(this.area._id);
			}
		}
		// Profondeur
		if (this._initialState.depth &&
				this.resultType.depths[this._initialState.depth])
		{
			this.selectDepth(this._initialState.depth);
		}
		else
		{
			for (depth in this.resultType.depths)
			{
				this.selectDepth(depth);
				break;
			}
		}
		// Date
		if (this._initialState.date)
		{
			var strDate = this._initialState.date;
			this.selectDate(new Date(
				parseInt(strDate.substring(0, 4), 10), parseInt(strDate.substring(4, 6), 10) - 1,
				parseInt(strDate.substring(6, 8), 10)));

			var timeconfig = this.resultType.getTimeConfig();
			var unit = timeconfig.unit;
			if (unit == 'minute' || unit == 'hour')
			{
				var hrs = parseInt(strDate.substring(9, 11), 10) * 3600000;
				var mins = parseInt(strDate.substring(11, 13), 10) * 60000;
				var secs = parseInt(strDate.substring(13, 15), 10) * 1000;
				var hourOffset = hrs + mins + secs;

				// On arrondi éventuellement si la valeur ne tombe pas juste en fonction du pas :
				// si la valeur est >= pas/2, on arrondi au pas supèrieur ;
				// sinon on arrondi au pas inférieur
				if (unit == 'minute')
				{
					var timeStepMs = timeconfig.step * 60000;
					var timeOffsetMs = timeconfig.offset * 60000 - this.date.getTimezoneOffset() * 60000;
				}
				else if (unit == 'hour')
				{
					var timeStepMs = timeconfig.step * 3600000;
					var timeOffsetMs = timeconfig.offset * 3600000 - this.date.getTimezoneOffset() * 60000;
				}
				else
				{
					this.hourOffset = hourOffset;
					return;
				}
				var remainder = hourOffset - timeOffsetMs;
				while (remainder < 0)
				{
					remainder += timeStepMs;
				}
				remainder = remainder % timeStepMs;
				if (remainder == 0)
				{
					this.hourOffset = hourOffset;
				}
				else if (remainder >= (timeStepMs / 2))
				{
					this.hourOffset = Math.min(
						hourOffset - remainder + timeStepMs,
						86400000 - timeStepMs);
				}
				else
				{
					this.hourOffset = Math.min(
						hourOffset - remainder,
						86400000 - timeStepMs);
				}
			}
			else
			{
				this.hourOffset = 0;
			}
		}
		else
		{
			this.selectDate(new Date());
			this._synchronizeHours();
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Synchronise l'offset de l'heure avec les choix actuels.
 */ 
	'_synchronizeHours' : function()
	{
			var timeConfig = this.resultType.getTimeConfig();
			if (timeConfig.unit == 'minute' || timeConfig.unit == 'hour')
			{
				// Il faut que l'offset tombe sur une valeur permise
				var now = this.hourOffset == 0 ? new Date() :
					new Date(this.date.getTime() + this.hourOffset);
				switch (timeConfig.unit) 
				{
					case 'minute' :
						var correctedTime = now.getUTCHours() * 60 +
							now.getUTCMinutes() - timeConfig.offset;
						this.hourOffset = ((correctedTime - correctedTime % timeConfig.step -
							now.getTimezoneOffset() + timeConfig.offset) * 60000) % 86400000;
						break;
					case 'hour' :
						var correctedTime = now.getUTCHours() - timeConfig.offset;
						this.hourOffset = (((correctedTime - correctedTime % timeConfig.step) * 60 -
							now.getTimezoneOffset() + timeConfig.offset) * 60000) % 86400000;
						break;
				}
			}
	},
/**
 * -----------------------------------------------------------------------------
 * Sélectionne une nouvelle variable.
 * @param variableID l'identifiant de la variable 
 */ 
	'selectVariable' : function(variableID)
	{
		this.variable = this.application.getVariables()[variableID];
		if (this.resultType)
		{
			this.selectResultType(this.resultType.id);
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Sélectionne une nouveau type de résultat.
 * @param rtypeID l'identifiant du nouveau type de résultat 
 */ 
	'selectResultType' : function(rtypeID)
	{
		this.resultType = this.variable.resultTypes[rtypeID];
		if (!this.resultType)
		{
			for( var id in this.variable.resultTypes)
			{
				if (this.resultType = this.variable.resultTypes[id])
				{
					break;
				}
			}
			if (!this.resultType)
			{
				alert("Aucun type de résultat disponible");
				return;
			}
		}
		this._synchronizeGeoItem();
		this._synchronizeHours();
	},
/**
 * -----------------------------------------------------------------------------
 * Sélectionne une nouvelle emprise.
 * @param areaID l'identifiant de la nouvelle emprise
 */ 
	'selectArea' : function(areaID)
	{
		this.area = this.application.getArea(areaID);
		this._synchronizeGeoItem();
	},
/**
* Sélectionne l'élément géométrique précédent ou celui par défaut lors du
* changement de carte.
*/
	'_synchronizeGeoItem' : function() 
	{
		if (this.resultType.id != 'map')
		{
			if (this.geoItem == null
					|| !this.resultType.geoItems[this.area._id + "_" + this.geoItem._id])
			{
				if (this.area && this.resultType.defaultGeoItem[this.area._id])
				{
					// Sélection du geoitem par défaut
					this.selectGeoItem(this.area._id + "_" + this.resultType.defaultGeoItem[this.area._id]._id);
				}else
				{
					this.selectGeoItem(null);
				}
			}
		}else
		{
			this.selectGeoItem(null);
		}
	},
/**
* -----------------------------------------------------------------------------
* S‚lectionne un nouvel ‚l‚ment g‚ographique (un point, une ligne ...).
* @param geoItemIndex l'identifiant de l'‚l‚ment
*/
	'selectGeoItem' : function(geoItemIndex)
	{
		if ((geoItemIndex) && this.resultType.geoItems[geoItemIndex])
		{
			this.geoItem = this.resultType.geoItems[geoItemIndex];
		}else
		{
			this.geoItem = this.resultType.defaultGeoItem[geoItemIndex];
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Sélectionne une nouvelle profondeur.
 * @param depthID l'identifiant de la nouvelle profondeur
 */ 
	'selectDepth' : function(depthID)
	{
		this.depth = depthID;
	},
/**
 * -----------------------------------------------------------------------------
 * Sélectionne une nouvelle journée.
 * @param date la date à utiliser
 */ 
	'selectDate' : function(date)
	{
		var unit = this.resultType.getTimeConfig().unit;
		if (unit == 'minute' || unit == 'hour')
		{
			// On gère les heures locales
			this.date = new Date(date.getFullYear(), date.getMonth(), date.getDate());
		}
		else
		{
			// On utilise minuit
			this.date = new Date(
				date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0, 0);
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Sélectionne une nouvelle heure dans la journée.
 * @param hour la nouvelle heure (un offset par rapport à minuit)
 */ 
	'selectHour' : function(hour)
	{
		if (typeof(hour) == 'string')
		{
			this.hourOffset = parseInt(hour, 10);
		}
		else
		{
			this.hourOffset = hour;
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Sélectionne une nouvelle animation.
 * @param animationID l'identifiant de la nouvelle animation ou null pour ne pas
 * sélectionner d'animation en cours 
 */ 
	'selectAnimation' : function(animID)
	{
		if (animID == null)
		{
			this.animation = null; 
		}
		else
		{
			this.animation = this.resultType.getAnimations()[animID];
		}
	},
/**
 * -----------------------------------------------------------------------------
 * Compile une structure de paramètres utilisable avec une requête Ajax vers le
 * serveur Tomcat, à partir des informations courantes.
 * @param type le type de requête ciblée (voir les constantes CurrentState.TYPE_xxx)
 * @param config un objet de configuration contenant en particulier la langue à
 * utiliser
 */ 
	'getRequestParams' : function(type, config)
	{
		switch (type)
		{
			case CurrentState.TYPE_APP_INFOS :
				var appID = (this.application ?
					this.application._id : this._initialState.application);
				var params = {
					'theme' : this.theme,
					'appli' : appID,
					'w' : 1000,
					'h' : 1000 };
				break;

			case CurrentState.TYPE_RESULT :
				var params = {
					'theme' : this.theme,
					'appli' : this.application._id,
					'var' : this.variable.id,
					'type' : this.resultType.id
				};
				var stepUnit = this.resultType.getTimeConfig().unit;
				if (stepUnit == 'hour' || stepUnit == 'minute')
				{
					params['date'] = this.date.getTime() + this.hourOffset;
				}
				else
				{
					params['date'] = this.date.getTime() - this.date.getTimezoneOffset() * 60000;
				}
				if (this.depth)
				{
					params['level'] = this.depth;
				}
				if (this.resultType.id == 'map')
				{
					params['area'] = this.area._id;
				}
				else
				{
					params['area'] = this.geoItem ? this.geoItem._area : '';
					params['geoid'] = this.geoItem ? this.geoItem._id : '';
				}
				break;
				
			case CurrentState.TYPE_BULLETIN :
				var appID = (this.application ?
					this.application._id : this._initialState.application);
				var params = {
					'theme' : this.theme,
					'appli' : appID };
				break;
				
			case CurrentState.TYPE_ANALYSIS :
				var appID = (this.application ?
					this.application._id : this._initialState.application);
				var params = {
					'theme' : this.theme,
					'appli' : appID,
					'type' : 'analyse',
					'date' : this.date.getTime() };
				break;
		}
		if (config)
		{
			params['lang'] = config.locale;
		}
		return params;
	},
/**
 * -----------------------------------------------------------------------------
 * Obtient un lien permanent déduit des informations courantes.
 * @param temporal true pour générer un lien permanent incluant la date
 */ 
	'getPermaLink' : function(forTemporal)
	{
		var variable = '/(variable)/' + this.variable.id;
		var resultType = '/(typevisu)/' + this.resultType.id;
		var depth = this.depth ? ('/(zlevel)/' + this.depth) : '';
		var zone = '/(zoneid)/' + this.area._id;
		if (this.geoItem)
		{
			var geoItem = '/(geoid)/' + this.geoItem._id;
		}
		else
		{
			var geoItem = '';
		}
		if (forTemporal)
		{
			var temporal = '/(date)/' + this.date.getFullYear();
			var digit = this.date.getMonth() + 1;
			if (digit < 10)
			{
				temporal += '0';
			}
			temporal += digit;
			digit = this.date.getDate();
			if (digit < 10)
			{
				temporal += '0';
			}
			temporal += digit + '_';
			digit = Math.floor(this.hourOffset / 3600000);
			if (digit < 10)
			{
				temporal += '0';
			}
			temporal += digit;
			digit = Math.floor((this.hourOffset % 3600000) / 60000);
			if (digit < 10)
			{
				temporal += '0';
			}
			temporal += digit + '00';
		}
		else
		{
			var temporal = '';
		}
		var href = variable + resultType + depth + zone + geoItem + temporal;
		return href;
	},
	
/**
 * Recopie de l'objet
 * @return un objet CurrentState
 */
	'copy' : function()
	{
		var copie = new CurrentState(this);
		// Etats initiaux
		copie._initialState = this._initialState;
		// Thème en cours
		copie.theme = this.theme;
		// Objet Application en cours
		copie.application = this.application;
		// Objet Variable en cours
		copie.variable = this.variable;
		// Objet ResultType en cours
		copie.resultType = this.resultType;
		// Objet Area en cours
		copie.area = this.area;
		// ID de la profondeur en cours
		copie.depth = this.depth;
		// Objet Date en cours (l'heure est fixée à minuit)
		copie.date = this.date;
		// Décalage en millisecondes dans la journee en h locale indiquant l'heure choisie
		// dans la liste déroulante, 0 si non applicable (heure masquee)
		copie.hourOffset = this.hourOffset;
		// Objet AnimationSettings en cours ou null
		copie.animation = this.animation;
		// Item géométrique
		copie.geoItem = this.geoItem;
		return copie;
	},
	/**
	 * Renvoit un tableau associatif contenant l'ensemble de l'état en cours.
	 * @return un tableau associatif
	 */
	'getState' : function()
	{
		var state = {
				theme		: this.theme,
				appli		: this.application._id,
				variable	: this.variable.id,
				type		: this.resultType.id
		};
		if (this.area != null)
		{
			state['area'] = this.area._id;
		}
		if (this.depth != null)
		{
			state['level'] = this.depth;
		}
		if (this.geoItem != null)
		{
			state['geoid'] = this.geoItem._id;
		}
		return state;
	},
	
	/**
	 * Renvoie l'état actuel de l'application Flash
	 */
	'getFlashState' : function()
	{
		return this.flashState;
	},
	
	/**
	 * Change l'état de l'applicaion Flash
	 */
	'setFlashState' : function( state )
	{
		this.flashState = state;
	}
}
