////////////////////////////////////////////////////////////////////////////////
//
//  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.accessibility
{

import flash.accessibility.Accessibility;
import flash.accessibility.AccessibilityProperties;
import flash.events.Event;
import flash.system.ApplicationDomain;

import mx.accessibility.AccImpl;
import mx.core.UIComponent;
import mx.core.mx_internal;

use namespace mx_internal;

/**
 *  UIComponentAccProps is a subclass of AccessibilityProperties
 *  for use by various UIComponents.
 *  It is used to provide accessibility to Form, ToolTip, and Error ToolTip.
 *  
 *  @langversion 3.0
 *  @playerversion Flash 9
 *  @playerversion AIR 1.1
 *  @productversion Flex 3
 */
public class UIComponentAccProps extends AccessibilityProperties
{
    include "../core/Version.as";

    //--------------------------------------------------------------------------
    //
    //  Class methods
    //
    //--------------------------------------------------------------------------

    /**
     *  Enables accessibility in the UIComponent class.
     * 
     *  <p>This method is called by application startup code
     *  that is autogenerated by the MXML compiler.
     *  Afterwards, when instances of UIComponent are initialized,
     *  their <code>accessibilityProperties</code> property
     *  will be set to an instance of this class.</p>
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public static function enableAccessibility():void
    {
        UIComponent.createAccessibilityImplementation =
            createAccessibilityImplementation;
    }

    /**
     *  @private
     *  Creates a UIComponent's AccessibilityProperties object.
     *  This method is called from UIComponent's
     *  initializeAccessibility() method.
     */
    mx_internal static function createAccessibilityImplementation(
                                            component:UIComponent):void
    {
        component.accessibilityProperties =
            new UIComponentAccProps(component);
    }
    
    /**
     *  @private
     *  Determines whether a component should avoid producing MSAA information
     *  directly.  Components that are represented otherwise, such as
     *  a form field's Required Field indicator (which causes "Required field"
     *  to be included in the field's accessible name) should be made silent
     *  in this way.
     *
     *  @param component The component to check.
     *
     *  @return true if this component should be silent.
     */
    mx_internal static function componentShouldBeSilent(component:UIComponent):Boolean
    {
        // All tests below require Group to exist and to be under a FormItem.
        var groupClass:Class = Class(AccImpl.getDefinition(
            "spark.components.Group", component.moduleFactory
        ));
        if (!groupClass)
            return false;
        var formItem:UIComponent = AccImpl.findMatchingAncestor(component, AccImpl.isFormItem);
        if (!formItem)
            return false;

        // This catches labels for FormItem fields,
        // and also one object related to the Required Field indicator.
        // TODO:  We might want to silence all Group components under a FormItem,
        // but this theory remains to be tested and so is not implemented here.
        // component.parent should be a FormItem skin (possibly a custom one),
        // so the parent of that should be the FormItem.
        // The try/catch block prevents an RTE if
        // component.parent.parent doesn't exist.
        try
        {
            if (component is groupClass
            && component.parent.parent === formItem)
                return true;
        }
        catch (e:Error)
        {
        }

        // This catches the Required Field graphic itself.
        // Sought structure: Image in Group in skin in FormItem.
        var imageClass:Class = Class(AccImpl.getDefinition(
            "spark.components.Image", component.moduleFactory
        ));
        if (!imageClass)
            return false;
        try
        {
        if (component is imageClass && component.parent is groupClass
        && component.parent.parent.parent === formItem)
            return true;
        }
        catch (e:Error)
        {
        }

        return false;
    }

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

    /**
     *  Constructor.
     *
     *  @param master The UIComponent instance that this
     *  AccessibilityProperties instance is making accessible.
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function UIComponentAccProps(component:UIComponent)
    {
        super();

        master = component;
        
        if (component.accessibilityProperties)
        {
            silent = component.accessibilityProperties.silent;
            
            forceSimple = component.accessibilityProperties.forceSimple;
            
            noAutoLabeling = component.accessibilityProperties.noAutoLabeling;
            
            if (component.accessibilityProperties.name)
                name = component.accessibilityProperties.name;
            
            if (component.accessibilityProperties.description)
                description = component.accessibilityProperties.description;
            
            if (component.accessibilityProperties.shortcut)
                shortcut = component.accessibilityProperties.shortcut;
        }
        
        if (AccImpl.getMatchingDefinition(master,
            AccImpl.getDefinitions("ScrollBar", master.moduleFactory)
        ))
        {
            silent = true;
            return;
        }

        if (isFormItemLabel(master))
        {
            // TODO:  Why is name set here?  Is this name used somewhere?
            name = AccImpl.getFormName(master);
            silent = true;
            return;
        }

        // Silence various subparts of Spark forms besides FormItem labels.
        if (componentShouldBeSilent(component))
        {
            // We can't set silent=true or whole FormItems and their fields
            // will disappear from MSAA, but if we set name="", the Player
            // will filter out these items for us.
            name = "";
            return;
        }

        // In complex layouts, the text of a FormHeading might not appear
        // where it should for assistive technology, because the FormHeading's
        // tabIndex does not also get applied to the text item.
        // That is fixed here when the FormHeading is being constructed.
        if (AccImpl.isFormHeading(master) && master.tabIndex > 0)
        {
            // Spark-specific solution.
            try
            {
                if (Object(master).labelDisplay.tabIndex == -1)
                    Object(master).labelDisplay.tabIndex = master.tabIndex;
            }
            catch (e:Error)
            {
            }
            // TODO:  Not solved for MX even though the problem does apply there.
        }

        var formName:String = AccImpl.getFormName(master);
        if (formName && formName.length != 0)
            name = formName + name;  

        if (master.toolTip && master.toolTip.length != 0)
            if (!component.accessibilityProperties || (component.accessibilityProperties && !component.accessibilityProperties.name))
        {
            oldToolTip = " " + master.toolTip;
            name += oldToolTip;
        }

        if (master.errorString && master.errorString.length != 0)
        {
            oldErrorString = " " + master.errorString;
            name += oldErrorString;
        }

        master.addEventListener("toolTipChanged", eventHandler);
        master.addEventListener("errorStringChanged", eventHandler);
    }

    /**
     *  @private
     *  Determines whether a component is a formItem label (Spark or MX).
     *
     *  @param component The component to check.
     *
     *  @return true if this is a label for a formItem.
     */
    protected function isFormItemLabel(component:UIComponent):Boolean
    {
        // This handles Spark labels on forms.
        var thisName:String = master.name;
        if (thisName == "labelDisplay"
            || thisName == "sequenceLabelDisplay"
            || thisName == "helpContentGroup"
            || thisName == "errorTextDisplay"
        )
            return true;

        // This handles MX FormItemLabel objects.
        return Boolean(AccImpl.getMatchingDefinition(master,
            AccImpl.getDefinitions("FormItemLabel", master.moduleFactory)
        ));
    }

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

    /**
     *  @private
     */
    private var oldToolTip:String;
    
    /**
     *  @private
     */
    private var oldErrorString:String;
    
    //--------------------------------------------------------------------------
    //
    //  Properties
    //
    //--------------------------------------------------------------------------

    //----------------------------------
    //  master
    //----------------------------------

    /**
     *  A reference to the UIComponent itself.
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    protected var master:UIComponent;
    
    //--------------------------------------------------------------------------
    //
    //  Event handlers
    //
    //--------------------------------------------------------------------------

    /**
     *  Generic event handler.
     *  All UIComponentAccProps subclasses must implement this
     *  to listen for events from its master component. 
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    protected function eventHandler(event:Event):void
    {
        var pos:int;

        switch (event.type)
        {
            case "errorStringChanged":
            {
                if (name && name.length != 0 && oldErrorString)
                {
                    pos = name.indexOf(oldErrorString);
                    if (pos != -1)
                    {
                        name = name.substring(0, pos) +
                               name.substring(pos + oldErrorString.length);
                    }
                    oldErrorString = null;
                }

                if (master.errorString && master.errorString.length != 0)
                {
                    if (!name)
                        name = "";

                    oldErrorString = " " + master.errorString;
                    name += oldErrorString;
                }

                Accessibility.updateProperties();
                break;
            }

            case "toolTipChanged":
            {
                if (name && name.length != 0 && oldToolTip)
                {
                    pos = name.indexOf(oldToolTip);
                    if (pos != -1)
                    {
                        name = name.substring(0, pos) +
                               name.substring(pos + oldToolTip.length);
                    }
                    oldToolTip = null;
                }

                if (master.toolTip && master.toolTip.length != 0)
                {
                    if (!master.accessibilityProperties || (master.accessibilityProperties && !master.accessibilityProperties.name))
                    {
                        if (!name)
                            name = "";

                        oldToolTip = " " + master.toolTip;
                        name += oldToolTip;
                    }
                }

                Accessibility.updateProperties();
                break;
            }
        }
    }
}

}
