/*!
 * XML-RPC client class
 * @version 1.0
 * @author M.F.Endenburg
 * @copyright (c) Denbel Systems, 2008
 */

// load namespaces
Denbel.load( 'rpc.XmlRpcClient' );
Denbel.load( 'rpc.XmlRpcMessage' );
Denbel.load( 'rpc.XmlRpcParameter' );

/**
 * XmlRpcClient static class
 * @param string URL
 * @return void
 */
Denbel.rpc.XmlRpcClient = function( url )
{
  Denbel.rpc.XmlRpcClient.superclass.constructor.call( this,
  {
    'url': url
  } );
  
	var rpcToEcma = new Denbel.lang.Object();
	rpcToEcma.set( 'i4', 'int' );
	rpcToEcma.set( 'int', 'int' );
	rpcToEcma.set( 'struct', 'object' );
	rpcToEcma.set( 'boolean', 'boolean' );
	rpcToEcma.set( 'array', 'array' );
	rpcToEcma.set( 'double', 'float' );
	rpcToEcma.set( 'string', 'string' );
	rpcToEcma.set( 'base64', 'base64' );
	rpcToEcma.set( 'datetime.iso8601', 'date' );
	Denbel.rpc.XmlRpcClient.rpcToEcmaConversion = rpcToEcma;
	   
	var ecmaToRpc = new Denbel.lang.Object();
	ecmaToRpc.set( 'int', 'i4' );
	ecmaToRpc.set( 'int', 'int' );
	ecmaToRpc.set( 'object', 'struct' );
	ecmaToRpc.set( 'boolean', 'boolean' );
	ecmaToRpc.set( 'array', 'array' );
	ecmaToRpc.set( 'float', 'double' );
	ecmaToRpc.set( 'string', 'string' );
	ecmaToRpc.set( 'base64', 'base64' );
	ecmaToRpc.set( 'date', 'datetime.iso8601' );
	Denbel.rpc.XmlRpcClient.ecmaToRpcConversion = ecmaToRpc;
};

/**
   * Supported data types
   * @var Array
   */
Denbel.rpc.XmlRpcClient.dataTypes = new Array
  (
      'boolean',
      'array',
      'object',
      'string',
      'int',
      'i4',
      'dateTime.iso8601',
      'double',
      'base64'
  );
  
  /**
   * Holds the last fault
   * @var Denbel.lang.Object
   */
  Denbel.rpc.XmlRpcClient.lastFault = null;
  
  /**
   * XML-RPC data type to ECMA type conversion table
   * @var Denbel.lang.Object
   */
  Denbel.rpc.XmlRpcClient.rpcToEcmaConversion = null;
  
  /**
   * ECMA to XML-RPC data type conversion table
   * @var Denbel.lang.Object
   */
  Denbel.rpc.XmlRpcClient.ecmaToRpcConversion = null;
  
  /**
   * The last response
   * @var string
   */
  Denbel.rpc.XmlRpcClient.lastResponse = null;

  /**
   * Converts a HTMLFormElement to a JSON object
   * @param HTMLFormElement form
   * @return Object
   */
  Denbel.rpc.XmlRpcClient.convertFormToJSon = function( form )
  {
    if( YAHOO.lang.isString( form ) )
    {
      form = YAHOO.util.Dom.get( form );
    }
    
    if( !form )
    {
      throw Error( 'No form' );
      return null;
    }
    
    if( !YAHOO.lang.isFunction( YAHOO.lang.JSON.parse ) )
    {
      throw Error( 'JSON is missing' );
      return null;
    }
    
    var n = null;
    var s = null;
    var v = null;
    var t = null;
    var tp = null;
    var el = null;
    var p = -1;
    var j = '{';
    
    var param = null;
    
    for( var i = 0; i < form.elements.length; i++ )
    {
      el = form.elements[i];
      
      t = el.tagName.toLowerCase();
      tp = el.getAttribute( 'type' );
      
      if( t != 'input' && t != 'select' && t != 'textarea' )
      {
        continue;
      }
      
      if( t == 'input' )
      {
        if( tp == 'file' )
        {
          YAHOO.log( 'file uploads are not supported with XML-RPC', 'error', 'Denbel.rpc.XmlRpcClient' );
          continue;
        }
      }
      
      if( t == 'input' && ( tp == 'button' || tp == 'submit' || tp == 'image' ) )
      {
        continue;
      }
      
      n = el.name;
      
      if( !n || n == null || n == '' )
      {
        continue;
      }
      
      if( tp == 'checkbox' || tp == 'radio' )
      {
        if( el.checked )
        {
          v = el.value;
        }
        else
        {
          continue;
        }
      }
      else
      {
        v = el.value;
      }
      
      if( v.indexOf( "\n" ) > -1 || YAHOO.util.Dom.hasClass( el, 'base64' ) )
      {
        v = '__BASE64__' + Denbel.core.Base64.encode( v ) + '__BASE64__';
      }
      
      if( j.length > 1 )
      {
        j += ',';
      }

      j += '"' + n + '":"' + v + '"';
    }
    
    j += '}';
    
    return YAHOO.lang.JSON.parse( j );
  },
  
