g_fOriginalErrorHandler = window.onerror;
window.onerror = remoteCallJavascriptErrorHandler;

function remoteCallJavascriptErrorHandler(sMessage, sURL, nLine)
{
  try
  {
    // répération de la stack trace
    var sStackTrace = getStackTrace(1);
    pDBConsole.error("Message:"+sMessage+"\n"+
        "URL:"+sURL+"\n"+
        "Line:"+nLine+"\n"+
        "StackTrace:\n"+sStackTrace);
    // envoi de l'erreur sur le serveur dans le but de l'insérer dans le log
    remoteCall("custom::remote::js_remote_log",
               "logError",
               sMessage,
               sURL,
               nLine,
               navigator.userAgent,
               sStackTrace);
  }
  // que faire si on ne peut pas logger l'erreur sur le serveur ?
  catch(ex)
  {
  }
  // renvoyer cette erreur
  if( g_fOriginalErrorHandler )
    g_fOriginalErrorHandler(sMessage, sURL, nLine);
}

/**
 * RemoteCallException
 */
function RemoteCallException( pException, sScriptError )
{
  this.sScriptError = sScriptError;
  this.sMessage = sScriptError;
  this.nErrorNumber = 0;
  this.sStackTrace = "";
  this.sSessionInfo = "";
}

RemoteCallException.prototype = new CheckerException();
RemoteCallException.prototype.constructor = RemoteCallException;
RemoteCallException.superclass = CheckerException.prototype;

RemoteCallException.prototype.toString = function()
{
  return this.nErrorNumber + ": " + this.sMessage;
};

RemoteCallException.prototype.getMessage = function()
{
  return this.getSessionInfo()+"\n"+CheckerException.prototype.getMessage.call(this)+"\n"+this.getStackTrace();
};

RemoteCallException.prototype.getErrorNumber = function()
{
  return this.nErrorNumber;
};

RemoteCallException.prototype.getStackTrace = function()
{
  return this.sStackTrace;
};

RemoteCallException.prototype.getSessionInfo = function()
{
  return this.sSessionInfo;
};

/**
 * Appel d'un "remote" resalys, en formatant les paramètres
 *  afin qu'ils soient pris en compte dans le remote.
 */
function remoteCall(sClassName)
{
  var aArgs = remoteCall.arguments;
  // on prépare les paramètres en les récupérant des paramétres passés à cette fonction
  var aParams = new Array();
  for(var i=1;i<aArgs.length;i++)
  {
    var sArg = aArgs[i];
    
    var sCompressedArg = '';
    // si c'est un XML je compresse
    try
    {
      if( window.pConfig && pConfig.getVarValue('use_browser_compression') > 0 &&
          getRegExp( /^<\?xml/ ).exec( sArg ) )
      {
        var sGzipArg = compressContent( sArg );
        if( sGzipArg != null )
          sCompressedArg = "_gzip:" + sGzipArg;
      }
    }
    catch(ex){}
    
    if( sCompressedArg != null && sCompressedArg != '' )
      aParams.push( sCompressedArg );
    else
      aParams.push( sArg );
  }

  //var sURL = createRemoteCallURL(sClassName, aParams);
  var aDecodedData = createRemoteCallOptions(sClassName, aParams);//getDecodedDatas( sURL );
  var sSimpleURL = getSimpleURL( /*sURL*/ serverURL );
  
  var options = {
      url: sSimpleURL,
      async: false,
      global: false,
      type: "POST",
      dataType: "text",
      data: aDecodedData
  };
  var xhr = jQuery.ajax( options );

  return evalRemoteCallResponse(xhr.responseText, options);
}

