

/**
 * Closure oo0 !_! 0oo
 */
(function() {
	var window = this,
			ev = window.ev,
			currentTimeMillis = ev && ev.ctm,
			LOG = ev && ev.log;

	if (!ev){
		throw 'XHRMethod#<init>: needs ev.core module!';
	}

	// on teste d'abord l'existance des classes nécessaires
	window.Classe.checkDefined('TimelineListener');
	window.Classe.checkDefined('XHRequest');

	window.XHRMethodErrorType = {
		TIMEOUT: 0,
		ERROR_500: 1,
		ERROR_503: 2,
		ERROR_404: 3,
		ERROR_HTTP: 4,
		EXCEPTION: 5,
		UNKNOWN_ERROR: 6,

		toString: function(value) {
			switch (value) {
				case this.TIMEOUT: return 'TIMEOUT';
				case this.ERROR_500: return 'ERROR_500';
				case this.ERROR_503: return 'ERROR_503';
				case this.ERROR_404: return 'ERROR_404';
				case this.ERROR_HTTP: return 'ERROR_HTTP';
				case this.EXCEPTION: return 'EXCEPTION';
				case this.UNKNOWN_ERROR: return 'UNKNOWN_ERROR';
				default: return 'UNKNOWN ERROR';
			}
		},

		getHttpErrorType: function(value) {
			var typeError;
			switch (value) {
				case 404:
					typeError = this.ERROR_404;
					break;
				case 500:
					typeError = this.ERROR_500;
					break;
				case 503:
					typeError = this.ERROR_503;
					break;
				default:
					typeError = this.ERROR_HTTP;
			}
			return typeError;
		}
	};

	/**
	 * Effectue le traitement par défaut de la boucle.
	 * 
	 * @param {!Object} method méthode en cours de traitement
	 * @param {!Object} event l'évènement courant
	 */
	function doDefault(method, event){
		if (typeof(method.catchError) === 'function') {
			method.catchError(window.XHRMethodErrorType.UNKNOWN_ERROR, 'Evènement de timeline inconnu : ' + event.getType());
		}
	}

	/**
	 * Effectue le traitement de l'arrêt de la boucle.
	 * 
	 * Dans le cas où la timeline se termine, on vérifie que la
	 * requête en cours ne s'est pas terminée juste à temps.
	 * 
	 * @param {!Object} method méthode en cours de traitement
	 * @param {!Object} event l'évènement courant
	 */
	function doStop(method, event){
		// ... on supprime l'écouteur de la timeline ...
		event.getSource().removeTimelineListener(method);
		if (method.request.xhr.readyState === 4) {
			// requête terminée
			if (method.request.xhr.status === 200) {// ... et on parse le résultat de la requête si le code de retour est 200
				if (method.request.xhr.responseXML) {
					method.parseData(method.request.xhr.responseXML);
				}
				else {
					method.parseData(method.request.xhr.responseText);
				}
			}
			else {// état de la requête HTTP incorrect
				if (typeof(method.catchError) === 'function') {
					method.catchError(window.XHRMethodErrorType.getHttpErrorType(method.request.xhr.status), 'HTTP status exception : ' + method.request.xhr.status + ' [' + method.url + ']');
				}
			}
		}
		else {
			// requête non terminée, c'est un timeout
			// ... on interrompt la requête en cours
			method.request.xhr.abort();
			// ... et on envoie une erreur
			if (typeof(method.catchError) === 'function') {
				method.catchError(window.XHRMethodErrorType.TIMEOUT, 'Timeout exception [' + method.url + ']');
			}
		}
	}

	/**
	 * Effectue le traitement principal de la boucle.
	 * 
	 * A chaque intervalle de temps de la timeline, on
	 * vérifie l'état de la requête.
	 * 
	 * @param {!Object} method méthode en cours de traitement
	 * @param {!Object} event l'évènement courant
	 */
	function doRunning(method, event){
		if (!method.started) {
			method.started = true;
			// exécution de la requête
			method.execute(method.request);
			// définition de l'heure d'arrêt (en cas d'exécution prolongée)
			method.timeoutDate = currentTimeMillis() + method.timeout;
			return;
		}
		// si la requête la requête est encore en cours d'exécution ...
		if (method.request.xhr.readyState === 4) {
			if (method.request.xhr.status == 200) {// ... et on parse le résultat de la requête si le code de retour est 200
				event.getSource().removeTimelineListener(method);//on supprime l'écouteur de la timeline ...
				if (method.request.xhr.responseXML) {
					method.parseData(method.request.xhr.responseXML);
				}
				else {
					method.parseData(method.request.xhr.responseText);
				}
			}
			else {//... si le code de retour n'est pas égale à 200, on gère l'erreur
				// exécution à nouveau de la requête
				if (method.retry > 0) {
					method.retry--;
					method.execute(method.request);
				}
				else {
					event.getSource().removeTimelineListener(method); // ... on supprime l'écouteur de la timeline ...
					if (typeof(method.catchError) == 'function') {
						method.catchError(window.XHRMethodErrorType.getHttpErrorType(method.request.xhr.status), 'HTTP status exception : ' + method.request.xhr.status + ' [' + method.url + ']');
					}
					else {      throw new Error(window.XHRMethodErrorType.getHttpErrorType(method.request.xhr.status), 'HTTP status exception : ' + method.request.xhr.status + ' [' + method.url + ']');//on affiche une erreur
}
				}
				
			}
			return;
		}
		// ... si le timeout n'est pas dépassé, on sort
		if (currentTimeMillis() < method.timeoutDate) {
			return;
		}
		// sinon ...
		// ... on interrompt la requête en cours
		method.request.xhr.abort();
		// ... on supprime l'écouteur de la timeline ...
		event.getSource().removeTimelineListener(method);
		// ... et on envoie une erreur
		if (typeof(method.catchError) == 'function') {
			method.catchError(window.XHRMethodErrorType.TIMEOUT, 'Timeout exception [' + method.url + ']');
		}
		// Ensuite on effectue le traitement de l'arrêt de la boucle
		doStop(method, event);
	}

	/**
	 * Constructeur de la classe XHRMethod.
	 * Il construit une instance de XHRMethod à partir des paramètres donnés.
	 *
	 * Cette classe est un auditeur de timeline :
	 *   la classe XHRMethod est une extension de la classe TimelineListener.
	 *
	 * Les instances de cette classe permettent d'effectuer des requêtes HTTP
	 * asynchrones via des objets XHRequest.
	 *
	 * @param {number} _timeout : temps accordé pour l'exécution de la requête.
	 * @param {string} _url : adresse de la page a appeler.
	 * @param {number} _retry : la requete sera relancée  _retry fois si le status de retour est différent de 200 ou 300.
	 */
	window.XHRMethod = function(_timeout, _url, _retry) {
		var theMethod = this;

		// appel du constructeur de TimelineListener
		window.TimelineListener.call(theMethod);

		// définition de propriété
		theMethod.timeout = _timeout;
		theMethod.url = _url;
		theMethod.timeoutDate = 0;
		theMethod.started = false;
		theMethod.retry = 0;
		if (typeof(_retry) === 'number' && _retry > 0) {
			theMethod.retry = _retry;
		}


		theMethod.request = new window.XHRequest(_url);

		/**
		 * Surcharge de la méthode throwTimelineEvent().
		 *
		 * @see TimelineListener#throwTimelineEvent()
		 */
		theMethod.throwTimelineEvent = function(_event) {
			switch (_event.getType()) {
				// Si la timeline démarre on fait la même chose qu'à chaque intervalle
				// (à priori la timeline est déjà démarrée lors de l'ajout d'une requête)
				case window.TimelineEventType().START:
				case window.TimelineEventType().RUNNING:
					doRunning(theMethod, _event);
					break;
				case window.TimelineEventType().STOP:
					doStop(theMethod, _event);
					break;
				default:
					doDefault(theMethod, _event);
					break;
			}
		};
	};

	/**
	 * Prototype de la classe
	 */
	window.XHRMethod.prototype = {
		/**
		 * Exécuion de la requête.
		 *
		 * @param {Object} _xhrequest : objet XHRequest valide permettant d'effectuer la requête.
		 */
		execute: function(_xhrequest) {
			LOG.warn('The method XHRMethod#execute(XHRequest) should be overriden!');
		},

		/**
		 * Extraction des données reçues.
		 *
		 * @param {Object} _data : donnée reçue (format XML).
		 */
		parseData: function(_data) {
			var theMethod = this;

			if (typeof(_data) === 'string') {
				LOG.debug('_data is string : ' + _data);
				return;
			}
			var exceptionTags = _data.getElementsByTagName('exception');
			if (!exceptionTags) { return; }
			if (!exceptionTags[0]) { return; }
			if (!exceptionTags[0].firstChild) { return; }
			var exception = window.stripCDATA(exceptionTags[0].firstChild.nodeValue);
			if (!exception) { return; }

			if (typeof(theMethod.catchError) == 'function') {
				theMethod.catchError(window.XHRMethodErrorType.EXCEPTION, 'XHRMethod(' + theMethod.url + ') : Exception [' + exception + ']');
			}else {
				throw new Error('XHRMethod(' + theMethod.url + ') : Exception [' + exception + ']');
			}
		}
	};

	// déclaration de l'héritage
	window.Classe.extend(window.XHRMethod, window.TimelineListener);
	LOG.info('XHRMethod.js ok');
}());

