////////////////////////////////////////////////////////////////////////////////
//
//  ADOBE SYSTEMS INCORPORATED
//  Copyright 2003-2007 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.controls
{

import flash.display.DisplayObject;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
import mx.core.EdgeMetrics;
import mx.core.IFlexDisplayObject;
import mx.core.IFlexModuleFactory;
import mx.core.IFontContextComponent;
import mx.core.IRectangularBorder;
import mx.core.IToolTip;
import mx.core.IUITextField;
import mx.core.UIComponent;
import mx.core.UITextField;
import mx.core.mx_internal;
import mx.styles.ISimpleStyleClient;

use namespace mx_internal;

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

/*
    Note: ToolTip is affected by the following styles:

    backgroundColor
    borderColor
    borderStyle
    color
    fontFamily
    fontSize
    fontStyle
    fontWidth
    paddingBottom
    paddingLeft
    paddingRight
    paddingTop
    shadowColor (when borderStyle is "toolTip")
    textAlign
    textDecoration
*/

include "../styles/metadata/ContainerBackgroundStyles.as"
include "../styles/metadata/BorderStyles.as"
include "../styles/metadata/LeadingStyle.as"
include "../styles/metadata/PaddingStyles.as"
include "../styles/metadata/TextStyles.as"

/**
 *  Radius of component corners.
 *  
 *  @default 2
 *  
 *  @langversion 3.0
 *  @playerversion Flash 9
 *  @playerversion AIR 1.1
 *  @productversion Flex 3
 */
[Style(name="cornerRadius", type="Number", format="Length", inherit="no", theme="halo, spark, mobile")]

/**
 *  Number of pixels between the container's bottom border and its content area.
 *  The default value is -1, so the bottom border of the last header
 *  overlaps the Accordion container's bottom border.
 *  
 *  @langversion 3.0
 *  @playerversion Flash 9
 *  @playerversion AIR 1.1
 *  @productversion Flex 3
 */
[Style(name="paddingBottom", type="Number", format="Length", inherit="no")]

/**
 *  Number of pixels between the container's top border and its content area.
 *  The default value is -1, so the top border of the first header
 *  overlaps the Accordion container's top border.
 *  
 *  @langversion 3.0
 *  @playerversion Flash 9
 *  @playerversion AIR 1.1
 *  @productversion Flex 3
 */
[Style(name="paddingTop", type="Number", format="Length", inherit="no")]

/**
 * Because this component does not define a skin for the mobile theme, Adobe
 * recommends that you not use it in a mobile application. Alternatively, you
 * can define your own mobile skin for the component. For more information,
 * see <a href="http://help.adobe.com/en_US/flex/mobileapps/WS19f279b149e7481c698e85712b3011fe73-8000.html">Basics of mobile skinning</a>.
 */
[DiscouragedForProfile("mobileDevice")]

/**
 *  The ToolTip control lets you provide helpful information to your users.
 *  When a user moves the mouse pointer over a graphical component, the ToolTip
 *  control pops up and displays text that provides information about the
 *  component.
 *  You can use ToolTips to guide users as they work with your application
 *  or customize the ToolTip controls to provide additional functionality.
 *
 *  @see mx.managers.ToolTipManager
 *  @see mx.styles.CSSStyleDeclaration
 *  
 *  @langversion 3.0
 *  @playerversion Flash 9
 *  @playerversion AIR 1.1
 *  @productversion Flex 3
 */
public class ToolTip extends UIComponent implements IToolTip, IFontContextComponent
{
    include "../core/Version.as";

    //--------------------------------------------------------------------------
    //
    //  Class properties
    //
    //--------------------------------------------------------------------------

    [Inspectable(category="Other")]
    
    /**
     *  Maximum width in pixels for new ToolTip controls.
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public static var maxWidth:Number = 300;

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

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

        // InteractiveObject variables.
        // Make the ToolTip invisible to the mouse so that it doesn't
        // interfere with the ToolTipManager's mouse-tracking.
        mouseEnabled = false;
    }

    //--------------------------------------------------------------------------
    //
    //  Variables
    //
    //--------------------------------------------------------------------------
    
    /**
     *  The internal object that draws the border.
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    mx_internal var border:IFlexDisplayObject;
    
    //--------------------------------------------------------------------------
    //
    //  Properties
    //
    //--------------------------------------------------------------------------

    //----------------------------------
    //  borderMetrics
    //----------------------------------

    /**
     *  @private
     */
    private function get borderMetrics():EdgeMetrics
    {
        if (border is IRectangularBorder)
            return IRectangularBorder(border).borderMetrics;

        return EdgeMetrics.EMPTY;
    }

    //----------------------------------
    //  fontContext
    //----------------------------------
    
    /**
     *  @private 
     */
    public function get fontContext():IFlexModuleFactory
    {
        return moduleFactory;
    }

    /**
     *  @private
     */
    public function set fontContext(moduleFactory:IFlexModuleFactory):void
    {
        this.moduleFactory = moduleFactory;
    }
    
    //----------------------------------
    //  text
    //----------------------------------

    /**
     *  @private
     *  Storage for the text property.
     */
    private var _text:String;

    /**
     *  @private
     */
    private var textChanged:Boolean;

    /**
     *  The text displayed by the ToolTip.
     *
     *  @default null
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function get text():String
    {
        return _text;
    }

    /**
     *  @private
     */
    public function set text(value:String):void
    {
        _text = value;
        textChanged = true;

        invalidateProperties();
        invalidateSize();
        invalidateDisplayList();
    }

    //----------------------------------
    //  textField
    //----------------------------------

    /**
     *  The internal UITextField that renders the text of this ToolTip.
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    protected var textField:IUITextField;

    //--------------------------------------------------------------------------
    //
    //  Overridden methods
    //
    //--------------------------------------------------------------------------

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

        // Create the border/background.
        createBorder();

        // Create the TextField that displays the tooltip text.
        createTextField(-1);
    }

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

        // if the font changed and we already created the label, we will need to 
        // destory it so it can be re-created, possibly in a different swf context.
        if (hasFontContextChanged() && textField != null)
        {
            var index:int = getChildIndex(DisplayObject(textField));
            removeTextField();
            createTextField(index);
            invalidateSize();
            textChanged = true;
        }

        if (textChanged)
        {
            // In general, we want the ToolTip style to be applied.
            // However, we don't want leftMargin and rightMargin
            // of the TextField's TextFormat to be set to the
            // paddingLeft and paddingRight of the ToolTip style.
            // We want these styles to affect the space between the
            // TextField and the border, but not the space within
            // the TextField.
            var textFormat:TextFormat = textField.getTextFormat();
            textFormat.leftMargin = 0;
            textFormat.rightMargin = 0;
            textField.defaultTextFormat = textFormat;

            textField.text = _text;
            textChanged = false;
        }
    }

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

        var bm:EdgeMetrics = borderMetrics;

        var leftInset:Number = bm.left + getStyle("paddingLeft");
        var topInset:Number = bm.top + getStyle("paddingTop");
        var rightInset:Number = bm.right + getStyle("paddingRight");
        var bottomInset:Number = bm.bottom + getStyle("paddingBottom");

        var widthSlop:Number = leftInset + rightInset;
        var heightSlop:Number = topInset + bottomInset;

        textField.wordWrap = false;

        if (textField.textWidth + widthSlop > ToolTip.maxWidth)
        {
            textField.width = ToolTip.maxWidth - widthSlop;
            textField.wordWrap = true;
        }

        measuredWidth = textField.width + widthSlop;
        measuredHeight = textField.height + heightSlop;
    }

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

        var bm:EdgeMetrics = borderMetrics;

        var leftInset:Number = bm.left + getStyle("paddingLeft");
        var topInset:Number = bm.top + getStyle("paddingTop");
        var rightInset:Number = bm.right + getStyle("paddingRight");
        var bottomInset:Number = bm.bottom + getStyle("paddingBottom");

        var widthSlop:Number = leftInset + rightInset;
        var heightSlop:Number = topInset + bottomInset;

        border.setActualSize(unscaledWidth, unscaledHeight);

        textField.move(leftInset, topInset);
        textField.setActualSize(unscaledWidth - widthSlop, unscaledHeight - heightSlop);
    }

    /**
     *  @private
     */
    override public function styleChanged(styleProp:String):void
    {
        // This will take care of doing invalidateSize() if styleProp
        // is "styleName" or a registered layout style such as "borderStyle".
        super.styleChanged(styleProp);

        // However, if the borderStyle changes from "errorTipAbove" to
        // "errorTipBelow" or vice versa, the measured size won't change.
        // (The pointy part of the skin simply changes from the bottom
        // to the top or vice versa.) This means that the LayoutManager
        // won't call updateDisplayList() because the size hasn't changed.
        // But the TextField has to be repositioned, so we need to
        // invalidate the layout as well as the size.
        if (styleProp == "styleName" ||
            styleProp == "borderSkin" ||
            styleProp == null)
        {
            //if the border skin has changed then rebuild it.
            if(border)
            {
                removeChild(DisplayObject(border));
                border = null;
            }
            
            createBorder();         
        }
        else if (styleProp == "borderStyle")
        {
            invalidateDisplayList();
        }
    }

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

    /**
     *  @private
     *  Creates the text field child and adds it as a child of this component.
     * 
     *  @param childIndex The index of where to add the child.
     *  If -1, the text field is appended to the end of the list.
     */
    mx_internal function createTextField(childIndex:int):void
    {
        if (!textField)
        {
            textField = IUITextField(createInFontContext(UITextField));

            textField.autoSize = TextFieldAutoSize.LEFT;
            textField.mouseEnabled = false;
            textField.multiline = true;
            textField.selectable = false;
            textField.wordWrap = false;
            textField.styleName = this;
            
            if (childIndex == -1)
                addChild(DisplayObject(textField));
            else 
                addChildAt(DisplayObject(textField), childIndex);
        }
    }

    /**
     *  @private
     *  Removes the text field from this component.
     */
    mx_internal function removeTextField():void
    {
        if (textField)
        {
            removeChild(DisplayObject(textField));
            textField = null;
        }
    }
    

    /**
     *  @private
     */
    mx_internal function getTextField():IUITextField
    {
        return textField;
    }
    
    /**
     *  @private
     */
    private function createBorder():void
    {        
        if (!border)
        {
            var borderClass:Class = getStyle("borderSkin");
            
            if (borderClass != null)
            {
                border = new borderClass();
                
                if (border is ISimpleStyleClient)
                    ISimpleStyleClient(border).styleName = this;
                
                // Add the border behind all the children.
                addChildAt(DisplayObject(border), 0);
                
                invalidateDisplayList();
            }
        }
    }
}

}