function remoteCallWait(sClassName, pCallBackInstance, pCallBackFunction, pCallBackErrorInstance, pCallBackError, sWaitMessage, bCanCancel)
{
  var aArgs = remoteCallWait.arguments;
  // on prépare les paramètres en les récupérant des paramétres passés à cette fonction
  var aParams = new Array();
  for(var i=7;i<aArgs.length;i++)
  {
    var sArg = aArgs[i];
    
    var sCompressedArg = '';
    // si c'est un XML je compresse
    try
    {
      if( pConfig && pConfig.getVarValue('use_browser_compression') > 0 &&
          getRegExp( /^<\?xml/ ).exec( sArg ) )
      {
        var sGzipArg = compressContent( sArg );
        if( sGzipArg != null )
          sCompressedArg = "_gzip:" + sGzipArg;
      }
    }
    catch(ex){}
    
    if( sCompressedArg != null && sCompressedArg != '' )
      aParams.push( sCompressedArg );
    else
      aParams.push( sArg );
  }
  
  //var sURL = createRemoteCallURL(sClassName, aParams);
  var aDecodedData = createRemoteCallOptions(sClassName, aParams);//getDecodedDatas( sURL );
  var sSimpleURL = getSimpleURL( /*sURL*/serverURL );
  
  var pAJAXRequest = jQuery.ajax({
      url: sSimpleURL,
      global: false,
      type: "POST",
      data: aDecodedData,
      dataType: "text",
      error: getRemoteCallWaitErrorHandler( pCallBackInstance, pCallBackFunction, pCallBackErrorInstance, pCallBackError),
      success: getRemoteCallWaitSuccessHandler( pCallBackInstance, pCallBackFunction, pCallBackErrorInstance, pCallBackError)
    } );
  
  showWaitLayer('wait_layer', sWaitMessage);
  // recherche du bouton stop pour annuler la requête en cours
  var pStopButton = getDOMObject('cancel_request_button');
  if( pStopButton )
  {
    if( bCanCancel )
    {
      pStopButton.onclick = getCancelRequestHandlerFunction(pAJAXRequest, pCallBackInstance, pCallBackFunction, pCallBackErrorInstance, pCallBackError);
      pStopButton.style.visibility = 'visible';
    }
    else
      pStopButton.style.visibility = 'hidden';
  }
}

function getDecodedDatas( sURL )
{
  var aEncodedParams = getDatas( sURL );
  var aDecodedData = {};
  for(var p in aEncodedParams)
  {
      aDecodedData[p] = decodeURIComponent(aEncodedParams[p]);
  }
  return aDecodedData;
}

function getDatas( sURL )
{
  var aTmp = sURL.split("?");
  var sParams = aTmp[1];
  var aEncodedParams = sParams.split("&");
  var aData = {};
  for(var i=0; i<aEncodedParams.length; i++)
  {
    if (aEncodedParams[i].match(getRegExp(/^.+=.*$/)))
    {
      var aKeyValue = aEncodedParams[i].split("=");
      aData[aKeyValue[0]] = aKeyValue[1];
    }
  }
  return aData;
}


function getSimpleURL( sURL )
{
  var aTmp = sURL.split("?");
  return aTmp[0];
}

function createRemoteCallURL(sClassName, aParams)
{
  var sURL = serverURL+'&mime_type=text/plain&';
  sURL += 'class=' + sClassName + '&';
  for( var i=0 ; i < aParams.length ; i++ )
  {
    if(aParams[i] === false)
      aParams[i] = 0;
    if(aParams[i] === true)
      aParams[i] = 1;
    var iIndex = i+1;
    sURL += 'arg' + iIndex + '=' + ( aParams[i] != null ? encodeURIComponent(aParams[i]) : '' ) + '&';
  }
  sURL += 'num_args=' + (aParams.length);
  return sURL;
}

function createRemoteCallOptions(sClassName, aParams)
{
  var aNewParams = {
      'mime_type' : 'text/plain',
      'class' : sClassName
  };
  
  if ( !window.sessionParams )
  {
    var sessionParams = getDatas( serverURL );
    window.sessionParams = {};
    for ( var p in sessionParams )
    {
      window.sessionParams[p] = sessionParams[p];
    }
  }
  for ( var p in window.sessionParams )
  {
    aNewParams[p] = window.sessionParams[p];
  }
  for( var i=0 ; i < aParams.length ; i++ )
  {
    if(aParams[i] === false)
      aParams[i] = 0;
    if(aParams[i] === true)
      aParams[i] = 1;
    aNewParams['arg'+(i+1)] = ( aParams[i] != null ? aParams[i] : '' );
  }
  aNewParams['num_args'] = aParams.length;
  return aNewParams;
}

