/**
* @version $ Id; cjtserver.js 21-03-2012 03:22:10 Ahmed Said $
*
* Block model class.
*/

/**
* Put CJTBlock class at global scope.
*/
var CJTBlockBase;

/**
* JQuery wrapper for the CJTBlock class.
*/
( function ( $ ) {

	/**
	* Flags values.
	*
	* @var object
	*/
	var flags = {
		location: [ 'header', 'footer' ],
		state: [ 'active', 'inactive' ]
	}

	/**
	* Provide simple access to single block properties.
	*
	* @author Ahmed Said
	* @version 6
	* @param DOMElement Block element.
	*/
	CJTBlockBase = function () {

		/**
		*
		*
		*
		*/
		this.aceEditor = null

		/**
		*
		*/
		this.blockPlugin = null

		/**
		* Block JQuery object.
		*
		* @var JQuery object.
		*/
		this.box = null

		/**
		* Block id.
		*
		* @var integer
		*/
		this.id = 0

		/**
		* Blocks property that allowed to be read or write.
		*
		* flags:
		*	- r : read.
		*	- w : write.
		*	- c : cookie.
		*	- S : Save when block is saved.
		*
		* @var object
		*/
		this.properties = {}

		/**
		*
		*/
		this.addProperties = function ( properties ) {
			// Prepare properties, add them to model list.
			$.each( properties, $.proxy(
				function ( name, propertyDefinition ) {
					// Add flag checker method to property object.
					propertyDefinition.flag = function ( flag ) {
						var flagIndex = this.flags.indexOf( flag )
						return ( flagIndex == -1 ) ? false : true
					}
					// Add name to the property object.
					propertyDefinition.name = name
					// Place block id in the selector.
					if ( propertyDefinition.selector != undefined ) {
						propertyDefinition.selector = propertyDefinition.selector.replace( '{blockId}', this.id )
					}
					// If OM is supported bind it.
					if ( propertyDefinition.om !== undefined ) {
						propertyDefinition.om.bind( this.blockPlugin, propertyDefinition )
					}
					// Cache property object.
					this.properties[ name ] = propertyDefinition
				}, this )
			)
		}

		/**
		* put your comment there...
		*
		* @param element
		*/
		this.CJTBlockBase = function ( blockPlugin, element, properties ) {
			// Initialize.
			this.blockPlugin = blockPlugin
			this.box = $( element )
			this.id = parseInt( this.box.find( 'input:hidden[name="blocks[]"]' ).val() )
			this.properties = {}
			// Define base properties.
			properties.name = { om: new CJTBlockPropertyHTMLNodeOM(), flags: 'rws', selector: 'input:text[name="cjtoolbox[{blockId}][name]"]' }
			properties.location = { om: new CJTBlockPropertyHTMLNodeOM(), flags: 'rw', selector: 'input:hidden[name="cjtoolbox[{blockId}][location]"]' }
			properties.state = { om: new CJTBlockPropertyHTMLNodeOM(), flags: 'rw', selector: 'input:hidden[name="cjtoolbox[{blockId}][state]"]' }
			// Initialize ALL (BASE, DERIVDED) properties.
			this.addProperties( properties )
		}

		/**
		* Get block property cvalue.
		*
		* @param string Property name.
		* @return mixed
		*/
		this.get = function ( name, _default ) {
			// Initialize.
			var value = null
			var property = this.property( name )
			// Check member variables first.
			if ( this[ name ] != undefined ) {
				value = this[ name ]
			}
			// There are two types of properties, cookie and element.
			else if ( property.flag( 'c' ) ) {
				// Get cookie value.
				var cookieName = name + '-' + this.id
				value = $.cookies.get( cookieName )
			}
			else { // Not cookies, it may be saved through JS object or inside HTML elements.
				// Custom implemetation.
				switch ( name ) {
					case 'code': // Code is through ACE-Editor Object.
						value = this.aceEditor.getSession().getValue()
						break
					default: // Get property value from html elements.
						value = property.om.get()
						break
				}
			}
			// If empty and _default is provided, return _default.
			if ( !value && ( _default != undefined ) ) {
				value = _default
			}
			// Returns
			return value
		}

		/**
		*
		*/
		this.getDIFields = function () {
			// Initialize.
			var diFields = null
			// Query DIFields selectors nodes.
			diFields = this.box.find( this.getDIProperties().selector.join( ',' ) )
			// Returns diFields
			return diFields
		}

		/**
		*
		*/
		this.getDIProperties = function () {
			// Initialize.
			var diProperties = { selector: [], list: {} }
			// Collect DIFields from the properties list.
			$.each( this.properties, $.proxy(
				function ( name, property ) {
					// All fields with 's' flag is a DIField.
					if ( property.flag( 's' ) ) {
						// Add property to the list.
						diProperties.list[ name ] = property
						// Add the selector as well.
						diProperties.selector.push( property.selector )
					}
				}, this )
			)
			// Return DIProperties.
			return diProperties
		}

		/**
		* Get operation queue object for the block.
		*
		* The method call CJTServer.getQueue method with the name parameter
		* generated from the operation name and the block id. The result is each block has
		* an object for each operation it may process.
		*
		* @param string Operation name.
		* @return CJTBlocksServerQueue Operation queue object.
		*/
		this.getOperationQueue = function ( operation ) {
			var name = ( operation + '-operation-' + this.get( 'id' ).toString() )
			var queue = CJTBlocksPage.server.getQueue( 'Blocks', name, 'blocksPage', 'save_blocks' )
			return queue
		}

		/**
		*
		*/
		this.loadBase = function ( properties ) {
			// Enable ACE Editor.
			this.aceEditor = ace.edit( 'editor-' + this.id )
			// Add ACE Editor Propety definition.
			properties.code = { om: new CJTBlockPropertyACEEditor(), flags: 'rws', selector: 'div#editor-{blockId}' }
			properties.editorLang = { flags: 'rwc' }
			properties.editorTheme = { flags: 'rwc' }
			properties.aceEditorMenuSettings = { flags: 'rwc' }
			// Initialize ALL (BASE, DERIVDED) properties.
			this.addProperties( properties )
			// Create bridge through "code" field
			// so that div element can set/get aceEditor real object.
			var codeDiv = $( this.property( 'code' ).selector ).get( 0 )
			codeDiv.getValue = function () {
				return this.aceEditor.getSession().getValue()
			}
			// Define aceEditor extension method for setting Editor Text with the possibility
			// of UNDO.
			this.aceEditor.setValuePossibleUndo = function ( value ) {
				// Directly clear using setValue('') prevent 'undo' action!
				// Select all text.
				this.selectAll()
				// Replace content with empty string!
				this.getSession().replace( this.getSelectionRange(), value )
				this.focus()
			}
		}

		/**
		* @internal
		*
		* Prepare property selector object.
		*
		* @param string Property name.
		* @return object Property selector object.
		*/
		this.property = function ( name ) {
			// Get property object from the cache.
			var property = this.properties[ name ]
			// Returns property cached object used to
			// get and set property values.
			return property
		}

		/**
		* Set block property value.
		*
		*
		* @param {string} Property name.
		* @param {string} Property value.
		* @return CJTServer.getDeferredObject().promise()
		*/
		this.set = function ( name, newValue ) {
			// In case (value != newValue) is FALSE
			// return Dummy Promise object.
			var promise = CJTServer.getDeferredObject().promise()
			var property = this.property( name )
			// There are two types of properties, cookie and element.
			if ( property.flag( 'c' ) ) {
				var expires = new Date( ( new Date() ).getTime() + ( ( 30 * 24 * 60 * 60 ) * 1000 ) ) // Live for 1 month.
				// Set cookie value.
				var cookieName = name + '-' + this.id
				$.cookies.set( cookieName, newValue, { expiresAt: expires } )
			}
			else {
				// Get element value.
				var element = this.box.find( property.selector )
				var value = element.val()
				// Update only if not same.
				if ( ( newValue != undefined ) && ( value != newValue ) ) {
					// Update on the server.
					var data = {
						id: this.get( 'id' ),
						property: name,
						value: newValue
					}
					// Save property at the server.
					queue = this.getOperationQueue( name )
					promise = queue.add( data )
						.success(
							function ( rProperty ) {
								// Change local value to new value.
								element.val( rProperty.value )
							}
						)
				}
			}
			return promise
		}

		/**
		* Send block Ajax queues to the server.
		*
		* This method calls CJTServerQueue.send method.
		* The result is sending all updated properties queue to the server.
		*
		* Please note: CJTServerQueue.send method will do nothing if the queue is locked.
		*
		* @return jqxhr
		*/
		this.sync = function ( name, data ) {
			if ( !data ) {
				data = {}
			}

			var ajaxPromise = this.getOperationQueue( name ).send( 'post', data )
			return ajaxPromise
		}

		/**
		* Switch block flag property.
		*
		* Because flag can represent only two states. This method
		* is to simplify changing flag value. It automatically detect
		* current flag value and the new value OR simply the value can be
		* forced to a specific state through the newValue parameter.
		*
		* @param string Flag name.
		* @param [mixed] Force flag value to newValue.
		* @return CJTServer.getDeferredObject().promise()
		*/
		this.switchFlag = function ( flag, newValue ) {
			var promise
			// Automatically switch the value if not specified.
			if ( newValue == undefined ) {
				var value = this.get( flag )
				var possibleValues = flags[ flag ]
				var currentIndex = possibleValues.indexOf( value )
				//// Truth Table ////
				// currentIndex(0) XOR 1 = 1
				// currentIndex(1) XOR 1 = 0
				var newIndex = currentIndex ^ 1
				var newValue = possibleValues[ newIndex ]
			}
			promise = this.set( flag, newValue )
			return promise
		}

		/**
		* Save Direct Interact Fields.
		*
		* @return void
		*/
		this.queueDIFields = function () {
			// Initialize.
			var dIFields = this.getDIProperties().list
			var queue = this.getOperationQueue( 'saveDIFields' )
			var blockId = this.get( 'id' )
			// For every field create queue request as a single property for the block.
			$.each( dIFields, $.proxy(
				function ( name ) {
					var field = {
						id: blockId,
						property: name,
						value: this.get( name )
					}
					// Add to queue list.
					queue.add( field )
				}, this )
			)
		}

	} // End class.

} )( jQuery )