/**
 * Converts XML-RPC data to XML object data
 * @example:
 * <data>
 *     <item>
 *         <[property]>[value]</[property]>
 *         ...
 *     </item>
 *     ...
 * </data>
 * @param XMLDocument
 * @return XMLDocument
 */
Denbel.rpc.XmlRpcClient.convertXmlRpcToXml = function( xmlRpc )
{
      var data = Denbel.rpc.XmlRpcClient.parseResponse( xmlRpc );
      
      if( !YAHOO.lang.isArray( data ) )
      {
          YAHOO.log( 'Result data is invalid in XmlRpcClient.convertXmlRpcToXml', 'error' );
          return null;
      }
      
      if( data.length == 1 )
      {
          data = data[0];
      }
      
      var xml = Denbel.util.XmlHelper.createXmlDocument();
      
      var root = xml.createElement( 'data' );
      var item = null;
      var node = null;
      var name = null;
      
      for( var i = 0; i < data.length; i++ )
      {
          if( !data[i].properties )
          {
              continue;
          }
          
          item = xml.createElement( 'item' );

          for( var j = 0; j < data[i].properties.length; j++ )
          {
              name = data[i].properties[j];
              
              node = xml.createElement( name );
              node.appendChild( xml.createCDATASection( data[i].get( name ) ) );
              item.appendChild( node );
          }
          
          root.appendChild( item );
          item = null;
      }
      
      xml.appendChild( root );
      
      return xml;
};

/**
 * Parses an XML-RPC response
 * @param XMLDocument
 * @return Array with response data
 */
