////////////////////////////////////////////////////////////////////////////////
//
//  ADOBE SYSTEMS INCORPORATED
//  Copyright 2009 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 mx.charts
{

import flash.display.DisplayObject;
import flash.events.MouseEvent;
import flash.geom.Rectangle;
import mx.charts.chartClasses.IChartElement;
import mx.charts.renderers.BoxItemRenderer;
import mx.charts.styles.HaloDefaults;
import mx.core.IDataRenderer;
import mx.core.IFactory;
import mx.core.IFlexDisplayObject;
import mx.core.IUITextField;
import mx.core.UIComponent;
import mx.core.UITextField;
import mx.core.mx_internal;
import mx.graphics.SolidColor;
import mx.styles.CSSStyleDeclaration;
import mx.styles.ISimpleStyleClient;
import mx.core.IFlexModuleFactory;

use namespace mx_internal;

//--------------------------------------
//  Styles
//--------------------------------------

include "../styles/metadata/GapStyles.as"
include "../styles/metadata/PaddingStyles.as"
include "styles/metadata/TextStyles.as"

/**
 *  Specifies an IFill object that defines the fill for the legend element.
 *  
 *  @langversion 3.0
 *  @playerversion Flash 9
 *  @playerversion AIR 1.1
 *  @productversion Flex 3
 */
[Style(name="fill", type="Object", inherit="no")]

/**
 *  Specifies the label placement of the legend element. Recognized values are 
 *  <code>"top"</code>, <code>"bottom"</code>, <code>"left"</code>, and <code>"right"</code>.
 *  
 *  @langversion 3.0
 *  @playerversion Flash 9
 *  @playerversion AIR 1.1
 *  @productversion Flex 3
 */
[Style(name="labelPlacement", type="String", enumeration="top,bottom,right,left", inherit="yes")]

/**
 *  Specifies the class that renders the marker portion of the legend item.
 *  Typically the marker is provided by the chart element that
 *  generates the legend item.
 *  When you create a legend manually, however, this style specifies
 *  what class is used to render the marker. 
 *  
 *  @langversion 3.0
 *  @playerversion Flash 9
 *  @playerversion AIR 1.1
 *  @productversion Flex 3
 */
[Style(name="legendMarkerRenderer", type="mx.core.IFactory", inherit="no")]

/** 
 *  Specifies the height of the legend element.
 *  
 *  @langversion 3.0
 *  @playerversion Flash 9
 *  @playerversion AIR 1.1
 *  @productversion Flex 3
 */
[Style(name="markerHeight", type="Number", format="Length", inherit="yes")]

/**
 *  Specifies the width of the legend element.
 *  
 *  @langversion 3.0
 *  @playerversion Flash 9
 *  @playerversion AIR 1.1
 *  @productversion Flex 3
 */
[Style(name="markerWidth", type="Number", format="Length", inherit="yes")]

/**
 *  Specifies the line stroke for the legend element.
 *  
 *  @langversion 3.0
 *  @playerversion Flash 9
 *  @playerversion AIR 1.1
 *  @productversion Flex 3
 */
[Style(name="stroke", type="Object", inherit="no")]

/**
 *  Controls the individual legend elements in a chart legend.
 *  
 *  @mxml
 *  
 *  <p>The <code>&lt;mx:LegendItem&gt;</code> tag inherits all the properties
 *  of its parent classes and adds the following properties:</p>
 *  
 *  <pre>
 *  &lt;mx:LegendItem
 *    <strong>Properties</strong>
 *    element="<i>No default</i>"    
 *    label="<i>No default</i>"
 *    source="<i>No default</i>"
 *    legendData="<i>No default</i>"
 *    marker="<i>No default</i>"
 *    markerAspectRatio="<i>No default</i>"
 * 
 *    <strong>Styles</strong>
 *    fill="<i>IFill; no default.</i>"
 *    fontWeight="normal|bold"
 *    fontSize="10"
 *    horizontalGap="8"
 *    labelPlacement="right|left|top|bottom"
 *    legendMarkerRenderer="<i>Defaults to series's itemRenderer</i>"
 *    markerHeight="15"
 *    markerWidth="10"
 *    paddingLeft="0"
 *    paddingRight="0"
 *    stroke="<i>IStroke; no default</i>"
 * 	  textDecoration="underline|none"
 *    verticalGap="6"
 *  /&gt;
 *  </pre>
 *  
 *  @see mx.charts.Legend
 *  @see mx.charts.chartClasses.LegendData
 *  
 *  @langversion 3.0
 *  @playerversion Flash 9
 *  @playerversion AIR 1.1
 *  @productversion Flex 3
 */
public class LegendItem extends UIComponent
{
    include "../core/Version.as";

	//--------------------------------------------------------------------------
	//
	//  Class initialization
	//
	//--------------------------------------------------------------------------

	//--------------------------------------------------------------------------
	//
	//  Constructor
	//
	//--------------------------------------------------------------------------

	/**
	 *  Constructor.
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 9
	 *  @playerversion AIR 1.1
	 *  @productversion Flex 3
	 */
	public function LegendItem()
	{
		super();
	}

	//--------------------------------------------------------------------------
	//
	//  Variables
	//
	//--------------------------------------------------------------------------

	/**
	 *  @private
	 */
	private var _moduleFactoryInitialized:Boolean = false;
	
	/**
	 *  @private
	 */
	private var nameLabel:IUITextField;

	/**
	 *  @private
	 */
	private var labelChanged:Boolean = false;

	//--------------------------------------------------------------------------
	//
	//  Properties
	//
	//--------------------------------------------------------------------------

	//----------------------------------
	//  element
	//----------------------------------

	[Inspectable(environment="none")]

	/**
	 *  The chart element that is responsible for generating this legend item.
	 *  When a Legend control's contents are automatically generated by a chart,
	 *  this field refers to the element (usually a series)
	 *  that this item represents.
	 *  In some cases, multiple items refer to the same element.
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 9
	 *  @playerversion AIR 1.1
	 *  @productversion Flex 3
	 */
	public var element:IChartElement;

	//----------------------------------
	//  label
	//----------------------------------

	/**
	 *  @private
	 *  Storage for the label property.
	 */
	private var _label:String = "";

	[Inspectable(category="General")]
	
	/**
	 *  Specifies the text that Flex displays alongside the legend element.
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 9
	 *  @playerversion AIR 1.1
	 *  @productversion Flex 3
	 */
	public function get label():String
	{
		return _label;
	}

	/**
	 *  @private
	 */
	public function set label(value:String):void
	{
		_label = value;
		labelChanged = true;

		invalidateSize();
		invalidateDisplayList();
	}

	//----------------------------------
	//  legendData
	//----------------------------------

	/**
	 *  @private
	 *  Storage for the legendData property.
	 */
	private var _legendData:Object;

	[Inspectable(environment="none")]

	/**
	 *  The LegendData instance that this LegendItem object represents.
	 *  When a chart generates the contents of a Legend control,
	 *  the elements of the chart generate one or more
	 *  LegendData structures for display in the Legend.
	 *  Each LegendData structure generates one LegendItem object.  
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 9
	 *  @playerversion AIR 1.1
	 *  @productversion Flex 3
	 */
	public function set legendData(value:Object):void
	{
		_legendData = value;

		if (_marker is IDataRenderer)
			(_marker as IDataRenderer).data = _legendData;
	}

	//----------------------------------
	//  marker
	//----------------------------------
	
	/**
	 *  @private
	 *  Storage for the marker property.
	 */
	private var _marker:IFlexDisplayObject;

	[Inspectable]

	/**
	 *  The marker displayed by this legend item.
	 *  Markers are typically assigned by the chart element
	 *  that generated the LegendData associated with this item.
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 9
	 *  @playerversion AIR 1.1
	 *  @productversion Flex 3
	 */
	public function get marker():IFlexDisplayObject
	{
		return _marker;
	}
	
	/**
	 *  @private
	 */
	public function set marker(value:IFlexDisplayObject):void
	{
		if (_marker)
			removeChild(_marker as DisplayObject);
		
		_marker = value;
		
		if (_marker)
			addChild(_marker as DisplayObject);

		if (_marker is IDataRenderer)
			(_marker as IDataRenderer).data = _legendData;

		invalidateDisplayList();
	}

	//----------------------------------
	//  markerAspectRatio
	//----------------------------------

	[Inspectable(category="General")]

	/**
	 *  The aspect ratio for the marker associated with this legend item.
	 *  Some markers provided by chart series are intended for display
	 *  at a particular aspect ratio.
	 *  If this property is set, the legend item guarantees
	 *  the aspect ratio of the marker during layout.
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 9
	 *  @playerversion AIR 1.1
	 *  @productversion Flex 3
	 */
	public var markerAspectRatio:Number;

	//----------------------------------
	//  source
	//----------------------------------

	[Inspectable(environment="none")]

	/**
	 *  Contains a reference
	 *  to the data series that the LegendItem was generated from
	 *  if you bind the <code>dataProvider</code> property
	 *  of the parent Legend control to a chart control.
	 *  Otherwise, this property contains a reference
	 *  to the object associated with the LegendItem.
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 9
	 *  @playerversion AIR 1.1
	 *  @productversion Flex 3
	 */
	public function get source():Object
	{
		return element;
	}

	//--------------------------------------------------------------------------
	//
	//  Overridden methods: UIComponent
	//
	//--------------------------------------------------------------------------
	
	/**
	 *  @inheritDoc
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 9
	 *  @playerversion AIR 1.1
	 *  @productversion Flex 3
	 */
	override public function set moduleFactory(factory:IFlexModuleFactory):void
	{
		super.moduleFactory = factory;
		
		if (_moduleFactoryInitialized)
			return;
		
		_moduleFactoryInitialized = true;
	}
	
	/**
	 *  @private
	 */
	override protected function createChildren():void
    {
        nameLabel = IUITextField(createInFontContext(UITextField));
		nameLabel.selectable = false;
		nameLabel.styleName = this;
		addChild(DisplayObject(nameLabel));

        super.createChildren();

        if (!nameLabel)
		{
			nameLabel = IUITextField(createInFontContext(UITextField));
			nameLabel.selectable = false;
			nameLabel.styleName = this;
			addChild(DisplayObject(nameLabel));
		}
    }

	/**
	 *  @private
	 */
	override protected function measure():void
	{
		super.measure();

		var txt:String = _label;
		
		var labelPlacement:String = getStyle("labelPlacement");
		if (labelPlacement == "" && parent)
			labelPlacement = UIComponent(parent).getStyle("labelPlacement");

		var itemW:Number = 0;
		var itemH:Number = 0;
		
		// If the text is empty (or undefined), make the preferred
		// size big enough to hold a capital and decending character
		// using the current font.
		if (txt == null || txt == "" || txt.length < 2)
			txt = "Wj";

		if (labelChanged)
		{
			labelChanged = false;
			nameLabel.htmlText = _label;
		}

		var textW:Number = nameLabel.textWidth + UITextField.TEXT_WIDTH_PADDING;
		var textH:Number = nameLabel.textHeight + UITextField.TEXT_HEIGHT_PADDING;

		if (isNaN(textW))
			textW = 0;
		if (isNaN(textH))
			textH = 0;

		if (textW > 0 || calculateMarkerWidth() > 0)
		{
			itemW += getStyle("paddingLeft") + getStyle("paddingRight");
			itemH += getStyle("paddingTop") + getStyle("paddingBottom");

			if (labelPlacement == "top" || labelPlacement == "bottom")
			{
				itemW += Math.max(textW, calculateMarkerWidth());
				itemH += getStyle("verticalGap") + calculateMarkerHeight() + textH;
			}
			else
			{
				itemW += getStyle("horizontalGap") + calculateMarkerWidth() + textW;
				itemH += Math.max(textH, calculateMarkerHeight());
			}
		}

		measuredWidth = itemW;
		measuredHeight = itemH;

		// Set the labelField size here.
		// This way the width getter will return the proper value
		// when the resize event is fired.
		nameLabel.setActualSize(getExplicitOrMeasuredWidth(), measuredHeight);
	}

	/**
	 *  @private
	 */
	override protected function updateDisplayList(unscaledWidth:Number,
												  unscaledHeight:Number):void
	{
		super.updateDisplayList(unscaledWidth, unscaledHeight);

		var labelPlacement:String = getStyle("labelPlacement");

		var labelX:Number = 0;
		var labelY:Number = 0;

		var markerX:Number = 0;
		var markerY:Number = 0;

		var paddingLeft:Number = getStyle("paddingLeft");
		var paddingRight:Number = getStyle("paddingRight");
		var paddingTop:Number = getStyle("paddingTop");

		var mAvailableWidth:Number = calculateMarkerWidth();
		var mAvailableHeight:Number = calculateMarkerHeight();

		var markerH:Number = mAvailableHeight;
		var markerW:Number = mAvailableWidth;

		var txt:String = _label;

		if (txt == null || txt.length < 2)
			txt = "Wj";

		if (labelChanged)
		{
			labelChanged = false;
			nameLabel.text = _label;
		}

		var textW:Number = nameLabel.textWidth + UITextField.TEXT_WIDTH_PADDING;
		var textH:Number = nameLabel.textHeight + UITextField.TEXT_HEIGHT_PADDING;

		var cx:Number;

		if (isNaN(textW))
			textW = 0;
		if (isNaN(textH))
			textH = 0;

		switch (labelPlacement)
		{
			case "top":
			{
				labelY = paddingTop;
				markerY = labelY + getStyle("verticalGap") + textH;

				cx = paddingLeft +
					 (unscaledWidth - paddingLeft - paddingRight) / 2;
				labelX = cx - textW / 2;
				markerX = cx - markerW / 2;
				
				break;
			}

			case "bottom":
			{
				markerY = paddingTop;
				labelY = markerY + getStyle("verticalGap") + markerH;

				cx = paddingLeft +
					 (unscaledWidth - paddingLeft - paddingRight) / 2;
				labelX = cx - textW / 2;
				markerX = cx - markerW / 2;
				
				break;
			}

			case "left":
			{
				markerX = unscaledWidth - markerW - paddingRight;
				labelX = markerX - getStyle("horizontalGap") - textW;

				labelY = markerY = paddingTop;
				if (textH < markerH)
					labelY += (markerH - textH) / 2;
				else
					markerY += (textH - markerH) / 2;
				
				break;
			}

			default: // default is right
			{
				markerX = paddingLeft;
				labelX = markerX + getStyle("horizontalGap") + markerW;

				markerY = labelY = paddingTop;
				if (markerH < textH)
					markerY += (textH - markerH) / 2;
				else
					labelY += (markerH - textH) / 2;
			
				break;
			}
		}

		nameLabel.move(labelX, labelY);

		if (!isNaN(markerAspectRatio))
		{
			var availableAR:Number = markerW / markerH;
			if (availableAR > markerAspectRatio)
				markerW = markerAspectRatio * markerH;
			else
				markerH = markerW / markerAspectRatio;
		}

		var rc:Rectangle = new Rectangle((mAvailableWidth - markerW) / 2,
										 (mAvailableHeight - markerH) / 2,
										 markerW, markerH);

		if (!_marker)
		{
			var markerClass:IFactory = getStyle("legendMarkerRenderer");
			
			if (markerClass)
				marker = markerClass.newInstance();
			else
				marker = new BoxItemRenderer();

			if (_marker is ISimpleStyleClient)
			{
				(_marker as ISimpleStyleClient).styleName =
					(!element) ? this : element;
			}
		}

		_marker.x = markerX + (mAvailableWidth - markerW) / 2;
		_marker.y = markerY + (mAvailableHeight - markerH) / 2;
		
		_marker.setActualSize(markerW, markerH);
	}

	/**
	 *  @private
	 */
	override public function styleChanged(styleProp:String):void
	{
		invalidateDisplayList();
	}

	//--------------------------------------------------------------------------
	//
	//  Methods
	//
	//--------------------------------------------------------------------------

	/**
	 *  @private
	 */
	private function calculateMarkerWidth():Number
	{
		var out:Number = getStyle("markerWidth");
		if (isNaN(out))
			out = 10;
		return out;
	}

	/**
	 *  @private
	 */
	private function calculateMarkerHeight():Number
	{
		var out:Number = getStyle("markerHeight");
		if (isNaN(out))
			out = 15;
		return out;
	}
}

}
