////////////////////////////////////////////////////////////////////////////////
//
//  ADOBE SYSTEMS INCORPORATED
//  Copyright 2003-2006 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.events.Event;

import mx.accessibility.AccConst;
import mx.collections.CursorBookmark;
import mx.collections.ICollectionView;
import mx.collections.IViewCursor;
import mx.controls.Tree;
import mx.controls.listClasses.IListItemRenderer;
import mx.controls.treeClasses.HierarchicalCollectionView;
import mx.core.UIComponent;
import mx.core.mx_internal;
import mx.events.TreeEvent;

use namespace mx_internal;

/**
 *  TreeAccImpl is a subclass of AccessibilityImplementation
 *  which implements accessibility for the Tree class.
 *  
 *  @langversion 3.0
 *  @playerversion Flash 9
 *  @playerversion AIR 1.1
 *  @productversion Flex 3
 */
public class TreeAccImpl extends AccImpl
{
    include "../core/Version.as";

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

	/**
	 *  Enables accessibility in the Tree class.
	 * 
	 *  <p>This method is called by application startup code
	 *  that is autogenerated by the MXML compiler.
	 *  Afterwards, when instances of Tree are initialized,
	 *  their <code>accessibilityImplementation</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
	{
		Tree.createAccessibilityImplementation =
			createAccessibilityImplementation;
	}

	/**
	 *  @private
	 *  Creates a Tree's AccessibilityImplementation object.
	 *  This method is called from UIComponent's
	 *  initializeAccessibility() method.
	 */
	mx_internal static function createAccessibilityImplementation(
								component:UIComponent):void
	{
		component.accessibilityImplementation =
			new TreeAccImpl(component);
	}

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

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

