// on teste d'abord l'existance des classes nécessaires
Classe.checkDefined("TimelineListener");
Classe.checkDefined("XHRequest");

/**
 * 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 {Integer} _timeout : temps accordé pour l'exécution de la requête
 * @param {String} _url : adresse de la page a appeler
 * @param {Integer} _retry : la requete sera relancée  _retry fois si le status de retour est différent de 200 ou 300
 */
function XHRMethod(_timeout, _url, _retry){
	// appel du constructeur de TimelineListener
	TimelineListener.call(this);

	// définition de propriété 
	this.timeout=_timeout;
	this.url=_url;
	this.timeoutDate=0;
	this.started=false;
	this.retry=0;
	if(_retry!=undefined && (typeof _retry)=="number" && _retry>0){
		this.retry=_retry;
	} 
	

	this.request=new XHRequest(this.url);
	
	/**
	 * Surcharge de la méthode throwTimelineEvent().
	 *
	 * @see TimelineListener#throwTimelineEvent()
	 */
	this.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 TimelineEventType().START:
			// A chaque intervalle de temps de la timeline, on vérifie l'état de la requête
			case TimelineEventType().RUNNING:
		  	if (!this.started) {
		  		this.started = true;
		  		// exécution de la requête
					this.execute(this.request);
					// définition de l'heure d'arrêt (en cas d'exécution prolongée)
					this.timeoutDate = (new Date()).getTime() + this.timeout;
					break;
				}
				// si la requête la requête est encore en cours d'exécution ...
				if (this.request.xhr.readyState != 4) {
					// ... si le timeout n'est pas dépassé, on sort
					if ((new Date()).getTime() < this.timeoutDate) 
						break;
					// sinon ...
					// ... on interrompt la requête en cours
					this.request.xhr.abort();
					// ... on supprime l'écouteur de la timeline ...
					_event.getSource().removeTimelineListener(this);
					// ... et on envoie une erreur
					if (typeof(this.catchError) == 'function') {
						this.catchError(XHRMethodErrorType.TIMEOUT, "Timeout exception [" + this.url + "]");
					}
				}
				else {
					
					if (this.request.xhr.status == 200) {// ... et on parse le résultat de la requête si le code de retour est 200
						
						_event.getSource().removeTimelineListener(this);//on supprime l'écouteur de la timeline ...
					
						if (this.request.xhr.responseXML)
							this.parseData(this.request.xhr.responseXML);
						else 
							this.parseData(this.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(this.retry > 0){
							this.retry--;
							this.execute(this.request);
						}else{
							_event.getSource().removeTimelineListener(this);	// ... on supprime l'écouteur de la timeline ...
							if (typeof(this.catchError) == 'function') {
								this.catchError(XHRMethodErrorType.getHttpErrorType(this.request.xhr.status), "HTTP status exception : " + this.request.xhr.status + " [" + this.url + "]");
							}else{
								throw new Error(XHRMethodErrorType.getHttpErrorType(this.request.xhr.status), "HTTP status exception : " + this.request.xhr.status + " [" + this.url + "]");//on affiche une erreur
							}
						}
					
					}
					break;
				}
			// 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
			case TimelineEventType().STOP:
	  		// ... on supprime l'écouteur de la timeline ...
				_event.getSource().removeTimelineListener(this);
				if (this.request.xhr.readyState != 4) {
					// requête non terminée, c'est un timeout
					// ... on interrompt la requête en cours
					this.request.xhr.abort();
					// ... et on envoie une erreur
					if (typeof(this.catchError) == 'function') {
						this.catchError(XHRMethodErrorType.TIMEOUT, "Timeout exception [" + this.url + "]");
					}
				}
				else {
					if (this.request.xhr.status == 200) {// ... et on parse le résultat de la requête si le code de retour est 200
						if (this.request.xhr.responseXML) 
							this.parseData(this.request.xhr.responseXML);
						else 
							this.parseData(this.request.xhr.responseText);
					}
					else {// état de la requête HTTP incorrect
						if (typeof(this.catchError) == 'function') {
							this.catchError(XHRMethodErrorType.getHttpErrorType(this.request.xhr.status), "HTTP status exception : " + this.request.xhr.status + " [" + this.url + "]");
						}
					}
					break;
				}
			default:
		  	if (typeof(this.catchError) == 'function') {
		  		this.catchError(XHRMethodErrorType.UNKNOWN_ERROR, "Evènement de timeline inconnu : " + _event.getType());
		  	}
		}
	}

}

/**
 * Prototype de la classe
 */
XHRMethod.prototype={
	/**
	 * Exécuion de la requête.
	 *
	 * @param {Object} _xhrequest : objet XHRequest valide permettant d'effectuer la requête
	 */
	execute: function(_xhrequest){
		if(typeof(log)=='function') log('The method XHRMethod#execute(XHRequest) should be overriden!', 'warn');
	},

	/**
	 * Extraction des données reçues.
	 *
	 * @param {Object} _data : donnée reçue (format XML)
	 */
	parseData: function(_data){
		if(typeof(_data)=='string'){
			if(typeof(log)=='function') log('_data is string : '+_data, 'debug');
			return;
		}
		var exceptionTags=_data.getElementsByTagName("exception");
		if(!exceptionTags) return;
		if(!exceptionTags[0]) return;
		if(!exceptionTags[0].firstChild) return;
		var exception=stripCDATA(exceptionTags[0].firstChild.nodeValue);
		if(!exception) return;
		if(exception=="") return;
		
		if(typeof(this.catchError)=='function'){
			this.catchError(XHRMethodErrorType.EXCEPTION, 'XHRMethod(' + this.url + ') : Exception [' + exception + ']');
		}
		else{
			
			throw new Error('XHRMethod(' + this.url + ') : Exception [' + exception + ']');
		}
	}
}

// déclaration de l'héritage
Classe.extend(XHRMethod, TimelineListener);
if (typeof(log)=='function') log("XHRMethod.js ok", "info");



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;
	}
}