Denbel.rpc.XmlRpcClient.parseResponse = function( xml )
{
     if( !xml )
     {
         YAHOO.log( 'No XMLDocument given', 'error', 'Denbel.rpc.XmlRpcClient' );
         return null;
     }
     
     var self = Denbel.rpc.XmlRpcClient;
     var params = xml.getElementsByTagName( 'param' );
     
     if( params.length == 0 )
     {
         var fault = xml.getElementsByTagName( 'fault' );
         
         if( fault.length > 0 )
         {
             self.parseFault( fault[0] );
             return null;
         }
     }
     
     self.lastFault = null;
     
     var param = null;
     var valNode = null;
     var type = null;
     var value = null;
     var data = new Array();
     
     for( var i = 0; i < params.length; i++ )
     {
          param = params[i];
          
          if( !param )
          {
              continue;
          }
          
          valNode = Denbel.util.XmlHelper.getNextChildElement( param );
          value = self.convertFromXml( valNode );
          data.push( value );
     }

     return data;
};

  /**
   * Creates an XMLElement from data type
   * @param XMLDocument document
   * @param string type
   * @param mixed value
   * @return XMLElement
   */
  Denbel.rpc.XmlRpcClient.createXmlFromType = function( dom, type, value )
  {
      var t = null;
      var self = Denbel.rpc.XmlRpcClient;
      
      switch( type )
      {
          case 'array':
              t = self.createArrayStruct( dom, value );
              break;

          case 'object':
              t = self.createObjectStruct( dom, value );
              break;

          case 'null':
          case null:
              t = dom.createElement( 'string' );
              break;
          
          case 'dateTime.iso8601':
              t = dom.createElement( type );
              t.appendChild( dom.createCDATASection( self.getISO8601DateString( value ) ) );
              break;

          default:
              if( type == 'string' && ( value.startsWith( '__BASE64__' ) && value.endsWith( '__BASE64__' ) ) )
              {
                  value = value.replace( '__BASE64__', '' );
                  value = value.replace( '__BASE64__', '' );
                  type = 'base64';
              }
              
              t = dom.createElement( type );
              t.appendChild( dom.createCDATASection( value ) );
              break;
      }
      
      return t;
  };
  
  /**
   * Creates an Array structure for use in an XML-RPC document
   * @param XMLDocument document
   * @param Array value
   * @return XMLElement
   */
  Denbel.rpc.XmlRpcClient.createArrayStruct = function( dom, value )
  {
      if( !YAHOO.lang.isArray( value ) )
      {
          YAHOO.log( 'value is not an Array', 'error' );
          return null;
      }
      
      var t = dom.createElement( 'array' );
      var d = dom.createElement( 'data' );
      var m = null;
      var mt = null;
      
      var self = Denbel.rpc.XmlRpcClient;
      var valueType = null;
      
      for( var i = 0; i < value.length; i++ )
      {
          valueType = self.getValueType( value[i] );
          
          m = dom.createElement( 'value' );
          mt = this.createXmlFromType( dom, valueType, value[i] );
          
          if( mt != null )
          {
             m.appendChild( mt );
          }
          
          d.appendChild( m );
      }
      
      t.appendChild( d );
      return t;
  };
  
  /**
   * Creates an object structure
   * @param XMLDocument document
   * @param object value
   * @return XMLElement
   */
  Denbel.rpc.XmlRpcClient.createObjectStruct = function( dom, value )
  {
      if( !YAHOO.lang.isObject( value ) )
      {
          YAHOO.log( 'value is not an object', 'error' );
          return null;
      }
     
      var t = dom.createElement( 'struct' );
      var m = null;
      var mt = null;
      var mv = null;
      var mn = null;
      
      var self = Denbel.rpc.XmlRpcClient;
      var valueType = null;
      
      for( var key in value )
      {
          valueType = self.getValueType( value[key] );
          
          m = dom.createElement( 'member' );
          
          mn = dom.createElement( 'name' );
          mn.appendChild( dom.createCDATASection( key ) );
          
          mv = dom.createElement( 'value' );
          mt = this.createXmlFromType( dom, valueType, value[key] );
          mv.appendChild( mt );
          
          m.appendChild( mn );
          m.appendChild( mv );
          
          t.appendChild( m );
      }
      
      return t;
  };
  
  /**
   * Converts a Denbel.rpc.XmlRpcMessage to string
   * @param Denbel.rpc.XmlRpcMessage
   * @return String
   */
  Denbel.rpc.XmlRpcClient.messageToString = function( message )
  {
    var xml = Denbel.rpc.XmlRpcClient.createRequest( message );
    return Denbel.util.XmlHelper.xmlToString( xml );
  };

  /**
   * Creates a request XML from XmlRpcMessage
   * @param Denbel.rpc.XmlRpcMessage
   * @return XMLDocument
   */
  Denbel.rpc.XmlRpcClient.createRequest = function( message )
  {
      var dom = Denbel.util.XmlHelper.createXmlDocument( '1.0', 'utf-8' );
      var root = dom.createElement( 'methodCall' );
      
      var el = dom.createElement( 'methodName' );
      el.appendChild( dom.createTextNode( message.getMethod() ) );
      root.appendChild( el );
      
      var paramRoot = dom.createElement( 'params' );
      root.appendChild( paramRoot );
      
      var p = null; // param
      var v = null; // value
      var t = null; // type
      
      var valueType = null;
      var type = null;
      var value = null;
      
      var self = Denbel.rpc.XmlRpcClient;
      var params = message.getParameters();
      
      for( var i = 0; i < params.length; i++ )
      {
          p = dom.createElement( 'param' ); // param node
          v = dom.createElement( 'value' ); // value node
          
          value  = params[i].getValue();

          if( !params[i].isBase64() )
          {
             type = self.getValueType( value );
          }
          else
          {
             type = 'base64';
          }
          
          t = self.createXmlFromType( dom, type, value );
          
          if( t != null )
          {
             v.appendChild( t );
          }
          
          p.appendChild( v );
          paramRoot.appendChild( p );
          
          p = null;
          v = null;
          t = null;
      }
      
      dom.appendChild( root );
      
      return dom;
  };
  
  /**
   * Converts an XML-RPC value to a typed variable
   * @param XMLElement value node
   * @return mixed
   */
  Denbel.rpc.XmlRpcClient.convertFromXml = function( valNode )
  {
      if( valNode.tagName != 'value' )
      {
          YAHOO.log( 'convertFromXml: did not receive a "value" node', 'error' );
          return null;
      }
      
      var typeNode = Denbel.util.XmlHelper.getNextChildElement( valNode );
      var rpcType  = typeNode.tagName.toLowerCase();
      var ecmaType = Denbel.rpc.XmlRpcClient.rpcToEcmaConversion.get( rpcType );
      
      var self  = Denbel.rpc.XmlRpcClient;
      var value = null;
      
      switch( ecmaType )
      {
          case 'string':
              value = Denbel.util.XmlHelper.getElementValue( typeNode );
              break;
          
          case 'int':
              value = parseInt( Denbel.util.XmlHelper.getElementValue( typeNode ) );
              break;
          
          case 'boolean':
              value = ( ( Denbel.util.XmlHelper.getElementValue( typeNode ) == 'true' ) ? true : false );
              break;
          
          case 'date':
              value = self.parseISODate( Denbel.util.XmlHelper.getElementValue( typeNode ) );
              break;
          
          case 'float':
          case 'double':
              value = parseFloat( Denbel.util.XmlHelper.getElementValue( typeNode ) );
              break;
          
          case 'array':
              value = self.createArrayFromStruct( valNode );
              break;
          
          case 'object':
              value = self.createObjectFromStruct( valNode );
              break;
          
          case 'base64':
              value = Denbel.core.Base64.decode( Denbel.util.XmlHelper.getElementValue( typeNode ) );
              break;
          
          default:
              value = null;
              break;
      }
      
      return value;
  };
  
  /**
   * Parses an ISO8601 date-time representation to a ECMA Date object
   * @param string date
   * @return Date
   */
  Denbel.rpc.XmlRpcClient.parseISODate = function( sDate )
  {
      if( !sDate || !YAHOO.lang.isString( sDate ) )
      {
          YAHOO.log( 'Invalid parameter type in XmlRpcClient.parseISODate', 'error' );
          return null;
      }
      
      // ISO 8601 date example: 2008-03-01T15:01:22+01:00
      // which is:
      // 2008-03-01 = first of March 2008
      // T = time
      // 15:01:22 = 1 minute past 15h and 22 seconds
      // +01:00 = timezone +1 hour GMT
      
      var datePart = sDate.split( 'T' )[0];
      var restPart = sDate.split( 'T' )[1];
      
      var timePart = restPart.substring( 0, 8 );
      var zonePart = restPart.substring( 8 );
      
      var date = datePart.split( '-' );
      var time = timePart.split( ':' );
      
      var r = new Date();
      
      r.setFullYear( date[0] );
      r.setMonth( ( date[1] - 1 ) );
      r.setDate( date[2] );
      r.setHours( time[0] );
      r.setMinutes( time[1] );
      r.setSeconds( time[2] );
      r.setMilliseconds( 0 );
      
      return r;
  };
  
  /**
   * Creates an Array from structure
   * @param XMLElement structure
   * @return Array
   */
  Denbel.rpc.XmlRpcClient.createArrayFromStruct = function( struct )
  {
      if( struct.tagName != 'value' )
      {
          YAHOO.log( 'createArrayFromStruct did not receive a "value" node.', 'error', 'Denbel.rpc.XmlRpcClient' );
          return null;
      }
      
      var values = struct.getElementsByTagName( 'data' )[0].childNodes;
      var value  = null;
      
      var self  = Denbel.rpc.XmlRpcClient;
      var array = new Array();
      
      for( var i = 0; i < values.length; i++ )
      {
          if( !values[i] || values[i].tagName != 'value' )
          {
              continue;
          }
          
          value = self.convertFromXml( values[i] );
          array.push( value );
      }
      
      return array;
  };
  
  /**
   * Creates an object structure for structure
   * @param XMLElement structure
   * @return Denbel.lang.Object
   */
  Denbel.rpc.XmlRpcClient.createObjectFromStruct = function( struct )
  {
      if( struct.tagName != 'value' )
      {
          YAHOO.log( 'createObjectFromStruct did not receive a "value" node', 'error' );
          return null;
      }
      
      var members  = struct.getElementsByTagName( 'struct' )[0].childNodes;
      var name     = null;
      var value    = null;
      
      var self = Denbel.rpc.XmlRpcClient;
      var obj  = new Denbel.lang.Object();
      
      for( var i = 0; i < members.length; i++ )
      {
          if( !members[i] )
          {
              continue;
          }
          
          name  = Denbel.util.XmlHelper.getElementValue( members[i].getElementsByTagName( 'name' )[0] );
          value = self.convertFromXml( members[i].getElementsByTagName( 'value' )[0] );

          obj.set( name, value );
      }
      
      return obj;
  };
  
  /**
   * Parses the XML fault response and generate an error
   * @param XMLElement fault
   * @return void
   */
  Denbel.rpc.XmlRpcClient.parseFault = function( fault )
  {
     if( !fault )
     {
         return;
     }
     
     var struct      = fault.getElementsByTagName( 'value' )[0];
     var faultObj    = Denbel.rpc.XmlRpcClient.createObjectFromStruct( struct );
     
     Denbel.rpc.XmlRpcClient.lastFault = faultObj;
     YAHOO.log( 'XmlRpcClient error [' + faultObj.get( 'faultCode' ) + ']: ' + faultObj.get( 'faultString' ), 'error' );
  };
  
  /**
   * Returns the data type of the given value
   * @param mixed value
   * @return string
   */
  Denbel.rpc.XmlRpcClient.getValueType = function( value )
  {
      if( value == null )
      {
        return 'null';
      }
      
      var dt = null;
      
      if( YAHOO.lang.isBoolean( value ) )
      {
        dt = 'boolean';
      }
      else if( YAHOO.lang.isArray( value ) )
      {
        dt = 'array';
      }
      else if( YAHOO.lang.isNumber( value ) )
      {
        var tmp = value.toString();
        
        if( tmp.indexOf( '.' ) > -1 )
        {
          dt = 'double';
        }
        else
        {
          dt = 'int';
        }
      }
      else if( YAHOO.lang.isString( value ) )
      {
        dt = 'string';
      }
      else if( YAHOO.lang.isObject( value ) )
      {
        if( value.getFullYear )
        {
          dt = 'dateTime.iso8601';
        }
        else
        {
          dt = 'object';
        }
      }
      
      if( !Denbel.util.inArray( dt, Denbel.rpc.XmlRpcClient.dataTypes ) )
      {
        YAHOO.log( 'Unsupported data type for value in Denbel.rpc.XmlRpcParameter.getType()', 'error' );
        return null;
      }
      
      return dt;
  };