		role = AccConst.ROLE_SYSTEM_OUTLINE;
	}

	//--------------------------------------------------------------------------
	//
	//  Overridden properties: AccImpl
	//
	//--------------------------------------------------------------------------

	//----------------------------------
	//  eventsToHandle
	//----------------------------------

	/**
	 *  @private
	 *	Array of events that we should listen for from the master component.
	 */
	override protected function get eventsToHandle():Array
	{
		return super.eventsToHandle.concat(
			[ "change", TreeEvent.ITEM_OPEN, TreeEvent.ITEM_CLOSE ]);
	}
	
	//--------------------------------------------------------------------------
	//
	//  Overridden methods: AccessibilityImplementation
	//
	//--------------------------------------------------------------------------

	/**
	 *  @private
	 *  Gets the role for the component.
	 *
	 *  @param childID children of the component
	 */
	override public function get_accRole(childID:uint):uint
	{
		return childID == 0 ? role : AccConst.ROLE_SYSTEM_OUTLINEITEM;
	}

	/**
	 *  @private
	 *  IAccessible method for returning the value of the TreeItem/Tree
	 *  which is spoken out by the screen reader
	 *  A Tree item reports its depth as its value.
	 *  The Tree itself reports no value.
	 *
	 *  @param childID uint
	 *
	 *  @return Name String
	 */
	override public function get_accValue(childID:uint):String
	{
		var accValue:String;
		
		if (childID != 0)
		{
			// Assuming childID is always ItemID + 1
			// because getChildIDArray is not always invoked.
			var index:int = childID - 1;
			var item:Object = getItemAt(index);
			if (!item)
				return accValue;
			
			var tree:Tree = Tree(master);
			var depth:int = tree.getItemDepth(
				item, index - tree.verticalScrollPosition);
			accValue = String(depth - 1);
		}

		return accValue;
	}

	/**
	 *  @private
	 *  IAccessible method for returning the state of the TreeItem.
	 *  States are predefined for all the components in MSAA.
	 *  Values are assigned to each state.
	 *  Depending upon the treeItem being Selected, Selectable,
	 *  Invisible, Offscreen, a value is returned.
	 *
	 *  @param childID uint
	 *
	 *  @return State uint
	 */
	override public function get_accState(childID:uint):uint
	{
		var accState:uint = getState(childID);
		
		if (childID > 0)
		{
			var tree:Tree = Tree(master);

			var index:int = childID - 1;

			// For returning states (OffScreen and Invisible)
			// when the list Item is not in the displayed rows.
			if (index < tree.verticalScrollPosition ||
				index >= tree.verticalScrollPosition + tree.rowCount)
			{
				accState |= AccConst.STATE_SYSTEM_INVISIBLE;
			}
			else
			{
				accState |= AccConst.STATE_SYSTEM_SELECTABLE;

				var item:Object = getItemAt(index);

				if (item && tree.dataDescriptor.isBranch(item, tree.dataProvider))
				{
					if (tree.isItemOpen(item))
						accState |= AccConst.STATE_SYSTEM_EXPANDED;
					else
						accState |= AccConst.STATE_SYSTEM_COLLAPSED;
				}

				var renderer:IListItemRenderer =
					tree.itemToItemRenderer(item);

				if (renderer != null && tree.isItemSelected(renderer.data))
					accState |= AccConst.STATE_SYSTEM_SELECTED | AccConst.STATE_SYSTEM_FOCUSED;
			}
		}
		return accState;
	}

	/**
	 *  @private
	 *  IAccessible method for returning the Default Action.
	 *
	 *  @param childID uint
	 *
	 *  @return name of default action.
	 */
	override public function get_accDefaultAction(childID:uint):String
	{
		if (childID == 0)
			return null;

		var tree:Tree = Tree(master);

		var item:Object = getItemAt(childID - 1);
		if (!item)
			return null;
		
		if (tree.dataDescriptor.isBranch(item, tree.dataProvider))
			return tree.isItemOpen(item) ? "Collapse" : "Expand";

		return null;
	}

	/**
	 *  @private
	 *  IAccessible method for executing the Default Action.
	 *
	 *  @param childID uint
	 */
	override public function accDoDefaultAction(childID:uint):void
	{
		var tree:Tree = Tree(master);

		if (childID == 0 || !tree.enabled)
			return;

		var item:Object = getItemAt(childID - 1);
		if (!item)
			return;
		
		if (tree.dataDescriptor.isBranch(item, tree.dataProvider))
			tree.expandItem(item, !tree.isItemOpen(item)); 
	}

 	/**
	 *  @private
	 *  Method to return an array of childIDs.
	 *
	 *  @return Array
	 */
	override public function getChildIDArray():Array
	{
		var n:int = Tree(master).dataProvider ?
					Tree(master).collectionLength :
					0;

		return createChildIDArray(n);
	}
	
	/**
	 *  @private
	 *  IAccessible method for returning the bounding box of the TreeItem.
	 *
	 *  @param childID uint
	 *
	 *  @return Location Object
	 */
	override public function accLocation(childID:uint):*
	{
		var tree:Tree = Tree(master);
		
		var index:int = childID - 1;
		
		if (index < tree.verticalScrollPosition ||
			index >= tree.verticalScrollPosition + tree.rowCount)
		{
			return null;
		}

		return tree.itemToItemRenderer(getItemAt(index));
	}

	/**
	 *  @private
	 *  IAccessible method for returning the childFocus of the List.
	 *
	 *  @param childID uint
	 *
	 *  @return focused childID.
	 */
	override public function get_accFocus():uint
	{
		var index:int = Tree(master).selectedIndex;
		
		return index >= 0 ? index + 1 : 0;
	}

	//--------------------------------------------------------------------------
	//
	//  Overridden methods: AccImpl
	//
	//--------------------------------------------------------------------------

	/**
	 *  @private
	 *  method for returning the name of the TreeItem/Tree
	 *  which is spoken out by the screen reader
	 *  The TreeItem should return the label as the name
	 *  with m of n string with level info and
	 *  Tree should return the name specified in the Accessibility Panel.
	 *
	 *  @param childID uint
	 *
	 *  @return Name String
	 */
	override protected function getName(childID:uint):String
	{
		if (childID == 0)
			return "";

		var tree:Tree = Tree(master);
		
		var item:Object = getItemAt(childID - 1);

		var name:String = "";
		
		if (!item)
			return name;
		
		if (tree.itemToLabel(item))
			name = tree.itemToLabel(item);

		return name;
	}

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

	/**
	 *  @private
	 */
	private function getItemAt(index:int):Object
	{
		var iterator:IViewCursor = Tree(master).collectionIterator;
		iterator.seek(CursorBookmark.FIRST, index);
		return iterator.current;
	}

	//--------------------------------------------------------------------------
	//
	//  Overridden event handlers: AccImpl
	//
	//--------------------------------------------------------------------------

	/**
	 *  @private
	 *  Override the generic event handler.
	 *  All AccImpl must implement this
	 *  to listen for events from its master component. 
	 */
	override protected function eventHandler(event:Event):void
	{
		// Let AccImpl class handle the events
		// that all accessible UIComponents understand.
		$eventHandler(event);

		var index:int = Tree(master).selectedIndex;
		
		var childID:uint = index + 1;

		switch (event.type)
		{
			case "change":
			{
				if (index >= 0)
				{
					Accessibility.sendEvent(master, childID,
											AccConst.EVENT_OBJECT_FOCUS);

					Accessibility.sendEvent(master, childID,
											AccConst.EVENT_OBJECT_SELECTION);
				}
				break;
			}
										
			case TreeEvent.ITEM_OPEN:
			case TreeEvent.ITEM_CLOSE:
			{
				if (index >= 0)
				{
					Accessibility.sendEvent(master, childID,
											AccConst.EVENT_OBJECT_STATECHANGE);
				}
				break;
			}
		}
	}
}

}