/*
var options = {
    url: sSimpleURL,
    async: false,
    global: false,
    type: "POST",
    dataType: "text",
    data: aDecodedData
};
*/
function evalRemoteCallResponse(sResponse, context)
{
  // eval the server response
  var pRet;
  try
  {
    eval( String(sResponse) );
  }
  catch(e)
  {
    throw new RemoteCallException(e, sResponse);
  }

  if( pRet === undefined )
  {
    if ( context )
    {
      throw new RemoteCallException(null, 'Invalid remote call response for "'+context.url+'": '+sResponse);
    }
    throw new RemoteCallException(null, 'Invalid remote call response: '+sResponse);
  }
  
  if( pRet instanceof RemoteCallException ||
      pRet instanceof CheckerException )
    throw pRet;

  return pRet;
}

/*
 *  AJAX stuff
 *
 *  Originally ripped from an IBM tutorial:
 *  http://www-128.ibm.com/developerworks/library/j-ajax1/?ca=dgr-lnxw01Ajax
 */

/*
 * Returns a new XMLHttpRequest object, or false if this browser
 * doesn't support it
 */
function newXMLHttpRequest()
{
  var xmlreq = false;

  if( window.XMLHttpRequest )
  {
    // Create XMLHttpRequest object in non-Microsoft browsers
    xmlreq = new XMLHttpRequest();
  }
  else if( window.ActiveXObject )
  {
    // Create XMLHttpRequest via MS ActiveX
    try
    {
      // Try to create XMLHttpRequest in later versions of Internet Explorer
      xmlreq = new ActiveXObject("Msxml2.XMLHTTP");
    }
    catch( e1 )
    {
      // Failed to create required ActiveXObject
      try
      {
        // Try version supported by older versions of Internet Explorer
        xmlreq = new ActiveXObject("Microsoft.XMLHTTP");
      }
      catch( e2 )
      {
        // Unable to create an XMLHttpRequest with ActiveX
      }
    }
  }

  return xmlreq;
}

/// Suppression des closures
com = {};
com.softbooking = {};
com.softbooking.resalys = {
    log: function( s )
    {
      if ( window.console )
        window.console.log( s );
    }
};

com.softbooking.resalys.AjaxHandlersManager = function() {};
com.softbooking.resalys.AjaxHandlersManager.prototype = {
    handlers: {}
};

com.softbooking.resalys.AsyncCallBackHandler = function( pCallBackInstance, pCallBackFunction, pCallBackErrorInstance, pCallBackError )
{
  this.pCallBackInstance = pCallBackInstance;
  this.pCallBackFunction = pCallBackFunction;
  this.pCallBackErrorInstance = pCallBackErrorInstance;
  this.pCallBackError = pCallBackError;
};

com.softbooking.resalys.AsyncCallBackHandler.prototype = {
    handle: function( sRequestResult, pAJAXRequest )
    {
      // si pas annulé (en cas d'annulation tout a déjà été géré par le onClick)
      if( !pAJAXRequest.isCanceled() )
      {
        hideWaitLayer('wait_layer');
        var nTime = getCurrentDate().getTime();
        try
        {
          var pRequest = pAJAXRequest.getRequest();
          if( pRequest.status == 200 )
          {
            if(this.pCallBackInstance)
            {
              this.pCallBackFunction.call(this.pCallBackInstance, evalRemoteCallResponse(sRequestResult), false );
            }
            else
            {
              this.pCallBackFunction( pAJAXRequest.isCanceled() ? '' : evalRemoteCallResponse(sRequestResult), false );
            }
          }
          else
          {
            this.pCallBackError.call(this.pCallBackErrorInstance, new Error("Invalid server response status: "+pRequest.status));
          }
        }
        catch(ex)
        {
          this.pCallBackError.call(this.pCallBackErrorInstance, ex);
        }
        //alert(new Date().getTime() - nTime);
      }
    }
};

