/**
 * <p>Sky.JSONUtil</p>
 *
 * <p>Copyright &copy; 2008 <a href="http://www.mysign.ch/">MySign AG</a></p>
 * 
 * @author Marcus Fihlon (<a href="http://www.mysign.ch/">MySign AG</a>)
 * @version $Revision$
 * @dependencies <a href="http://www.prototypejs.org/">Prototype >= 1.6.0.2</a>
 */

 // create the name space
var Sky;
if ( !Sky )
{
	Sky = {};
}

// create the JSONUtil class
Sky.JSONUtil = Class.create ();

Sky.JSONUtil._REQUIRED_PROTOTYPE = '1.6.0.2';

Sky.JSONUtil.prototype =
{
	/**
	 * <p>This is the constructor of this class.</p>
	 *
	 * @param opts map of options
	 */
	initialize: function ( opts )
	{
		// checking Sky.PrototypeExtensions
		if( Object.isUndefined( Sky.PrototypeExtensions ) )
		{
			throw( "Sky.JSONUtil requires the Sky.PrototypeExtensions JavaScript framework" );
		}
		// checking Prototype
		if( !Sky.PrototypeExtensions.isPrototypeLoaded( Sky.JSONUtil._REQUIRED_PROTOTYPE ) )
		{
			throw( "Sky.JSONUtil requires the Prototype JavaScript framework >= " + Sky.JSONUtil._REQUIRED_PROTOTYPE );
		}
	
		// default options
		this.options = 
		{
			// element references
			'window': Sky.Window.instances.last (),

			// plugin functions (getting the response data as parameter)
			'contentPlugin'        : null,
			'dialogPlugin'         : null,
			'formPlugin'           : null,
			'processResponsePlugin': null,

			// you can specify callback functions
			'onSuccess'    : null,
			'onSuccessOnce': null,
			'onFailure'    : null,
			'onFailureOnce': null,

			// configuration options
			'closeWindowOnSuccess'       : false,
			'contentTarget'              : null,
			'dialogOptions'              : {},
			'skipMessages'               : false,
			'overrideDataOkWithTrue'     : false,
			'overrideDataOkWithFalse'    : false,
			'showContentInDialog'        : true,
			'showFormInDialog'           : false,
			'targetDialog'               : null,
			
			// overlay window definition
			'overlayWindowOpacity'     : 0.5,
			'overlayWindowContent'     : '',
			'cssOverlayClass'          : ''
		};

		// initialize overlay window object
		this.window_overlay = null;
		
		// Array for selects to hide in IE 6.0
		this.newHiddenSelects = new Array();

		// hash map for additional type handler
		this.typehandler = new Hash ();

		// extend default options with specified options, if defined
		if ( ! Object.isUndefined ( opts ) )
		{
			Object.extend( this.options, opts );
		}
	},

	/**
	 * <p>Add the specified type handler.</p>
	 */
	addTypeHandler: function ( type, handler )
	{
		this.typehandler.set ( type, handler );
	},

	/**
	 * <p>Remove the specified type handler.</p>
	 */
	removeTypeHandler: function ( type )
	{
		this.typehandler.unset ( type );
	},

	/**
	 * <p>This method executes a request with the specified parameters and
	 * takes the action corresponding on the received response type.</p>
	 *
	 * @param requestType the type of the request (get, put, post, delete)
	 * @param requestUrl the url for the request
	 * @param requestData map of request data, may be empty
	 * @param opts map of options, may be empty
	 */
	doRequest: function ( requestType, requestUrl, requestData, opts )
	{
		// renew default options
		this.options.window = Sky.Window.instances.last ();

		// extend default options with specified options, if defined
		if ( ! Object.isUndefined ( opts ) )
		{
			Object.extend( this.options, opts );
		}
		
		// show overlay while processing
		this.showOverlay ();

		// check the requestType
		if ( requestType.toLowerCase () !== 'get' &&
			requestType.toLowerCase () !== "post" &&
			requestType.toLowerCase () !== 'put' &&
			requestType.toLowerCase () !== 'delete' )
		{
			logError ( 'Illegal request type specified: ' + requestType );
			alert ( 'Ungültiger Request-Typ spezifiziert: ' + requestType );
			return false;
		}

		// check the requestUrl
		if ( Object.isUndefined ( requestUrl ) ||
			requestUrl.empty () )
		{
			logError ( 'Illegal request url specified: ' + requestUrl );
			alert ( 'Ungültige Request-URL spezifiziert: ' + requestUrl );
			return false;
		}
		
		// create the post data for the request
		var postData = '';
		
		// if the request is of the type put or delete...
		if ( requestType.toLowerCase () === 'put' || requestType.toLowerCase () === 'delete' )
		{
			// ...we have to specify the method in the post data, too, because put and delete are simulated by a post
			postData += '_method=' + requestType;
		}

		// cappend the request data
		if ( ! Object.isUndefined ( requestData ) )
		{
			if ( ! postData.empty () )
			{
				postData += '&';
			}
			postData += Object.toQueryString ( requestData );
		}

		// execute the request
		var request = new Ajax.Request ( requestUrl,
		{
			method: requestType,
			postBody: postData,
			onSuccess: function ( transport )
			{
				// hide overlay
				this.hideOverlay ();
					
				if ( ! transport.responseText.isJSON () )
				{
					logError ( 'The response data received is no JSON!' );
					alert ( 'Unerwartete Daten im falschen Format erhalten!' );
					return false;
				}

				// the request was successful - extract the response data
				var data = transport.responseText.evalJSON ();

				// check if we have a callback function
				if ( ! Object.isUndefined ( this.options.onSuccess ) && this.options.onSuccess !== null )
				{
					this.options.onSuccess ( data );
				}
				if ( ! Object.isUndefined ( this.options.onSuccessOnce ) && this.options.onSuccessOnce !== null )
				{
					this.options.onSuccessOnce ( data );
					this.options.onSuccessOnce = null;
				}

				// check if we have to override the OK response
				if ( this.options.overrideDataOkWithTrue === true )
				{
					data.ok = true;
					this.options.overrideDataOkWithTrue = false;
				}
				if ( this.options.overrideDataOkWithFalse === true )
				{
					data.ok = true;
					this.options.overrideDataOkWithFalse = false;
				}

				// check if the response from the server is tagged as okay
				if ( data && data.ok && data.ok === true )
				{

					// check if we have to close the window
					if ( this.options.closeWindowOnSuccess )
					{
						Sky.Window.instances.last () .hide ();
						this.options.closeWindowOnSuccess = false;
					}

					// call user defined type handler, if any
					var myHandler = this.typehandler.get ( data.type );
					if ( myHandler )
					{
						return myHandler ( data );
					}

					// do something corresponding on the response type
					switch ( data.type )
					{
						case 'content':
							return this.doContent ( data );
						case 'dialog':
							return this.doDialog ( data );
						case 'form':
							return this.doForm ( data );
						case 'procresp':
							return this.doProcessResponse ( data) ;
					}

					logError ( 'Unknown response type received: ' + data.type );
					alert ( 'Unbekannter Response-Typ: ' + data.type );
					return false;
				}
				else if ( data && data.messages )
				{
					// show messages if any and we are allowed to
					if ( ! this.options.skipMessages && data.messages && data.messages.size () > 0 )
					{
						alert ( data.messages.join ( '\n' ) );
					}
					else
					{
						// return to default behaviour
						this.options.skipMessages = false;
					}
					return false;
				}
				else if ( data )
				{
					//  there is something wrong with the data
					logError ( 'Response has errors or is not complete: ' + data );
					alert ( 'Fehlerhafte oder unvollständige Daten empfangen: ' + data );
					return false;
				}
				else
				{
					// we don't got any usable data
					logError ( 'No response received!' );
					alert ( 'Keine Daten erhalten!' );
					return false;
				}
			}.bind ( this ),
			onFailure: function ( transport )
			{
				// hide overlay
				this.hideOverlay ();
					
				// check if we have a callback function
				if ( ! Object.isUndefined ( this.options.onFailure ) && this.options.onFailure !== null )
				{
					this.options.onFailure ( transport );
				}
				if ( ! Object.isUndefined ( this.options.onFailureOnce ) && this.options.onFailureOnce !== null )
				{
					this.options.onFailureOnce ( transport );
					this.options.onFailureOnce = null;
				}

				// the request resulted in a failure
				logError ( 'Failure in AJAX communication!' );
				alert ( "Entschuldigung, bei der Kommunikation mit dem " +
					"Server trat ein Fehler auf.\nBitte probieren Sie es in " +
					"wenigen Augenblicken erneut.\nVielen Dank für Ihr " +
					"Verständnis.", false );
				return false;
			}
		} );

		return true;
	},

	/**
	 * <p>This method displayes a dialog with the transmittet HTML code.</p>
	 */
	doDialog: function ( data )
	{
		if ( ! Object.isUndefined ( data.html ) && ! data.html.empty () )
		{

			if ( ! Object.isUndefined ( this.options.targetDialog ) && this.options.targetDialog !== null )
			{
				this.options.targetDialog.swapContent ( { contentCode : data.html } );
				this.options.targetDialog = null;
				return true;
			}

			var opts =
			{
					contentCode: data.html
			};
			Object.extend( opts, this.options.dialogOptions );
			var newDialog = new Sky.Window( opts );

			// call the dialog plugin, if any
			if ( this.options.dialogPlugin )
			{
				this.options.dialogPlugin ( data );
			}

			return true;
		}

		logError ( 'No HTML to display in a dialog!' );
		alert ( 'Keine Daten zum Anzeigen erhalten!' );
		return false;
	},

	/**
	 * <p>This method swaps the content of the basket window to display the new
	 * transmittet HTML code.</p>
	 */
	doForm: function ( data )
	{
		if ( ! Object.isUndefined ( this.options.showFormInDialog ) && this.options.showFormInDialog === true )
		{
			this.options.showFormInDialog = false;
			return this.doDialog ( data );
		}

		if ( data && data.html && data.html !== '' )
		{
			if ( ! $( this.options.window.windowId ) )
			{
				this.options.window = Sky.Window.instances.last ();
			}
			
			if ( Element.myIsExisting ( this.options.window ) )
			{
				this.options.window.swapContent ( { contentCode : data.html } );
			}

			// call the form plugin, if any
			if ( this.options.formPlugin )
			{
				this.options.formPlugin ( data );
			}

			return true;
		}

		logError ( 'No HTML to display in a form!' );
		alert ( 'Keine Daten zum Anzeigen erhalten!' );
		return false;
	},

	/**
	 * <p>This method preccesses a process response by showing the messages, if any,
	 * calling a process response plugin, if any, and swapping the content of the form, if any.</p>
	 */
	doProcessResponse: function ( data )
	{
		// show messages if any and we are allowed to
		if ( ! this.options.skipMessages && data.messages && data.messages.size () > 0 )
		{
			alert ( data.messages.join ( '\n' ) );
		}
		else
		{
			// return to default behaviour
			this.options.skipMessages = true;
		}

		// call the process response plugin
		if ( this.options.processResponsePlugin )
		{
			this.options.processResponsePlugin ( data );
		}

		// if there is HTML code, swap the form
		if ( data.html && ! data.html.empty () )
		{
			this.doForm ( data );
		}

		return true;
	},
	
	/**
	 * <p>This method replaces the content of the specified target with the transmittet HTML code.</p>
	 */
	doContent: function ( data )
	{
		// show messages if any
		if ( data.messages && data.messages.size () > 0 )
		{
			alert ( data.messages.join ( '\n' ) );
		}
		
		// call the content plugin
		if ( this.options.contentPlugin )
		{
			this.options.contentPlugin ( data );
		}

		if ( this.options.contentTarget === null && this.options.showContentInDialog === false )
		{
			this.options.showContentInDialog = true;
		}
		else if ( this.options.contentTarget !== null )
		{
			this.options.showContentInDialog = false;
		}
		
		// show the content in a new dialog if configured
		if ( data.html && ! data.html.empty () && this.options.showContentInDialog === true )
		{
			this.doDialog ( data );
		}
		// if there is HTML code, swap the content
		else if ( data.html && ! data.html.empty () && this.options.contentTarget !== null )
		{
			$( this.options.contentTarget ).update( data.html );
			this.options.contentTarget = null;
		}
		// this should never happen
		else if ( Object.isUndefined ( data.html ) || data.html.empty () )
		{
			logError ( 'No HTML to display in a dialog!' );
			alert ( 'Keine Daten zum Anzeigen erhalten!' );
		}
	},
	
	showOverlay: function ()
	{
		if ( this.window_overlay === null )
		{
			var style =
			{
				'position': 'absolute',
				'margin'  : 'auto',
				'top'     : '0',
				'left'    : '0',
				'width'   : document.viewport.getWidth () + 'px',
				'height': document.viewport.getHeight () + 'px',
				'backgroundColor': '#000000',
				'zIndex': '100000',
				'textAlign': 'center',
				'verticalAlign': 'middle'
			};

			this.window_overlay = new Element("div", { id: "JSONUtil_overlay" }).setOpacity ( this.options.overlayWindowOpacity ).setStyle ( style );
			this.window_overlay.update( this.options.overlayWindowContent );

			if ( this.options.cssOverlayClass !== null && this.options.cssOverlayClass !== '' )
			{
				this.window_overlay.addClassName( this.options.cssOverlayClass );
			}
			
			var dOffsets = document.viewport.getScrollOffsets();
			this.window_overlay.setStyle( {
					top: dOffsets.top + 'px',
					left: dOffsets.left + 'px'
				} );

			$(document.body).insert({'top': this.window_overlay});
			
			if( Prototype.Browser.IE && !navigator.appVersion.match(/\b7.0\b/) )
			{
				var allSelects = $$( "select" );
				for( var i = 0; i < allSelects.length; i++ )
				{
					var currSelect = allSelects[i];
					if( currSelect.getStyle( 'visibility' ) != 'hidden' )
					{
						this.newHiddenSelects[this.newHiddenSelects.length] = currSelect;
						currSelect.setStyle( { visibility: 'hidden' } );
					}
				}
			}
		}
	},
	
	hideOverlay: function ()
	{
		if ( this.window_overlay !== null )
		{
			this.window_overlay.remove ();
			this.window_overlay = null;
			
			if( Prototype.Browser.IE && !navigator.appVersion.match(/\b7.0\b/) )
			{
				for( var i = 0; i < this.newHiddenSelects.length; i++ )
				{
					var currSelect = this.newHiddenSelects[i];
					currSelect.setStyle( { visibility: 'visible' } );
				}
				this.newHiddenSelects = new Array();
			}
		}
	}
};

// For compatibility reasons
var My;
if( !My ) My = {};
My.JSONUtil = Sky.JSONUtil;