/**
 * Dialog Module for wikiEditor
 */
( function ( $, mw ) {

$.wikiEditor.modules.dialogs = {

	/**
	 * Compatability map
	 */
	browsers: {
		// Left-to-right languages
		ltr: {
			msie: [['>=', 7]],
			// jQuery UI appears to be broken in FF 2.0 - 2.0.0.4
			firefox: [
				['>=', 2], ['!=', '2.0'], ['!=', '2.0.0.1'], ['!=', '2.0.0.2'], ['!=', '2.0.0.3'], ['!=', '2.0.0.4']
			],
			opera: [['>=', 9.6]],
			safari: [['>=', 3]],
			chrome: [['>=', 3]]
		},
		// Right-to-left languages
		rtl: {
			msie: [['>=', 7]],
			// jQuery UI appears to be broken in FF 2.0 - 2.0.0.4
			firefox: [
				['>=', 2], ['!=', '2.0'], ['!=', '2.0.0.1'], ['!=', '2.0.0.2'], ['!=', '2.0.0.3'], ['!=', '2.0.0.4']
			],
			opera: [['>=', 9.6]],
			safari: [['>=', 3]],
			chrome: [['>=', 3]]
		}
	},

	/**
	 * API accessible functions
	 */
	api: {
		addDialog: function ( context, data ) {
			$.wikiEditor.modules.dialogs.fn.create( context, data );
		},
		openDialog: function ( context, module ) {
			if ( module in $.wikiEditor.modules.dialogs.modules ) {
				var mod = $.wikiEditor.modules.dialogs.modules[module],
					$dialog = $( '#' + mod.id );
				if ( $dialog.length === 0 ) {
					$.wikiEditor.modules.dialogs.fn.reallyCreate( context, mod, module );
					$dialog = $( '#' + mod.id );
				}

				// Workaround for bug in jQuery UI: close button in top right retains focus
				$dialog.closest( '.ui-dialog' )
					.find( '.ui-dialog-titlebar-close' )
					.removeClass( 'ui-state-focus' );

				$dialog.dialog( 'open' );
			}
		},
		closeDialog: function ( context, module ) {
			if ( module in $.wikiEditor.modules.dialogs.modules ) {
				$( '#' + $.wikiEditor.modules.dialogs.modules[module].id ).dialog( 'close' );
			}
		}
	},

	/**
	 * Internally used functions
	 */
	fn: {
		/**
		 * Creates a dialog module within a wikiEditor
		 *
		 * @param {Object} context Context object of editor to create module in
		 * @param {Object} config Configuration object to create module from
		 */
		create: function ( context, config ) {
			var mod, module, filtered, i, $existingDialog;

			// Defer building of modules, unless they require immediate creation
			for ( mod in config ) {
				module = config[mod];
				// Only create the dialog if it's supported, isn't filtered and doesn't exist yet
				filtered = false;
				if ( typeof module.filters !== 'undefined' ) {
					for ( i = 0; i < module.filters.length; i++ ) {
						if ( $( module.filters[i] ).length === 0 ) {
							filtered = true;
							break;
						}
					}
				}
				// If the dialog already exists, but for another textarea, simply remove it
				$existingDialog = $( '#' + module.id );
				if ( $existingDialog.length > 0 && $existingDialog.data( 'context' ).$textarea !== context.$textarea ) {
					$existingDialog.remove();
				}
				// Re-select from the DOM, we might have removed the dialog just now
				$existingDialog = $( '#' + module.id );
				if ( !filtered && $.wikiEditor.isSupported( module ) && $existingDialog.length === 0 ) {
					$.wikiEditor.modules.dialogs.modules[mod] = module;
					context.$textarea.trigger( 'wikiEditor-dialogs-setup-' + mod );
					// If this dialog requires immediate creation, create it now
					if ( typeof module.immediateCreate !== 'undefined' && module.immediateCreate ) {
						$.wikiEditor.modules.dialogs.fn.reallyCreate( context, module, mod );
					}
				}
			}
		},

		/**
		 * Build the actual dialog. This done on-demand rather than in create()
		 * @param {Object} context Context object of editor dialog belongs to
		 * @param {Object} module Dialog module object
		 * @param {String} name Dialog name (key in $.wikiEditor.modules.dialogs.modules)
		 */
		reallyCreate: function ( context, module, name ) {
			var msg, dialogDiv,
				configuration = module.dialog;
			// Add some stuff to configuration
			configuration.bgiframe = true;
			configuration.autoOpen = false;
			// By default our dialogs are modal, unless explicitely defined in their specific configuration.
			if ( typeof configuration.modal === 'undefined' ) {
				configuration.modal = true;
			}
			configuration.title = $.wikiEditor.autoMsg( module, 'title' );
			// Transform messages in keys
			// Stupid JS won't let us do stuff like
			// foo = { mw.msg( 'bar' ): baz }
			configuration.newButtons = {};
			for ( msg in configuration.buttons ) {
				configuration.newButtons[mw.msg( msg )] = configuration.buttons[msg];
			}
			configuration.buttons = configuration.newButtons;
			// Create the dialog <div>
			dialogDiv = $( '<div>' )
				.attr( 'id', module.id )
				.html( module.html )
				.data( 'context', context )
				.appendTo( $( 'body' ) )
				.each( module.init )
				.dialog( configuration );
			// Set tabindexes on buttons added by .dialog()
			$.wikiEditor.modules.dialogs.fn.setTabindexes( dialogDiv.closest( '.ui-dialog' )
				.find( 'button' ).not( '[tabindex]' ) );
			if ( !( 'resizeme' in module ) || module.resizeme ) {
				dialogDiv
					.bind( 'dialogopen', $.wikiEditor.modules.dialogs.fn.resize )
					.find( '.ui-tabs' ).bind( 'tabsshow', function () {
						$( this ).closest( '.ui-dialog-content' ).each(
							$.wikiEditor.modules.dialogs.fn.resize );
					} );
			}
			dialogDiv.bind( 'dialogclose', function () {
				context.fn.restoreSelection();
			} );

			// Let the outside world know we set up this dialog
			context.$textarea.trigger( 'wikiEditor-dialogs-loaded-' + name );
		},

		/**
		 * Resize a dialog so its contents fit
		 *
		 * Usage: dialog.each( resize ); or dialog.bind( 'blah', resize );
		 * NOTE: This function assumes $.ui.dialog has already been loaded
		 */
		resize: function () {
			var oldWS, thisWidth, wrapperWidth,
				wrapper = $( this ).closest( '.ui-dialog' ),
				oldWidth = wrapper.width(),
				// Make sure elements don't wrapped so we get an accurate idea of whether they really fit. Also temporarily show
				// hidden elements. Work around jQuery bug where <div style="display: inline;"/> inside a dialog is both
				// :visible and :hidden
				oldHidden = $( this ).find( '*' ).not( ':visible' );

			// Save the style attributes of the hidden elements to restore them later. Calling hide() after show() messes up
			// for elements hidden with a class
			oldHidden.each( function () {
				$( this ).data( 'oldstyle', $( this ).attr( 'style' ) );
			} );
			oldHidden.show();
			oldWS = $( this ).css( 'white-space' );
			$( this ).css( 'white-space', 'nowrap' );
			if ( wrapper.width() <= $( this ).get( 0 ).scrollWidth ) {
				thisWidth = $( this ).data( 'thisWidth' ) ? $( this ).data( 'thisWidth' ) : 0;
				thisWidth = Math.max( $( this ).get( 0 ).width, thisWidth );
				$( this ).width( thisWidth );
				$( this ).data( 'thisWidth', thisWidth );
				wrapperWidth = $( this ).data( 'wrapperWidth' ) ? $( this ).data( 'wrapperWidth' ) : 0;
				wrapperWidth = Math.max( wrapper.get( 0 ).scrollWidth, wrapperWidth );
				wrapper.width( wrapperWidth );
				$( this ).data( 'wrapperWidth', wrapperWidth );
				$( this ).dialog( { 'width': wrapper.width() } );
				wrapper.css( 'left', parseInt( wrapper.css( 'left' ), 10 ) - ( wrapper.width() - oldWidth ) / 2 );
			}
			$( this ).css( 'white-space', oldWS );
			oldHidden.each( function () {
				$( this ).attr( 'style', $( this ).data( 'oldstyle' ) );
			} );
		},
		/**
		 * Set the right tabindexes on elements in a dialog
		 * @param $elements Elements to set tabindexes on. If they already have tabindexes, this function can behave a bit weird
		 */
		setTabindexes: function ( $elements ) {
			// Get the highest tab index
			var tabIndex = $( document ).lastTabIndex() + 1;
			$elements.each( function () {
				$( this ).attr( 'tabindex', tabIndex++ );
			} );
		}
	},

	// This stuff is just hanging here, perhaps we could come up with a better home for this stuff
	modules: {},

	quickDialog: function ( body, settings ) {
		$( '<div>' )
			.text( body )
			.appendTo( $( 'body' ) )
			.dialog( $.extend( {
				bgiframe: true,
				modal: true
			}, settings ) )
			.dialog( 'open' );
	}

};

}( jQuery, mediaWiki ) );