com.softbooking.resalys.ReadyStateHandler = function( pAJAXRequest, pResponseHandler, bCheckStatus, nTimeoutID, bUseXML )
{
  //com.softbooking.resalys.log('Build new ReadyStateHandler');
  if( !pAJAXRequest ) {
    throw new Error( "Error, asynchronous call back called without valid XMLHTTP object" );
  }
  if( !pResponseHandler ) {
    throw new Error( "Error, asynchronous call back called without valid callback function" );
  }
  this.pAJAXRequest = pAJAXRequest;
  this.pResponseHandler = pResponseHandler;
  this.bCheckStatus = bCheckStatus;
  this.nTimeoutID = nTimeoutID;
  this.bUseXML = bUseXML;
};

com.softbooking.resalys.ReadyStateHandler.prototype = {
    /*
     * Returns a function that waits for the specified XMLHttpRequest
     * to complete, then passes its XML response to the given handler function.
     *   pRequest         : The XMLHttpRequest whose state is changing
     *   pResponseHandler : Function to pass the XML response to
     *   bCheckStatus     : Throws an error for bad statuses
     *   nTimeoutID       : Index of the timeout timeout
     *   bUseXML          : Returns an XML object instead of text
     */
    handle: function()
    {
        var pRequest = this.pAJAXRequest.getRequest();
        var bCheckStatus = this.bCheckStatus;
        var bUseXML = this.bUseXML;
        // If the request's status is "complete"
        if( pRequest.readyState == 4 )
        {
          if( this.nTimeoutID ) {
            window.clearTimeout( this.nTimeoutID );
          }
          try
          {
            // Check that a successful server response was received
            if( !bCheckStatus || pRequest.status == 200 )
            {
              // Pass the XML/Text payload of the response to the handler function
              this.pResponseHandler( bUseXML ? pRequest.responseXML : pRequest.responseText, this.pAJAXRequest );
            }
            else
            {
              // An HTTP problem has occurred
              throw new Error( "HTTP error: " + pRequest.status );
            }
          }
          catch( ex )
          {
            // pRequest.status n'est pas défini parfois
            //throw new Error( "Error while retrieving the response from the server" );
          }
        }
    }
};

com.softbooking.resalys.TimeoutHandler = function( pRequest )
{
  //com.softbooking.resalys.log('Build new TimeoutHandler');
  this.pRequest = pRequest;
};

com.softbooking.resalys.TimeoutHandler.prototype = {
    handle: function() {
      // pas la peine de vérifier le reste?
      if( this.pRequest == null || this.pRequest.readyState == 4 )
        return;
      else
        this.pRequest.abort();
      this.pRequest = null;
    }
};

com.softbooking.resalys.RemoteCallWaitHandler = function( pCallBackInstance, pCallBackFunction, pCallBackErrorInstance, pCallBackError )
{
  com.softbooking.resalys.log('Build new RemoteCallWaitHandler');
  this.pCallBackInstance = pCallBackInstance;
  this.pCallBackFunction = pCallBackFunction;
  this.pCallBackError = pCallBackError;
  this.pCallBackErrorInstance = pCallBackErrorInstance;
};

com.softbooking.resalys.RemoteCallWaitHandler.prototype = {
    error: function(xml, textStatus, errorThrown){
      hideWaitLayer('wait_layer');
      this.pCallBackError.call(this.pCallBackErrorInstance, errorThrown);
      return false;
    },
    success: function(msg){
      hideWaitLayer('wait_layer');
      try {
      if(this.pCallBackInstance)
        this.pCallBackFunction.call(this.pCallBackInstance, evalRemoteCallResponse(msg), false );
      else
        this.pCallBackFunction( evalRemoteCallResponse(msg), false );
      } catch(ex) {
        this.pCallBackError.call(this.pCallBackErrorInstance, ex);
      }
      return false;
    }
};

com.softbooking.resalys.CancelHandler = function(pAJAXRequest, pCallBackInstance, pCallBackFunction, pCallBackErrorInstance, pCallBackError)
{
  this.pAJAXRequest = pAJAXRequest;
  this.pCallBackInstance = pCallBackInstance;
  this.pCallBackFunction = pCallBackFunction;
  this.pCallBackErrorInstance = pCallBackErrorInstance;
  this.pCallBackError = pCallBackError;
};

