////////////////////////////////////////////////////////////////////////////////
//
// ADOBE SYSTEMS INCORPORATED
// Copyright 2007-2010 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE:  Adobe permits you to use, modify, and distribute this file 
// in accordance with the terms of the license agreement accompanying it.
//
////////////////////////////////////////////////////////////////////////////////
package flashx.textLayout.operations
{
	import flashx.textLayout.debug.assert;
	import flashx.textLayout.edit.ElementMark;
	import flashx.textLayout.edit.ElementRange;
	import flashx.textLayout.edit.IMemento;
	import flashx.textLayout.edit.MementoList;
	import flashx.textLayout.edit.ModelEdit;
	import flashx.textLayout.edit.ParaEdit;
	import flashx.textLayout.edit.SelectionState;
	import flashx.textLayout.elements.DivElement;
	import flashx.textLayout.elements.FlowElement;
	import flashx.textLayout.elements.FlowGroupElement;
	import flashx.textLayout.elements.FlowLeafElement;
	import flashx.textLayout.elements.ListItemElement;
	import flashx.textLayout.elements.ParagraphElement;
	import flashx.textLayout.elements.SubParagraphGroupElementBase;
	import flashx.textLayout.elements.TextFlow;
	import flashx.textLayout.events.DamageEvent;
	import flashx.textLayout.formats.ITextLayoutFormat;
	import flashx.textLayout.formats.TextLayoutFormat;
	import flashx.textLayout.tlf_internal;

	use namespace tlf_internal;

	/**
	 * The CreateDivOperation class encapsulates creating DivElement
	 *
	 * @playerversion Flash 10
	 * @playerversion AIR 1.5
	 * @langversion 3.0 
	 */
	public class CreateDivOperation extends FlowTextOperation
	{
		// describes the target
		private var _divParentMarker:ElementMark;
		private var _mementoList:MementoList;

		private var _format:ITextLayoutFormat;
		private var _divElement:DivElement;		// the element that gets created
		
		private var _postOpSelectionState:SelectionState;
		
		/** 
		 * Creates an CreateDivOperation object.
		 * 
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
	 	 * @langversion 3.0 
		 */
		public function CreateDivOperation(operationState:SelectionState, parent:FlowGroupElement = null, format:ITextLayoutFormat = null)
		{
			super(operationState);
			
			this.parent = parent;
			_format = format;
			_mementoList = new MementoList(operationState.textFlow);
		}
		
		/** 
		 * Specifies the parent element for the new DivElement
		 * 
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0 
		 */
		public function get parent():FlowGroupElement
		{
			return _divParentMarker ? _divParentMarker.findElement(originalSelectionState.textFlow) as FlowGroupElement : null;
		}
		public function set parent(value:FlowGroupElement):void
		{
			if (!value)
			{
				// descend to the lowest level non-paragraph element that contains both positions
				// effectively make the new div as close to the spans as possible
				value = textFlow;
				
				var begPos:int = this.absoluteStart;
				var endPos:int = this.absoluteEnd;
				for (;;)
				{
					var begChildIdx:int = value.findChildIndexAtPosition(begPos);
					var elem:FlowGroupElement = value.getChildAt(begChildIdx) as FlowGroupElement;
					if (elem is ParagraphElement)
						break;
					begPos -= elem.parentRelativeStart;
					endPos -= elem.parentRelativeStart;
					if (endPos >= elem.textLength)	// end pos is in the next element
						break;
					value = elem;
				}
			}
			else if (value is SubParagraphGroupElementBase)
				value = value.getParagraph().parent;
			
			_divParentMarker = new ElementMark(value,0);
		}
		
		/** TextLayoutFormat to be applied to the new DivElement. 
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0 
		 */	
		public function get format():ITextLayoutFormat
		{ return _format; }
		public function set format(value:ITextLayoutFormat):void
		{ _format = value; }
		
		/** The new DivElement. 
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0 
		 */	
		public function get newDivElement():DivElement
		{ return _divElement; }
		
		/** @private */
		public override function doOperation():Boolean
		{
			var target:FlowGroupElement = parent;
			
			// do nothing if target is a paragraphElement or below
			if (!target || (target is ParagraphElement) || (target is SubParagraphGroupElementBase))
				return false;
			
			// find the starting child that's going to be in the div and 
			var begChildIndex:int = 0;
			var begStart:int = absoluteStart - target.getAbsoluteStart();
			CONFIG::debug { assert(begStart >= 0 && begStart < target.textLength,"CreateDivOperation: bad target"); }
			
			var endChildIndex:int;
			var endStart:int = absoluteEnd - target.getAbsoluteStart();
			CONFIG::debug { assert(endStart >= 0 && endStart <= target.textLength,"CreateDivOperation: bad target"); }
			
			// scratch vars
			var child:FlowGroupElement;
			
			if (begStart > 0)
			{
				// figure out the starting child
				begChildIndex = target.findChildIndexAtPosition(begStart);
				child = target.getChildAt(begChildIndex) as FlowGroupElement;
				if (child.parentRelativeStart != begStart)
				{					
					_mementoList.push(ModelEdit.splitElement(textFlow,child,begStart-child.parentRelativeStart));
					
					if (child is ParagraphElement)
						endStart++;
					begChildIndex++;
				}
			}
			
			if (endStart >= 0)
			{
				if (endStart >= target.textLength - 1)
					endChildIndex = target.numChildren;
				else
				{
					// figure out the starting child
					endChildIndex = target.findChildIndexAtPosition(endStart);
					child = target.getChildAt(endChildIndex) as FlowGroupElement;
					if (child.parentRelativeStart != endStart)			
					{
						_mementoList.push(ModelEdit.splitElement(textFlow,child,endStart-child.parentRelativeStart));
						endChildIndex++;
					}
				}
			}
			else
				endChildIndex = begChildIndex+1;
			
			_divElement = new DivElement;
			_divElement.format = format;
						
			if (begChildIndex == target.numChildren)
			{
				// new div at the end of target
				child = target.getChildAt(target.numChildren-1) as FlowGroupElement;
				
				_mementoList.push(ModelEdit.splitElement(textFlow,child,child.textLength));
				_mementoList.push(ModelEdit.addElement(textFlow,_divElement,target,target.numChildren));


				_mementoList.push(ModelEdit.moveElement(textFlow,child,_divElement,_divElement.numChildren));
			}
			else
			{
				_mementoList.push(ModelEdit.addElement(textFlow,_divElement,target,endChildIndex));


				if (begChildIndex == endChildIndex)
				{
					_mementoList.push(ModelEdit.addElement(textFlow,new ParagraphElement,_divElement,0));					
				}
				else
				{
					while (begChildIndex < endChildIndex)
					{
						child = target.getChildAt(begChildIndex) as FlowGroupElement;
						if (child is ListItemElement)
						{
							// move all the children up and delete the ListItemElement
							while (child.numChildren)
								_mementoList.push(ModelEdit.moveElement(textFlow,child.getChildAt(0),_divElement,_divElement.numChildren));
							_mementoList.push(ModelEdit.removeElements(textFlow,target,begChildIndex,1));
						}
						else
						{
							_mementoList.push(ModelEdit.moveElement(textFlow,child,_divElement,_divElement.numChildren));
							child.normalizeRange(0,child.textLength);
						}
						endChildIndex--;
					}
				}
				// normalize does this
				if ((target is ListItemElement) && (target as ListItemElement).normalizeNeedsInitialParagraph())
					_mementoList.push(ModelEdit.addElement(textFlow,new ParagraphElement,target,0));
			}
			
			if (originalSelectionState.selectionManagerOperationState && textFlow.interactionManager)
			{
				textFlow.normalize();
				_postOpSelectionState = new SelectionState(textFlow,_divElement.getAbsoluteStart(),_divElement.getAbsoluteStart()+_divElement.textLength-1);
				textFlow.interactionManager.setSelectionState(_postOpSelectionState);
			}
			
			return true;
		}

		/** @private */
		public override function undo():SelectionState
		{
			_mementoList.undo();
			return originalSelectionState; 
		}
		
		/** @private */
		public override function redo():SelectionState
		{
			_mementoList.redo();
			return _postOpSelectionState; 
			
		}
	}
}