// extend
YAHOO.extend( Denbel.rpc.XmlRpcClient, Denbel.rpc.Protocol,
{
  connectionObject: null,

  /**
   * Creates a message for this protocol
   * @param string method name
   * @return Denbel.rpc.XmlRpcMessage
   */
  createMessage: function( method )
  {
      return new Denbel.rpc.XmlRpcMessage( method );
  },
    
  /**
   * Calls a service
   * @param XmlRpcMessage message
   * @param object callback YAHOO connection callback
   * @param bool set to true to get from cache
   * @param string params extra url parameters
   * @return void
   */
  callService: function( message, callback, fromCache, params )
  {
    if( !message )
    {
      YAHOO.log( 'No XmlRpcMessage given', 'error' );
      return;
    }
    
    if( fromCache !== true )
    {
      if( params )
      {
        params += '&';
      }
      else
      {
        params = '';
      }
     
      params += 'nocache=1';
    }
    
    var dom = Denbel.rpc.XmlRpcClient.createRequest( message );
	
	var a = this.convertXmlToString( dom );
	
    this.sendRequest( this.convertXmlToString( dom ), callback, params );
  },
  
  /**
   * Converts a XML document to string
   * @param XMLDocument object
   * @return string
   */
  convertXmlToString: function( dom )
  {
    return Denbel.util.XmlHelper.xmlToString( dom );
  },
  
  /**
   * Returns the fault of the last response if any
   * @return Denbel.lang.Object
   */
  getFault: function()
  {
    return Denbel.rpc.XmlRpcClient.lastFault;
  },
    
    /**
     * Sends a request to a service
     * @param string XML data
     * @param object YAHOO connection callback
     * @param string p extra url parameters
     * @return void
     */
    sendRequest: function( data, callback, p )
    {
      if( !callback || !callback.success || !callback.failure )
      {
        YAHOO.log( 'Callback is not properly set up', 'error', 'Denbel.rpc.XmlRpcClient' );
        return;
      }
      
      var url = this.cfg.url;
      
      if( p )
      {
        url += '?' + p;
      }
        
      YAHOO.util.Connect.initHeader( 'X-Application-Id', Denbel.Website.createFP(), true );
      this.connectionObject = YAHOO.util.Connect.asyncRequest( 'post', url,
      {
       success: function( e )
       {
         Denbel.rpc.XmlRpcClient.lastResponse = e.responseText;
         var data = Denbel.rpc.XmlRpcClient.parseResponse( e.responseXML );
         
         if( data )
         {
           e.argument.callback.success.call( this,
           {
             data: data,
             fault: null,
             argument: e.argument.callback.argument,
             responseText: e.responseText,
             responseXML: e.responseXML,
             status: e.status,
             statusText: e.statusText,
             responseBody: e.responseBody
           } );
         }
         else
         {
           e.argument.callback.failure.call( this,
           {
              data: null,
              fault: e.argument.obj.getFault(),
              argument: e.argument.callback.argument,
              responseText: e.responseText,
              responseXML: e.responseXML,
              status: e.status,
              statusText: e.statusText,
              responseBody: e.responseBody
           } );
         }
       },
       failure: function( e )
       {
         var data = null;
         
         try
         {
           Denbel.rpc.XmlRpcClient.lastResponse = e.responseText;
           data = Denbel.rpc.XmlRpcClient.parseResponse( e.responseXML );
         }
         catch( err )
         {
           data = null;
         }
           
         e.argument.callback.failure.call( this,
         {
           'data': data,
           'fault': e.argument.obj.getFault(),
           'status': e.status,
           'statusText': e.statusText,
           'argument': e.argument.callback.argument,
           'responseText': e.responseText,
           'responseXML': e.responseXML,
           'status': e.status,
           'statusText': e.statusText,
           'responseBody': e.responseBody
         } );
       },
       argument:
       {
         obj: this,
         callback: callback
       }
      }, data );
    },
    
    /**
     * Returns a value indicating the request is in progress
     * @return Boolean
     */
    isInProgress: function()
    {
      if( this.connectionObject )
      {
        return YAHOO.util.Connect.isCallInProgress( this.connectionObject );
      }
      
      return false;
    },
    
    /**
     * Aborts a request
     * @param object callback
     * @param Boolean isTimeout
     * @return Boolean
     */
    abort: function( callback, isTimeout )
    {
      if( this.connectionObject )
      {
        if( !isTimeout )
        {
          isTimeout = false;
        }
        
        if( !callback )
        {
          callback =
          {
            success: function( res )
            {
            },
            failure: function( res )
            {
            },
            argument: null
          };
        }
      
        return YAHOO.util.Connect.abort( this.connectionObject, callback, isTimeout );
      }
      
      return false;
    },
    
    /**
     * Sets a header
     * @param String name
     * @param String value
     * @param Boolean isDefault
     * @return void
     */
    setHeader: function( name, value, isDefault )
    {
      YAHOO.util.Connect.initHeader( name, value, isDefault );
    },
    
    /**
     * Returns the ISO8601 formatted date/time string for a Date object
     * @param Date object
     * @return string
     */
    getISO8601DateString: function( date )
    {
          if( !date )
          {
              return null;
          }
          
          var str = date.getFullYear() + '-';
          var tmp = ( date.getMonth() + 1 ).toString();
          
          if( tmp.length < 2 )
          {
              tmp = '0' + tmp;
          }
          
          str += tmp + '-';
          tmp = date.getDate().toString();
          
          if( tmp.length < 2 )
          {
              tmp = '0' + tmp;
          }
          
          str += tmp + 'T';
          tmp = date.getHours().toString();
          
          if( tmp.length < 2 )
          {
              tmp = '0' + tmp;
          }
          
          str += tmp + ':';
          tmp = date.getMinutes().toString();
          
          if( tmp.length < 2 )
          {
              tmp = '0' + tmp;
          }
          
          str += tmp + ':';
          tmp = date.getSeconds().toString();
          
          if( tmp.length < 2 )
          {
              tmp = '0' + tmp;
          }
          
          str += tmp;
          tmp = date.getTimezoneOffset();
          
          var diff = -( tmp / 60 ); // time difference
          var hours = parseInt( diff ); // hours
          var mins = ( ( diff - hours ) * 60 ).toString(); // minutes
          
          hours = hours.toString();
          
          if( hours.substring( 0, 1 ) == '-' )
          {
              if( hours.length < 3 )
              {
                  hours = '-0' + hours;
              }
          }
          else if( hours.substring( 0, 1 ) == '+' )
          {
              if( hours.length < 3 )
              {
                  hours = '+0' + hours;
              }
          }
          else
          {
              if( hours.length < 2 )
              {
                  hours = '+0' + hours;
              }
          }
          
          if( mins.length < 2 )
          {
              mins = '0' + mins;
          }
          
          str += hours + ':' + mins;
          
          return str;
    },
    
    /**
     * Converts this object to its string representation
     * @return string
     */
    toString: function()
    {
        return 'Denbel.rpc.XmlRpcClient';
    }
} );

