////////////////////////////////////////////////////////////////////////////////
//
// 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.conversion
{
	import flash.utils.Dictionary;
	
	import flashx.textLayout.debug.assert;
	import flashx.textLayout.elements.ContainerFormattedElement;
	import flashx.textLayout.elements.DivElement;
	import flashx.textLayout.elements.FlowElement;
	import flashx.textLayout.elements.FlowGroupElement;
	import flashx.textLayout.elements.FlowValueHolder;
	import flashx.textLayout.elements.InlineGraphicElement;
	import flashx.textLayout.elements.LinkElement;
	import flashx.textLayout.elements.ListElement;
	import flashx.textLayout.elements.ParagraphFormattedElement;
	import flashx.textLayout.elements.SpanElement;
	import flashx.textLayout.elements.SubParagraphGroupElement;
	import flashx.textLayout.elements.TCYElement;
	import flashx.textLayout.elements.TextFlow;
	import flashx.textLayout.formats.FormatValue;
	import flashx.textLayout.formats.ITextLayoutFormat;
	import flashx.textLayout.formats.ListMarkerFormat;
	import flashx.textLayout.formats.TextLayoutFormat;
	import flashx.textLayout.formats.WhiteSpaceCollapse;
	import flashx.textLayout.property.Property;
	import flashx.textLayout.tlf_internal;
	
	use namespace tlf_internal;
	
	[ExcludeClass]
	/** 
	 * @private
	 * Export filter for TextLayout format. 
	 */
	internal class TextLayoutExporter extends BaseTextLayoutExporter
	{	
		static private var _formatDescription:Object= TextLayoutFormat.description;

		public function TextLayoutExporter()
		{
			super(new Namespace("http://ns.adobe.com/textLayout/2008"), null, TextLayoutImporter.defaultConfiguration);
		}
		
		static private const brTabRegEx:RegExp = new RegExp("[" + "\u2028" + "\t" + "]"); // Doesn't /\u2028\t/ work?
		
		/** Gets the regex that specifies characters to be replaced with XML elements
		 *  Note: Each match is a single character 
		 */
		protected override function get spanTextReplacementRegex():RegExp
		{
			return brTabRegEx;
		}
		
		/** Gets the xml element used to represent a character in the export format
		 */
		protected override function getSpanTextReplacementXML(ch:String):XML
		{
			var replacementXML:XML;
			if (ch == '\u2028')
				replacementXML = <br/>;
			else if (ch == '\t')
				replacementXML = <tab/>;
			else
			{
				CONFIG::debug {assert(false, "Did not recognize character to be replaced with XML"); }
				return null;			
			}
		
			replacementXML.setNamespace(flowNS);
			return replacementXML;	
		}
		
		/** Helper function to export styles (core or user) in the form of xml attributes or xml children
		 * @private
		 */
		tlf_internal function createStylesFromDescription(styles:Object, description:Object, includeUserStyles:Boolean, exclusions:Array):Array
		{
			var sortableStyles:Array = [];
			for (var key:String in styles)
			{
				var val:Object = styles[key];
				if (exclusions && exclusions.indexOf(val) != -1)
					continue;
				
				var prop:Property = description[key];
				if (!prop)
				{
					if (includeUserStyles)
					{
						// User style
						if ((val is String) || val.hasOwnProperty("toString"))
						{
							// Is or can be converted to a String which will be used as an XML attribute value
							sortableStyles.push({xmlName:key, xmlVal:val});
						}						
					}
				}
				else if (val is TextLayoutFormat)
				{
					// A style dictionary; Will be converted to an XMLList containing elements to be added as children 
					var customDictProp:XMLList = exportObjectAsTextLayoutFormat(key,(val as TextLayoutFormat).getStyles());
					if (customDictProp)
						sortableStyles.push({xmlName:key, xmlVal:customDictProp});
				}
				else
					sortableStyles.push({xmlName:key, xmlVal:prop.toXMLString(val)});		
			}
			return sortableStyles;  
		}
		
		tlf_internal function exportObjectAsTextLayoutFormat(key:String,styleDict:Object):XMLList
		{
			// link attributes and ListMarkerFormat
			var elementName:String;
			var description:Object;
			if (key == LinkElement.LINK_NORMAL_FORMAT_NAME || key == LinkElement.LINK_ACTIVE_FORMAT_NAME || key == LinkElement.LINK_HOVER_FORMAT_NAME)
			{
				elementName = "TextLayoutFormat";
				description = TextLayoutFormat.description;
			}
			else if (key == ListElement.LIST_MARKER_FORMAT_NAME)
			{
				elementName = "ListMarkerFormat";
				description = ListMarkerFormat.description;
			}
			
			if (elementName == null)
				return null;
				
			// create the  element
			var formatXML:XML = <{elementName}/>;
			formatXML.setNamespace(flowNS);
			var sortableStyles:Array = createStylesFromDescription(styleDict, description, true, null);
			exportStyles(XMLList(formatXML), sortableStyles);
			
			// create the link format element
			var propertyXML:XMLList = XMLList(<{key}/>);
			propertyXML.appendChild(formatXML);
			return propertyXML;
		}
			
		protected override function exportFlowElement(flowElement:FlowElement):XMLList
		{
			var rslt:XMLList = super.exportFlowElement(flowElement);
			
			var allStyles:Object = flowElement.styles;
			if (allStyles)
			{
				// WhiteSpaceCollapse attribute should never be exported (except on TextFlow -- handled separately)
				delete allStyles[TextLayoutFormat.whiteSpaceCollapseProperty.name];
				// To prevent "inherit" from getting exported for the root node, comment in the following line, and remove the one after that (only need one call to exportStyles
				var sortableStyles:Array = createStylesFromDescription(allStyles,formatDescription,true,flowElement.parent ? null : [FormatValue.INHERIT]);
				exportStyles(rslt, sortableStyles );
			}
			
			// export id and styleName
			if (flowElement.id != null)
				rslt.@["id"] = flowElement.id;
			if (flowElement.typeName != flowElement.defaultTypeName)
				rslt.@["typeName"] = flowElement.typeName;
				
			return rslt;
		}

		/** Base functionality for exporting an Image. Exports as a FlowElement,
		 * and exports image properties.
		 * @param exporter	Root object for the export
		 * @param image	Element to export
		 * @return XMLList	XML for the element
		 */
		static public function exportImage(exporter:BaseTextLayoutExporter, image:InlineGraphicElement):XMLList
		{
			var output:XMLList = exportFlowElement(exporter, image);
			
			// output the img specific values
			if (image.height !== undefined)
				output.@height = image.height;
			if (image.width !== undefined)
				output.@width = image.width;
		//	output.@rotation = image.rotation;  don't support rotation yet
			if (image.source != null)
				output.@source = image.source;
			if (image.float != undefined)
				output.@float = image.float;
						
			return output;
		}

		/** Base functionality for exporting a LinkElement. Exports as a FlowGroupElement,
		 * and exports link properties.
		 * @param exporter	Root object for the export
		 * @param link	Element to export
		 * @return XMLList	XML for the element
		 */
		static public function exportLink(exporter:BaseTextLayoutExporter, link:LinkElement):XMLList
		{
			var output:XMLList = exportFlowGroupElement(exporter, link);

			if (link.href)
				output.@href= link.href;
				
			if (link.target)
				output.@target = link.target;
				
			return output;
		}
		
		/** Base functionality for exporting a DivElement. Exports as a FlowContainerFormattedElement
		 * @param exporter	Root object for the export
		 * @param div	Element to export
		 * @return XMLList	XML for the element
		 */
		static public function exportDiv(exporter:BaseTextLayoutExporter, div:DivElement):XMLList
		{
			return exportContainerFormattedElement(exporter, div);
		}
		
		/** Base functionality for exporting a SubParagraphGroupElement. Exports as a FlowGroupElement
		 * @param exporter	Root object for the export
		 * @param elem	Element to export
		 * @return XMLList	XML for the element
		 */
		static public function exportSPGE(exporter:BaseTextLayoutExporter, elem:SubParagraphGroupElement):XMLList
		{
			return exportFlowGroupElement(exporter, elem);
		}
		/** Base functionality for exporting a TCYElement. Exports as a FlowGroupElement
		 * @param exporter	Root object for the export
		 * @param tcy	Element to export
		 * @return XMLList	XML for the element
		 */
		static public function exportTCY(exporter:BaseTextLayoutExporter, tcy:TCYElement):XMLList
		{
			return exportFlowGroupElement(exporter, tcy);
		}
		
		override protected function get formatDescription():Object
		{
			return _formatDescription;
		}		

	}
}
