/*
 * FCKeditor - The text editor for Internet - http://www.fckeditor.net
 * Copyright (C) 2003-2007 Frederico Caldeira Knabben
 *
 * == BEGIN LICENSE ==
 *
 * Licensed under the terms of any of the following licenses at your
 * choice:
 *
 *  - GNU General Public License Version 2 or later (the "GPL")
 *    http://www.gnu.org/licenses/gpl.html
 *
 *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
 *    http://www.gnu.org/licenses/lgpl.html
 *
 *  - Mozilla Public License Version 1.1 or later (the "MPL")
 *    http://www.mozilla.org/MPL/MPL-1.1.html
 *
 * == END LICENSE ==
 *
 * Implementation for the "Insert/Remove Ordered/Unordered List" commands.
 */

var FCKListCommand = function( tagName )
{
	this.TagName = tagName;
}

FCKListCommand.prototype = 
{
	GetState : function()
	{
		if ( FCK.EditMode != FCK_EDITMODE_WYSIWYG || ! FCK.EditorWindow )
			return FCK_TRISTATE_DISABLED ;
		var terminals = FCKSelection.GetSelectionTerminals() ;
		var listBlock = this._GetNearestListParent( terminals.startNode, terminals.endNode ) ;
		if ( listBlock )
			return FCK_TRISTATE_ON ;
		else
			return FCK_TRISTATE_OFF ;
	},

	Execute : function()
	{
		if ( this.GetState() == FCK_TRISTATE_OFF )
		{
			// Get the selected items ranges.
			var selectionRange = FCKSelection.GetSelectionRange() ;
			var itemRanges = selectionRange.GetSelectedParagraphs() ;

			// Cut the contents of the items into document fragments.
			// For each item contents, find the shallowest tree depth and work from that level.
			var markerObj = {} ;
			var itemDocFragments = [] ;
			for ( var i = 0 ; i < itemRanges.length ; i++ ) 
			{
				// find out the shallowest node by dynamic programming.
				var currentItem = itemRanges[i] ;
				var currentNode = currentItem._Range.startContainer ;
				if ( currentNode.nodeType == 1 && currentNode.lastChild )
					currentNode = currentNode.childNodes[ currentItem._Range.startOffset ] ;
				var startNode = currentNode ;
				var endNode = currentItem._Range.endContainer ;
				if ( endNode.nodeType == 1 && endNode.lastChild )
					endNode = endNode.childNodes[ currentItem._Range.endOffset ] ;
				endNode = FCKDomTools.GetNextSourceNode( endNode ) ;
				var shallowestNode = startNode ;
				while ( currentNode != endNode )
				{
					var unmarkedNodes = [] ;
					// for each node, loop through its parents until either document.body or a marked node is found.
					// also, keep record of the unmarked nodes encountered.
					var ancestor = currentNode ;
					while ( ancestor != ancestor.ownerDocument.body
					     && ancestor._FCKDepthCounter == undefined )
					{
						unmarkedNodes.unshift( ancestor ) ;
						ancestor = ancestor.parentNode ;
					}
					var topDepth = ( ancestor == ancestor.ownerDocument.body ? 0 : ancestor._FCKDepthCounter ) ;
					for ( var j = 0 ; j < unmarkedNodes.length ; j++ )
					{
						FCKDomTools.SetNodeMarker( markerObj, unmarkedNodes[j], '_FCKDepthCounter', 
							       topDepth + 1 + j ) ;
						markedNodes.push( unmarkedNodes[j] ) ;
					}
					if ( ancestor._FCKDepthCounter < shallowestNode._FCKDepthCounter )
						shallowestNode = ancestor ;
					currentNode = FCKDomTools.GetNextSourceNode( currentNode ) ;
				}

				// Create the document fragment for the item.
				var currentFragment = selectionRange.Window.document.createDocumentFragment() ;
				itemDocFragments.push( currentFragment ) ;

				// For each DOM node within this range:
				// If the node's depth is the same as the shallowest node, then simply add the node to the document fragment.
				// Else, recreate or reuse all the node's parents up to the shallowest node's level
				// 	- If the parents can be reused, just add the node or the appropriate subtree to the parent.
				//	- If no parent can be reused, reproduce the whole subtree and keep a record for all nodes of the subtree
				// 		so that those nodes can be reused as parent nodes later.
				currentNode = startNode ;
				while ( currentNode != endNode )
				{
					if ( currentNode._FCKDepthCounter > shallowest._FCKDepthCounter )
					{
						var ancestor = currentNode.parentNode ;
						var lastAncestor = null ;
						while ( ancestor._FCKDepthCounter >= shallowestNode._FCKDepthCounter && 
								ancestor._FCKImageNode == undefined )
						{
							var imageNode = ancestor.cloneNode( false ) ;
							FCKDomTools.SetNodeMarker( markerObj, ancestor, '_FCKImageNode', imageNode ) ;
							if ( lastAncestor && lastAncestor._FCKImageNode )
								imageNode.appendChild( lastAncestor._FCKImageNode ) ;
							lastAncestor = ancestor ;
							ancestor = ancestor.parentNode ;
						}
						var parentNode = currentNode.parentNode ;
						FCKDomTools.SetNodeMarker( markerObj, currentNode, '_FCKPreviousParent', parentNode ) ;
						FCKDomTools.SetNodeMarker( markerObj, currentNode, '_FCKPreviousNextSibling', 
								currentNode.nextSibling ) ;
						parentNode.removeChild( currentNode ) ;
						parentNode._FCKImageNode.appendChild( currentNode ) ;
						currentFragment.appendChild( ancestor._FCKImageNode ) ;
					}
					else
					{
						FCKDomTools.SetNodeMarker( markerObj, currentNode, '_FCKPreviousParent', currentNode ) ;
						FCKDomTools.SetNodeMarker( markerObj, currentNode, '_FCKPreviousNextSibling',
							       currentNode.nextSibling ) ;
						currentNode.parentNode.removeChild( currentNode ) ;
						currentFragment.appendChild( currentNode ) ;
					}
					currentNode = FCKDomTools.GetNextSourceNode( currentNode ) ;
				}
			}

			// Now we should have a list of document fragments corresponding to individual list items.
			// We should not create the list immediately, however. Due to the following reasons:
			// 1. Multiple lists might be needed if parts of the list are in tables.
			// 	This case is not mentioned in Midas list rules (http://www.mozilla.org/editor/list-rules.html)
			//	But the behavior can be tested in the built-in insert list commands of both IE and Midas.
			//	Let's model it this way: if any of the tree nodes between this node (exclusive) and the parent node
			// 	at depth=nearestCommonParent (inclusive), is a PathBlockLimitElement, then a new list is needed.
			// 2. If any of the selected list items are already list items, take care not to change the indentation
			//	or invalidate the non-selected list items of the same list.
			//	(Midas list rules G, H, I, J, and note that Midas's case I is somewhat inconsistent!!)
			// 3. After we've created the lists, where should we insert them to?
			// 	Look for the highest level item in the list, and insert the list right before the highest level
			//	item's original nextSibling.
			var listBlocks = [] ;
			for ( var i = 0 ; i < itemDocFragments.length ; i++ )
			{
				var previousParent = itemDocFragments.firstChild._FCKPreviousParent ;
				var ancestor = previousParent ;
				//while ( ancestor._FCKDepthCounter <= )
			}


			// Clean up all the markings
			FCKDomTools.ClearNodeMarkers( markerObj ) ;
		}
		else if ( this.GetState() == FCK_TRISTATE_ON )
		{
		}
		/*
		if ( FCKBrowserInfo.IsIE && this.GetState() == FCK_TRISTATE_OFF )
		{
			// IE does not split selected <br> tags when it is making lists. (See #428)
			// So, pre-split the blocks for IE.
			var range = FCKSelection.GetSelectionRange() ;
			var items = range.GetSelectedParagraphs() ;
			for ( var i = 0 ; i < items.length ; i++ )
			{
				try
				{
					var itemContents = items[i].ExtractContents() ;
					var dummyBlock = FCK.EditorDocument.createElement( 'div' ) ;
					items[i].InsertNode( dummyBlock ) ;
					itemContents.AppendTo( dummyBlock ) ;
				}
				catch ( e ) {}
			}
			range.Select() ;
		}
		*/
		//FCK.ExecuteNamedCommand( this.Name ) ;
	},

	_GetNearestListParent : function( node1, node2 )
	{
		var commonParents = FCKDomTools.GetCommonParents( node1, node2 ) ;
		for ( var i = commonParents.length - 1 ; i >= 0 ; i-- )
		{
			var tagName = String(commonParents[i].tagName) ;
			if ( tagName.IEquals( [ 'ul', 'ol' ] ) )
			{
				if ( tagName.IEquals( this.TagName ) )
					return commonParents[i] ;
				else
					return null ;
			}
		}
		return null ;
	}
};