/******************************************************************************/

/**
 * XmlRpcMessage object
 * @param string method
 * @return void
 */
Denbel.rpc.XmlRpcMessage = function( method )
{
      Denbel.rpc.XmlRpcMessage.superclass.constructor.call( this );
      this.value = method;
};

// extends
YAHOO.extend( Denbel.rpc.XmlRpcMessage, Denbel.rpc.ProtocolMessage,
{
	/**
	 * Sets the method to call
	 * @param string method name
	 * @return void
	 */
      setMethod: function( name )
      {
          this.value = name;
      },
      
      /**
       * sets a new parameter
       * @param mixed value
       * @param boolean base64
       * @return int
       */
      setParameter: function( value, base64 )
      {
        return this.createAndAddParameter( value, base64 );
      },
      
      /**
       * Create and add a parameter
       * @param mixed value
       * @param bool base64 set to true to identify the value as base64 encoded
       * @return int
       */
      createAndAddParameter: function( value, base64 )
      {
          if( !base64 )
          {
              base64 = false;
          }
          
          var p = new Denbel.rpc.XmlRpcParameter( value, null, base64 );
          return this.addParameter( p );
      },
      
    /**
     * Adds a complete form to the message
     * @param Variant string ID of the Form element or an HTMLFormElement
     * @return void
     */
    addForm: function( form )
    {
      this.addParameter( new Denbel.rpc.XmlRpcParameter( Denbel.rpc.XmlRpcClient.convertFormToJSon( form ) ) );
    },

	/**
	 * Gets the method name
	 * @return string
	 */
      getMethod: function()
      {
          return this.value;
      },

	/**
	 * Converts this object to its string representation
	 * @return string
	 */
      toString: function()
      {
          return 'Denbel.rpc.XmlRpcMessage';
      }
} );