com.softbooking.resalys.CancelHandler.prototype = {
    handle: function() {
      // on note que cette requete est annulée car lorsque l'on va appeler
      //  la méthode abort, le système va appeler le ready-state handler et
      //  ce dernier ne doit pas rappeler les callback en cas d'annulation
      var pAJAXRequest = this.pAJAXRequest;
      if ( pAJAXRequest instanceof AJAXCall )
      {
        pAJAXRequest.setCanceled(true);  
        var pRequest = pAJAXRequest.getRequest();
        pRequest.abort();
      }
  
      // on cache la layer
      //destroyWaitLayer('wait_layer');
      hideWaitLayer('wait_layer');

      // on appel le callback
      //  en lui signalant l'annulation
      try
      {
        if(this.pCallBackInstance)
          this.pCallBackFunction.call(this.pCallBackInstance, '', true );
        else
          this.pCallBackFunction( '', true );
      }
      catch(ex)
      {
        this.pCallBackError.call(this.pCallBackErrorInstance, ex);
      }
      // retourne false pour éviter que le bonton n'efectue l'appel de
      //  son lien par défaut (href)
      return false;
  }
};

/**
 * Get the handles managers for Ajax events. Create one if needed.
 * @return
 */
function getHandlersManager()
{
  if (!window.handlersManager )
  {
    window.handlersManager = new com.softbooking.resalys.AjaxHandlersManager();
  }
  return window.handlersManager;
}

/*
 * Function which call the current timeout handler
 */
function handleTimeout() {
  //com.softbooking.resalys.log('Handle Timeout');
  getHandlersManager().handlers['timeout'].handle();
}

/*
 * Returns a function to cancel the remote call
 *   pRequest : The XMLHttpRequest whose state is changing
 */
function getTimeoutHandler( pRequest )
{
  // Create a handler for the event
  getHandlersManager().handlers['timeout'] = new com.softbooking.resalys.TimeoutHandler( pRequest );
  // Return a function which will call the handler when needed
  return handleTimeout;
}

function handleRemoteCallWaitSuccess( msg ) {
  //com.softbooking.resalys.log('Handle remote_call_wait success');
  getHandlersManager().handlers['remote_call_wait'].success( msg);
}

function handleRemoteCallWaitError( xml, textStatus, errorThrown ) {
  //com.softbooking.resalys.log('Handle remote_call_wait error');
  getHandlersManager().handlers['remote_call_wait'].error( xml, textStatus, errorThrown );
}

function getRemoteCallWaitSuccessHandler( pCallBackInstance, pCallBackFunction, pCallBackErrorInstance, pCallBackError)
{
  //com.softbooking.resalys.log('(SU) Create new RemoteCallWaitHandler');
  getHandlersManager().handlers['remote_call_wait'] = new com.softbooking.resalys.RemoteCallWaitHandler( pCallBackInstance, pCallBackFunction, pCallBackErrorInstance, pCallBackError );
  return handleRemoteCallWaitSuccess;
}

function getRemoteCallWaitErrorHandler(pCallBackInstance, pCallBackFunction, pCallBackErrorInstance, pCallBackError)
{
  //com.softbooking.resalys.log('(ER) Create new RemoteCallWaitHandler');
  getHandlersManager().handlers['remote_call_wait'] = new com.softbooking.resalys.RemoteCallWaitHandler( pCallBackInstance, pCallBackFunction, pCallBackErrorInstance, pCallBackError );
  return handleRemoteCallWaitError;
}

function handleCancel() {
  //com.softbooking.resalys.log('Handle Cancel');
  getHandlersManager().handlers['cancel'].handle();
  getHandlersManager().handlers['cancel'] = null;
}

function getCancelRequestHandlerFunction(pAJAXRequest, pCallBackInstance, pCallBackFunction, pCallBackErrorInstance, pCallBackError)
{
  getHandlersManager().handlers['cancel'] = new com.softbooking.resalys.CancelHandler( pAJAXRequest, pCallBackInstance, pCallBackFunction, pCallBackErrorInstance, pCallBackError );
  return handleCancel;
}

function handleReadyState()
{
  //com.softbooking.resalys.log('Handle ready');
  getHandlersManager().handlers['ready'].handle();
}

