/*
 * Distributed under the terms of the GPL:
 * Copyright (c) 2009, Matthias Sonnenkalb
 * (www.matthias-sonnenkalb.net, msonnenkalb@gmx.de)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
function msAjax(){

	var xhr = false;

	this.config = {
		'URL'			: 'http://www.matthias-sonnenkalb.net/',
		'widgetBase'	: 'widgetsbase.php',
		'asynchronous'	: true,
		'method'		: 'POST',
		'charset'		: 'UTF-8',
		'cache'			: false,
		'timer'			: 0,
		'interval'		: 0,
		'headers'		: {
			'X-Requested-With'		: 'XMLHttpRequest',
			'X-MsAjax-Version'		: '1.2',
			'Accept'				: 'text/javascript, text/html, application/xml, text/xml, */*',
			'Content-Type'			: 'application/x-www-form-urlencoded; charset=UTF-8'
		}
	};

	/**
	 * send.
	 *
	 * send request (try to get the widget).
	 */
	this.send = function( widgetName, callbackFunc, argsObj ) {
		if( xhr ) {
			xhr.open( this.config.method, this.config.URL + this.config.widgetBase, this.config.asynchronous );

			if( this.config.asynchronous == true ) {
				xhr.onreadystatechange = function() {
					if( xhr.readyState == 4 ) {
						// performs catch if retval === null
						try {
							retval = JSON.parse( unescape( xhr.responseText ));
							if( !!callbackFunc ) {
								callbackFunc( retval );
							}
						}catch( e ){
							alert("invalid server response");
						}
					}
				};
			}

			// setting up headers
			for( var header in this.config.headers ) {
				xhr.setRequestHeader( header, this.config.headers[header] );
			}

			xhr.send( data( widgetName, argsObj ));

			if( this.config.asynchronous == false ) {
				// performs catch if retval === null
				try {
					retval = JSON.parse( unescape( xhr.responseText ));
					if( !!callbackFunc ) {
						callbackFunc( retval );
					}
				}catch( e ){
					alert("invalid server response");
				}
			}
		}
	}

	/**
	 * timerSend.
	 *
	 * will be called once if timerout (config.timer) is reached.
	 */
	this.timerSend = function( objInstance, widgetName, callbackFunc, argsObj ) {
		setTimeout( function(){
			objInstance.send( widgetName, callbackFunc, argsObj );
		}, objInstance.config.timer );
	}

	/**
	 * intervalSend.
	 *
	 * will be called every n-milliseconds (n is defined by value of config.interval).
	 */
	this.intervalSend = function( objInstance, widgetName, callbackFunc, argsObj ) {
		// because the next request takes place in 'config.interval' milliseconds
		setInterval( function(){
			objInstance.send( widgetName, callbackFunc, argsObj );
		}, objInstance.config.interval );
		// we have to perform the first request of widget-function here
		this.send( widgetName, callbackFunc, argsObj );
	}

	/**
	 * set.
	 *
	 * configuration setter.
	 */
	this.set = function( obj, prop, value ){
		obj[prop] = value;
	}

	/**
	 * set.
	 *
	 * configuration getter.
	 * returns 'undefined' if propterty not available
	 */
	this.get = function( obj, prop ){
		return obj[prop];
	}

	/**
	 * toString.
	 *
	 * provides the configuration object as a string.
	 */
	this.toString = function( obj ) {
		var str = "{\n";

		for( var prop in obj ){
			if( typeof obj[prop] == "object" ) {
				str += prop + " : " + this.toString( obj[prop] );
			}else{
				str += prop + " = " + obj[prop] + "\n";
			}
		}

		return str + "}\n";
	}

	/**
	 * build up a request object
	 *
	 * widgetName (string) - name of widget we want to request
	 * argsObj (json-object) - parameter given to widget (optional)
	 *
	 * return boolean true or false
	 */
	var data = function( widgetName, argsObj )
	{
		if( !widgetName )
		{
			alert('Error :: No widget-name given!');
			return false;
		}

		var data = new Object();
		data.widgetName = widgetName;

		if( argsObj ) {
			data.argsObj = new Object();
			for( var property in argsObj ) {
				if( argsObj[property] != null && typeof argsObj[property] != 'object' ) {
					data.argsObj[property] = encodeURIComponent( argsObj[property] );
				// okay an object (object or array) was found
				// than you have to encode the properties of that object by using encodeURIComponent
				// function before you call this action (that is while you create this object
				// before you call msAjax.send)
				}else{
					data.argsObj[property] = argsObj[property];
				}
			}
		} else {
			data.argsObj = null;
		}

		return 'args=' + escape( JSON.stringify( data ));
	}

	// -------------------------------------------------------
	// try to get an request instance
	// -------------------------------------------------------
	// Mozilla, Opera, Safari and IE (>= v7)
	if( window.XMLHttpRequest ) {
		xhr = new XMLHttpRequest();
	// IE 6 or prior
	} else {
		try {
			xhr = new ActiveXObject("Msxml2.XMLHTTP");
		} catch( e ) {
			try {
				xhr = new ActiveXObject("Microsoft.XMLHTTP");
			} catch( e ) {
				alert("cannot get an instance of XMLHttpRequest object");
				xhr = false;
			}
		}
	}
}