/**
 * @author p.stenzel
 */

Ajax.RPCRequest = Class.create(Ajax.Base, {
  _complete: false,
  response: null,
  responseObject: new Array(),
  initialize: function($super, url, options) {
    $super(options);
    this.transport = Ajax.getTransport();
    this.request(url);
  },
  request: function(url) {
    this.url = url;
    this.method = this.options.method;
    var params = Object.clone(this.options.parameters);

    if (!['get', 'post'].include(this.method)) {
      // simulate other verbs over post
      params['_method'] = this.method;
      this.method = 'post';
    }

    this.parameters = params;

    if (params = Object.toQueryString(params)) {
      // when GET, append parameters to URL
      if (this.method == 'get')
        this.url += (this.url.include('?') ? '&' : '?') + params;
      else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
        params += '&_=';
    }

    try {
		
      this.response = new Ajax.Response(this);
      if (this.options.onCreate) this.options.onCreate(this.response);
      Ajax.Responders.dispatch('onCreate', this, this.response);

      this.transport.open(this.method.toUpperCase(), this.url, this.options.asynchronous);

      if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);

      this.transport.onreadystatechange = this.onStateChange.bind(this);
      this.setRequestHeaders();

//      this.body = this.method == 'post' ? (this.options.postBody || params) : null;
		this.body = this.setRequestTags();
      this.transport.send(this.body);

      /* Force Firefox to handle ready state 4 for synchronous requests */
      if (!this.options.asynchronous && this.transport.overrideMimeType)
        this.onStateChange();

    }
    catch (e) {
		this.dispatchException(e);
    }
  },

  onStateChange: function() {
    var readyState = this.transport.readyState;
    if (readyState > 1 && !((readyState == 4) && this._complete))
      this.respondToReadyState(this.transport.readyState);
  },

  setRequestHeaders: function() {
    var headers = {
      'X-Requested-With': 'XMLHttpRequest',
      'X-Prototype-Version': Prototype.Version,
      'Accept': 'text/xml, */*'
    };

    if (this.method == 'post') {
      headers['Content-type'] = this.options.contentType +
        (this.options.encoding ? '; charset=' + this.options.encoding : '');

      /* Force "Connection: close" for older Mozilla browsers to work
       * around a bug where XMLHttpRequest sends an incorrect
       * Content-length header. See Mozilla Bugzilla #246651.
       */
      if (this.transport.overrideMimeType &&
          (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
            headers['Connection'] = 'close';
    }

    // user-defined headers
    if (typeof this.options.requestHeaders == 'object') {
      var extras = this.options.requestHeaders;

      if (Object.isFunction(extras.push))
        for (var i = 0, length = extras.length; i < length; i += 2)
          headers[extras[i]] = extras[i+1];
      else
        $H(extras).each(function(pair) { headers[pair.key] = pair.value });
    }

    for (var name in headers)
      this.transport.setRequestHeader(name, headers[name]);
  },

  setRequestTags: function() {
  	this.methodName = this.options.methodName;
	var returnString = "<?xml version='1.0'?>\n"
		+"<methodCall>\n"
			+"<methodName>"+this.methodName+"</methodName>\n";
			if(this.options.methodParams)	{
				returnString += "<params>\n";
					var self = this;
					this.options.methodParams.each(function(param){
						returnString += "<param>\n"
							+"<value>"
								+self.encodeXML(param)
							+"</value>\n"
						+"</param>\n";
					}
//					.createCallback(this)
					)
				returnString += "</params>\n";
			}
		returnString += "</methodCall>\n";
	return returnString;
  },

  encodeXML: function(param)	{
  	var output = "";
	var returnString = "";
	var self = this;

	switch(typeof param)	{
		case 'object':
			if (Object.isArray(param)) {
				returnString += this.getTag("array",
					this.getTag("data",
						this.getArrayValues(param)
					)
				);
			}
			else 
				if (Object.isHash(param)) {
					returnString += this.getTag("struct",
						this.getStructValues(param)
					);
				}
				else {
					returnString += this.getTag("struct",
						this.getStructValues(param)
					);
				}
			break;
		case 'number':
			if (param % 1 == 0) {
				returnString += this.getTag('int',param);
			} else	{
				returnString += this.getTag('double',param);
			}
			break;
		case 'boolean':
			returnString += this.getTag('boolean',param);
			break;
		default:
			returnString += this.getTag('string',param);
	}
	return returnString;
//	<array>
//	   <data>
//	      <value><i4>12</i4></value>
//	      <value><string>Egypt</string></value>
//	      <value><boolean>0</boolean></value>
//	      <value><i4>-31</i4></value>
//	      </data>
//	   </array>  	
  },

  getTag: function(name, arg)	{
  	/**
  	 * @todo : aus "Test&Test Test" wird "Test&Test; Test" - wo kommt das Semikolon denn jetzt her?!? 
  	 */
  	if(name == "string"){
		arg = "<![CDATA[" + arg + "]]>"
	}
 	return "<"+name+">"+arg+"</"+name+">\n"
  },
  
  getArrayValues: function(arg)	{
  	var returnString = "";
	var self = this;
  	arg.each(function(child)	{
		returnString += self.getTag('value',
			self.encodeXML(child)
		);
	})
//	.createCallback(this))
	return returnString;
  },
  
  getStructValues: function(arg)	{
  	var returnString = "";
	if (Object.isHash(arg)) {
		var self = this;
		arg.each(function(pair)	{
			returnString += self.getTag('member', self.getTag('name', pair.key) +
			self.getTag('value', self.encodeXML(pair.value)));
		}
//		.createCallback(this)
		)
	}	else {
		for (var name in arg) {
			returnString += this.getTag('member', this.getTag('name', name) +
			this.getTag('value', this.encodeXML(arg[name])));
		}
	}
	return returnString;  	
  },

  handleResponse: function(xml)	{
  	this.responseObject = new Array();
   	var self = this;
	var nodeList = xml.getElementsByTagName("param");
	for(var i=0;i<nodeList.length;i++)	{
		var value = false;
		value = this.getNode(nodeList[i]);
		if(value && value!=undefined)	{
			value.each(function(val,key)	{
				if(val!=undefined && val!="undefined")	{
					self.responseObject.push(val);
				}
			})
			
//			value.each(function(test)	{
//				alert("Test1: "+test);
//				if (test!=undefined && test != "undefined") {
//					for(var test2 in test)	{
//						alert(test+": "+test[test2]);
//					}
//				}
//			})

//			this.responseObject.push(value);
		}
	}
  },

  getNode: function(nodeList)	{
  	var output;
	nodeList = nodeList.childNodes;

	for(var node=0;node<nodeList.length;node++)	{
		var tagName = nodeList[node].tagName;
		if(tagName!=undefined)	{
			switch (tagName) {
				case "array":
					try {
						output = this.getArrayNodeValues(nodeList[node]);
					}	catch(e)	{
						alert("Fehler in Zeile "+e.line+"\n"+e.message);
					}
					break;
				case "struct":
					try {
						output = this.getStructNodeValues(nodeList[node]);
					}	catch(e)	{
						alert("Fehler in Zeile "+e.line+"\n"+e.message);
					}
					break;					
				case "value":
					output = this.getNode(nodeList[node]);
					break;
				default:
					if (nodeList[node].textContent) {
						output = nodeList[node].textContent;
					}	else	{
						output = nodeList[node].text;
					}
			}
			break;
		}
	}
	return output;
  },

  getArrayNodeValues: function(nodeList)	{
  	var resultArr = new Array();
		
	var structs = nodeList.getElementsByTagName('struct');
	nodeList = nodeList.getElementsByTagName('value');
	for(var i=0;i<nodeList.length;i++)	{
		if(nodeList[i].parentNode.tagName=='data')	{
			value = this.getNode(nodeList[i]);
			resultArr.push(value);
		}
	}
	return resultArr;
  },

  getStructNodeValues: function(nodeList)	{
  	var resultArr = new Object();
	
  	nodeList = nodeList.getElementsByTagName('member');
	for (var i = 0; i < nodeList.length; i++) {
		var test = nodeList[i];
		var name = nodeList[i].getElementsByTagName('name')
		name = name[0]
		if (name.textContent) {
			name = name.textContent;
		}	else	{
			name = name.text;
		}
		var value = nodeList[i].getElementsByTagName('value');
		value = value[0];
		value = this.getNode(value);
		if (!Object.isArray(resultArr[name])) {
			resultArr[name] = new Array();
		}
		resultArr[name].push(value);
	}
	return resultArr;
  },

  success: function() {
    var status = this.getStatus();
    return !status || (status >= 200 && status < 300);
  },

  getStatus: function() {
    try {
      return this.transport.status || 0;
    } catch (e) { return 0 }
  },

  respondToReadyState: function(readyState) {
    var state = Ajax.RPCRequest.Events[readyState];
	this.response = new Ajax.Response(this);

    if (state == 'Complete') {
      try {
        this._complete = true;
        (this.options['on' + this.response.status]
         || this.options['on' + (this.success() ? 'Success' : 'Failure')]
         || Prototype.emptyFunction)(this.response, this.response.headerJSON);
      } catch (e) {
        this.dispatchException(e);
      }

      var contentType = this.response.getHeader('Content-type');
      if (this.options.evalJS == 'force'
          || (this.options.evalJS && this.isSameOrigin() && contentType
          && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
        this.evalResponse();
    }

    try {
		if(state=='Complete')	{
			if (!this.response.transport.responseXML) {
				var parser = new DOMParser();
				var doc = parser.parseFromString(this.response.transport.responseText, "text/xml");
				this.handleResponse(doc);
			}
			else {
				this.handleResponse(this.response.transport.responseXML);
			}
		}
      (this.options['on' + state] || Prototype.emptyFunction)(this.response, this.response.headerJSON);
      Ajax.Responders.dispatch('on' + state, this, this.response, this.response.headerJSON);
    } catch (e) {
      this.dispatchException(e);
    }

    if (state == 'Complete') {
      // avoid memory leak in MSIE: clean up
      this.transport.onreadystatechange = Prototype.emptyFunction;
    }
  },

  isSameOrigin: function() {
    var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
    return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
      protocol: location.protocol,
      domain: document.domain,
      port: location.port ? ':' + location.port : ''
    }));
  },

  getHeader: function(name) {
    try {
      return this.transport.getResponseHeader(name) || null;
    } catch (e) { return null }
  },

  evalResponse: function() {
    try {
      return eval((this.transport.responseText || '').unfilterJSON());
    } catch (e) {
      this.dispatchException(e);
    }
  },

  dispatchException: function(exception) {
    (this.options.onException || Prototype.emptyFunction)(this, exception);
    Ajax.Responders.dispatch('onException', this, exception);
  }
});

Ajax.RPCRequest.Events =
  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
  
  
function dump(arr,level) {
var dumped_text = "";
if(!level) level = 0;

//The padding given at the beginning of the line.
var level_padding = "";
for(var j=0;j<level+1;j++) level_padding += "    ";

if(typeof(arr) == 'object') { //Array/Hashes/Objects
 for(var item in arr) {
  var value = arr[item];
 
  if(typeof(value) == 'object') { //If it is an array,
   dumped_text += level_padding + "'" + item + "' ...\n";
   dumped_text += dump(value,level+1);
  } else {
	if (!Object.isFunction(value)) {
		dumped_text += level_padding + "'" + item + "' => \"" + value + "\"\n";
	}
  }
 }
} else { //Stings/Chars/Numbers etc.
 dumped_text = "===>"+arr+"<===("+typeof(arr)+")";
}
return dumped_text;
}