function getReadyStateHandler( pAJAXRequest, pResponseHandler, bCheckStatus, nTimeoutID, bUseXML )
{
  getHandlersManager().handlers['ready'] = new com.softbooking.resalys.ReadyStateHandler( pAJAXRequest, pResponseHandler, bCheckStatus, nTimeoutID, bUseXML );
  return handleReadyState;
}

function handleAsyncCallBack( s, r )
{
  //com.softbooking.resalys.log('Handle async response');
  getHandlersManager().handlers['async'].handle( s, r );
  getHandlersManager().handlers['async'] = null;
}

function getRemoteCallAsyncCallBack(pCallBackInstance, pCallBackFunction, pCallBackErrorInstance, pCallBackError)
{
  getHandlersManager().handlers['async'] = new com.softbooking.resalys.AsyncCallBackHandler( pCallBackInstance, pCallBackFunction, pCallBackErrorInstance, pCallBackError );
  return handleAsyncCallBack;
}

/*
 * Shortcut for AJAXCall with a single URL
 */
function AJAXCallURL( sFullUrl, pCallBackFunction, nTimeOut, bCheckStatus, bUseXML )
{
  // construct URL
  var aURLParts = new String(sFullUrl).split('?');
  var sURL    = aURLParts[0];
  var sParams = aURLParts[1];
  
  return AJAXCall( sURL, sParams, pCallBackFunction, nTimeOut, bCheckStatus, bUseXML );
}

/*
 * Call a remote script and returns its result as an XML document or a text value.
 * 
 *   sURL              : Server location to call
 *   sParams           : Parameters to pass to the server
 *   pCallBackFunction : Function to pass the response to
 *   nTimeOut          : Time in second to wait before abording the request
 *   bCheckStatus      : Throws an error for bad statuses
 *   bUseXML           : Returns an XML object instead of text
 */
function AJAXCall( sURL, sParams, pCallBackFunction, nTimeOut, bCheckStatus, bUseXML )
{
  // Obtain an XMLHttpRequest instance
  var pRequest = newXMLHttpRequest();

  // Asynchronous
  var bAsynchronous = pCallBackFunction != null;
  
  var pAJAXRequest = new AJAXRequest(pRequest);
  
  if( bAsynchronous )
  {
    var nTimeoutID = null;
    if( nTimeOut ) // plus vraiment asynchrone en fait :/
    {
      var hTimeout = getTimeoutHandler( pRequest );
      nTimeoutID = window.setTimeout( hTimeout, nTimeOut * 1000 );
    }

    // Set the handler function to receive callback notifications from the request object
    pRequest.onreadystatechange = getReadyStateHandler( pAJAXRequest, pCallBackFunction, bCheckStatus, nTimeoutID );
  }

  // Open an HTTP POST connection to the server
  // Third parameter specifies request is asynchronous.
  pRequest.open( "POST", sURL, bAsynchronous ? true : false );
  
  // Specify that the body of the request contains form data
  pRequest.setRequestHeader( "Content-Type", "application/x-www-form-urlencoded" );
  
  // Bug potentiel avec l'IE, à decommenter s'il se produit
  //sParams += "hash=" + Math.random();
  
  // Send form encoded data
  pRequest.send( sParams );
  
  if( bAsynchronous )
  {
    return pAJAXRequest;
  }
  else
  {
    return bUseXML ? pRequest.responseXML : pRequest.responseText;
  }
}

function AJAXRequest(pRequest)
{
  this.getRequest = AJAXRequest_getRequest;
  this.setCanceled = AJAXRequest_setCanceled;
  this.isCanceled = AJAXRequest_isCanceled;
  
  this.pRequest = pRequest;
  this.bCanceled = false;
  
}

function AJAXRequest_getRequest()
{
  return this.pRequest;
}

function AJAXRequest_setCanceled(bCanceled)
{
  this.bCanceled = bCanceled;
}

function AJAXRequest_isCanceled()
{
  return this.bCanceled;
}

function compressContent( sContent )
{
  //console.time( 'compress' );
  var sEncodedContent = encodeURIComponent( sContent );
  var sCompressedContent = compressJS( sEncodedContent );
  //console.timeEnd( 'compress' );
  
  return sCompressedContent;
}
