////////////////////////////////////////////////////////////////////////////////
//
// 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.compose
{
	import flash.events.Event;
	import flash.geom.Rectangle;
	import flash.text.engine.TextBlock;
	import flash.text.engine.TextLine;
	import flash.text.engine.TextLineValidity;
	
	import flashx.textLayout.container.ContainerController;
	import flashx.textLayout.debug.Debugging;
	import flashx.textLayout.debug.assert;
	import flashx.textLayout.elements.ContainerFormattedElement;
	import flashx.textLayout.elements.FlowElement;
	import flashx.textLayout.elements.FlowGroupElement;
	import flashx.textLayout.elements.FlowLeafElement;
	import flashx.textLayout.elements.OverflowPolicy;
	import flashx.textLayout.elements.ParagraphElement;
	import flashx.textLayout.elements.TextFlow;
	import flashx.textLayout.formats.BaselineOffset;
	import flashx.textLayout.formats.BlockProgression;
	import flashx.textLayout.formats.Direction;
	import flashx.textLayout.formats.ITextLayoutFormat;
	import flashx.textLayout.formats.TextAlign;
	import flashx.textLayout.formats.VerticalAlign;
	import flashx.textLayout.tlf_internal;
	
	use namespace tlf_internal;

	[ExcludeClass]
	/** Keeps track of internal state during composition. 
	 * 
	 * This is the simpler version, used when there are no floats, no wraps, no columns.
	 * @private
	 */
	public class SimpleCompose extends BaseCompose
	{
		// reusable scratch TextFlowLine
		protected var workingLine:TextFlowLine = new TextFlowLine(null, null);
		
		// resulting TextLines
		public var _lines:Array;
		
		// scratch aligns for VJ
		private var _vjLines:Array;
		
		// for figuring out when to do VJ
		private var vjBeginLineIndex:int = 0;
		private var vjDisableThisParcel:Boolean = false;
		private var vjType:String;
		
		// accumulator for absolute start computation to support truncation 
		private var _totalLength:Number;
		
		/** Constructor. */
		public function  SimpleCompose()
		{	
			super();
			_lines = new Array();
			_vjLines = new Array();
		}
		
		/** @private */
		protected override function createParcelList():ParcelList
		{
			return ParcelList.getParcelList();
		}
		/** @private */
		protected override function releaseParcelList(list:ParcelList):void
		{
			ParcelList.releaseParcelList(list);
		}	

		protected override function  initializeForComposer(composer:IFlowComposer, composeToPosition:int, controllerStartIndex:int, controllerEndIndex:int):void
		{
			// Always compose from the start of the flow
			_startController = composer.getControllerAt(0);
			_startComposePosition = 0;
			super.initializeForComposer(composer, composeToPosition, 0, controllerEndIndex);
			
			// vj support
			_vjLines.splice(0);
			vjBeginLineIndex = 0;
			vjDisableThisParcel = false;
			vjType =_startController.computedFormat.verticalAlign;
		}

		/** @private */
		public override function composeTextFlow(textFlow:TextFlow, composeToPosition:int, controllerEndIndex:int):int
		{
			_flowComposer = textFlow.flowComposer as StandardFlowComposer;
			_curLine = workingLine;
			CONFIG::debug { assert (_curLine != null, "_curLine is null"); }
			// empty out lines array
			_lines.splice(0);
			
			// accumulator initialization
			_totalLength = 0;
			
			return super.composeTextFlow(textFlow, composeToPosition, controllerEndIndex);
		}
		
 		override protected function doVerticalAlignment(canVerticalAlign:Boolean,nextParcel:Parcel):void
 		{
			var vjParcel:Parcel = parcelList.currentParcel;

			if (canVerticalAlign && vjType != VerticalAlign.TOP && vjBeginLineIndex != _lines.length &&  !vjDisableThisParcel)
			{
				var controller:ContainerController = _curParcel.controller;
				var beginFloatIndex:int = 0;
				var endFloatIndex:int = 0;
				if (controller.numFloats > 0)
				{
					beginFloatIndex = controller.findFloatIndexAtOrAfter(_curParcelStart);
					endFloatIndex = controller.findFloatIndexAfter(_curElementStart + _curElementOffset);
				}
				applyVerticalAlignmentToColumn(vjParcel.controller,vjType,_vjLines,0,_vjLines.length, beginFloatIndex, endFloatIndex);
			}

			_vjLines.splice(0);
			vjBeginLineIndex = _lines.length;
			vjDisableThisParcel = false;
			if (nextParcel)
				vjType = nextParcel.controller.computedFormat.verticalAlign;
 		}
 		
		// all lines are visible in the factory
		override protected function isLineVisible(textLine:TextLine):Boolean
		{ return textLine != null; }
		
		/** Called when we are finished composing a line. Handler for derived classes to override default behavior.  */
		override protected function endLine(textLine:TextLine):void
		{
			super.endLine(textLine);
			
			_curLine.createShape(_blockProgression, textLine);
			
			if (textFlow.backgroundManager)
				textFlow.backgroundManager.finalizeLine(_curLine);
				
			textLine.userData = _totalLength; 		// store absolute start position in the userData field
			_totalLength += textLine.rawTextLength; // update length accumulator
			_lines.push(textLine);
			if (vjType != VerticalAlign.TOP)
				_vjLines.push(new VJHelper(textLine,_curLine.height));
				
			commitLastLineState(_curLine);	
		}

		public function get textFlow():TextFlow
		{ return _textFlow; }
		
		private var _resetLineHandler:Function;
		
		/** Callback to the client to reset a line when its being rebroken */
		public function get resetLineHandler():Function
		{ return _resetLineHandler; }
		public function set resetLineHandler(val:Function):void
		{ _resetLineHandler = val; }
		
		/** @private */
		protected override function resetLine(textLine:TextLine):void
		{
			super.resetLine(textLine);
			if (_resetLineHandler != null)
				_resetLineHandler(textLine);
		}
		
		/** @private */
		protected override function composeNextLine():TextLine
		{
			CONFIG::debug { assert(!_previousLine || _previousLine.validity == "valid","Bad prevline: "+Debugging.getIdentity(_previousLine)); }
			
			var numberLine:TextLine;
			// create numberLine if in a listElement
			if (_listItemElement && _listItemElement.getAbsoluteStart() == _curElementStart+_curElementOffset)
			{
				var isRTL:Boolean = _curParaElement.computedFormat.direction == Direction.RTL;
				numberLine = TextFlowLine.createNumberLine(_listItemElement, _curParaElement, _flowComposer.swfContext, isRTL ? _parcelList.rightMargin : _parcelList.leftMargin);				
				pushInsideListItemMargins(numberLine);
			}
			
			// space to palce a line?			
			if (!_parcelList.getLineSlug(_lineSlug, 0, 0, _textIndent, _curParaFormat.direction == Direction.LTR))
				return null;
			
			var textLine:TextLine;
			
			for (;;) 
			{
				for (;;)
				{	
					// generate new line
					CONFIG::debug { assert(!_parcelList.atEnd(), "failing to stop"); }
					CONFIG::debug { assert(_curElement is FlowLeafElement, "element must be leaf before calling composeLine"); }
					
					textLine = createTextLine(_lineSlug.width, !_lineSlug.wrapsKnockOut /* don't allow emergency breaks next to floats or padded elements */);
					if (textLine)
						break;

					// force advance within the parcel to the next wider slug, or (if there are no more) to the next parcel
					var newDepth:Number = _curParcel.findNextTransition(_lineSlug.depth);
					if (newDepth < Number.MAX_VALUE)
					{
						_parcelList.addTotalDepth(newDepth - _lineSlug.depth);
						_parcelList.getLineSlug(_lineSlug, 0, 1, _textIndent, _curParaFormat.direction == Direction.LTR);
					}
					else
					{
						advanceToNextParcel();
						if (!_parcelList.atEnd())
							if (_parcelList.getLineSlug(_lineSlug, 0, 1, _textIndent, _curParaFormat.direction == Direction.LTR))
								continue;
						popInsideListItemMargins(numberLine);
						return null;
					}
				}

				
				// updates _lineSlug
				if (fitLineToParcel(textLine, true, numberLine))	// TODO!!!!!!
					break;		// we have a good line
				if (resetLineHandler != null)
					resetLineHandler(textLine);
				if (_parcelList.atEnd())		// keep going
				{
					popInsideListItemMargins(numberLine);
					return null;
				}
			}
			
			popInsideListItemMargins(numberLine);

			CONFIG::debug { assert(textLine != null, "textLine != null"); }		
			return textLine;
		}
        
        /** @private */
        tlf_internal function swapLines(lines:Array):Array
        {
        	var current:Array = _lines;
        	_lines = lines;
        	return current;
        }

		/** Final adjustment on the content bounds. */
 		override protected function finalParcelAdjustment(controller:ContainerController):void
 		{
			CONFIG::debug { assert(controller.absoluteStart == 0,"SimpleCompose: multiple controllers not supported"); }

			var minX:Number = TextLine.MAX_LINE_WIDTH;
 			var minY:Number = TextLine.MAX_LINE_WIDTH;
 			var maxX:Number = -TextLine.MAX_LINE_WIDTH;
			
			var verticalText:Boolean = _blockProgression == BlockProgression.RL;
			
			if (!isNaN(_parcelLogicalTop))
			{
				if (verticalText)
					maxX = _parcelLogicalTop;
				else
					minY = _parcelLogicalTop;
			}
			
			if (!_measuring)
			{
				if (verticalText)
					minY = _accumulatedMinimumStart;
				else
					minX = _accumulatedMinimumStart;			
			}
			else
			{
	 			var textLine:TextLine;
	 			var startPos:int = 0;
				
				var firstLineAdjust:Number;
				var effectiveIndent:Number;
				var edgeAdjust:Number;
				var curPara:ParagraphElement;
				var curParaFormat:ITextLayoutFormat;
				var paddingVerticalAdjust:Number = 0;		// logical vertical adjustment due to padding on paragraph & divs
				var paddingHorizontalAdjust:Number = 0;		// logical horizontal adjustment due to padding on paragraph & divs
				var previousParagraph:ParagraphElement = null;
	
				for each (textLine in _lines)
				{
					var leaf:FlowLeafElement = controller.textFlow.findLeaf(startPos);
					var para:ParagraphElement = leaf.getParagraph();
					if (para != previousParagraph)
					{
						// Recalculate padding values for the new paragraph
						paddingVerticalAdjust = 0;
						paddingHorizontalAdjust = 0;
						var fge:FlowGroupElement = para;
						while (fge && fge.parent)
						{
							if (verticalText)
							{
								paddingVerticalAdjust += fge.getEffectivePaddingRight();
								paddingHorizontalAdjust += fge.getEffectivePaddingTop();
							}
							else
							{
								paddingVerticalAdjust += fge.getEffectivePaddingTop();
								paddingHorizontalAdjust += fge.getEffectivePaddingLeft();
							}
							fge = fge.parent;
						}
						previousParagraph = para;
					}
	
	            	// Check the logical vertical dimension first
	            	// If the lines have children, they may be inlines. The origin of the TextLine is the baseline, 
	            	// which does not include the ascent of the inlines or the text. So we have to factor that in.
					// var verticalAdjust:Number = verticalText ? textLine.descent : textLine.ascent;
					var inlineAscent:Number = 0;
					if (textLine.numChildren > 0)		// adjustjust logical vertical coord to take into account inlines
					{
						var leafStart:int = leaf.getAbsoluteStart();
						inlineAscent = TextFlowLine.getTextLineTypographicAscent(textLine, leaf, leafStart, startPos + textLine.rawTextLength);
					}
	
					// Figure out the logical horizontal adjustment
					CONFIG::debug { assert(curPara != para, "found it"); }
					if (curPara != para)
					{
						curParaFormat = para.computedFormat;
						if (curParaFormat.direction == Direction.LTR)
						{
							firstLineAdjust = Math.max(curParaFormat.textIndent, 0);
							effectiveIndent = curParaFormat.paragraphStartIndent;
						}
						else
						{
							firstLineAdjust = 0;
							effectiveIndent = curParaFormat.paragraphEndIndent;
						}
					}
					effectiveIndent += paddingHorizontalAdjust;
					
					edgeAdjust = textLine.textBlockBeginIndex == 0 ? effectiveIndent + firstLineAdjust : effectiveIndent;
					edgeAdjust = verticalText ? textLine.y - edgeAdjust : textLine.x - edgeAdjust;
					
					var numberLine:TextLine = TextFlowLine.findNumberLine(textLine);
					
					if (numberLine)
					{
						var numberLineStart:Number = verticalText ? numberLine.y+textLine.y : numberLine.x+textLine.x;
						edgeAdjust = Math.min(edgeAdjust,numberLineStart);
					}
					
					if (verticalText)
					{
						minY = Math.min(edgeAdjust, minY);
			            maxX = Math.max(textLine.x + Math.max(inlineAscent,textLine.ascent) + paddingVerticalAdjust, maxX);
					}
					else
					{
						minX = Math.min(edgeAdjust, minX);
						if (inlineAscent < textLine.ascent)
							inlineAscent = textLine.ascent;
			           	minY = Math.min(textLine.y - (inlineAscent + paddingVerticalAdjust), minY);
			  		}
			  		startPos += textLine.rawTextLength;
   				}
			}
			
            // Don't make adjustments for tiny fractional values.
            /*if (minX != _parcelLeft && Math.abs(minX-_parcelLeft) >= 1)
         		_parcelLeft = minX;
            if (maxX != _parcelRight && Math.abs(maxX-_parcelRight) >= 1)
         		_parcelRight = maxX;
         	if (minY != _parcelTop && Math.abs(minY-_parcelTop) >= 1)
           		_parcelTop = minY;*/
			if (minX != TextLine.MAX_LINE_WIDTH && Math.abs(minX-_parcelLeft) >= 1)
				_parcelLeft = minX;
			if (maxX != -TextLine.MAX_LINE_WIDTH && Math.abs(maxX-_parcelRight) >= 1)
				_parcelRight = maxX;
			if (minY != TextLine.MAX_LINE_WIDTH && Math.abs(minY-_parcelTop) >= 1)
				_parcelTop = minY;

 		}		
		
		tlf_internal override function releaseAnyReferences():void
		{
			super.releaseAnyReferences();
			workingLine.initialize(null,0,0,0,0,null);
			resetLineHandler = null;
			// parcelList.releaseAnyReferences();
		}
	}
}
import flash.text.engine.TextLine;
import flashx.textLayout.compose.IVerticalJustificationLine;
import flash.text.engine.TextLineCreationResult;

class VJHelper implements IVerticalJustificationLine
{
	private var _line:TextLine;
	private var _height:Number;

	public function VJHelper(line:TextLine,h:Number)
	{
		_line = line;
		_height = h;
	}
	public function get x():Number
	{ return _line.x; }
	public function set x(val:Number):void
	{ _line.x = val; }
		
	public function get y():Number
	{ return _line.y; }
	public function set y(val:Number):void
	{ _line.y = val; }
		
	public function get ascent():Number
	{ return _line.ascent; }
	public function get descent():Number
	{ return _line.descent; }
	public function get height():Number
	{ return _height; }
}