/******************************************************************************/

/**
 * XmlRpcParameter object
 * @param mixed value (this must be of a type defined in Denbel.rpc.XmlRpcClient.dataTypes)
 * @param string name the name of the parameter or null to not specify a name
 * @param bool base64 set to true to indicate the specified value is base64 encoded
 * @return void
 */
Denbel.rpc.XmlRpcParameter = function( value, name, base64 )
{
    Denbel.rpc.XmlRpcParameter.superclass.constructor.call( this, name, value );

    if( !base64 )
    {
        base64 = false;
    }

    if( YAHOO.lang.isString( value ) )
    {
        if( value.startsWith( '__BASE64__' ) && value.endsWith( '__BASE64__' ) )
        {
            base64 = true;
            value = value.replace( '__BASE64__', '' );
            value = value.replace( '__BASE64__', '' );
        }
    }

    this.value = value;
    this.base64 = base64;
};

// prototype
YAHOO.extend( Denbel.rpc.XmlRpcParameter, Denbel.rpc.ProtocolParameter,
{
    // fields
    base64: false,
    
    /**
     * Returns the data type of the internal value
     * @return string
     */
    getType: function()
    {
        return Denbel.rpc.XmlRpcClient.getValueType( this.value );
    },
    
    /**
     * Returns a value indicating the current value is base64 encoded
     * @return bool
     */
    isBase64: function()
    {
        return this.base64;
    },
    
    /**
     * Converts this object to its string representation
     * @return string
     */
    toString: function()
    {
        return 'Denbel.rpc.XmlRpcParameter';
    }
} );

