/**
 * @private
 */
Ext.define('Ext.menu.KeyNav', {
    extend: 'Ext.util.KeyNav',
    
    constructor: function(config) {
        var me = this;
 
        me.menu = config.target;
        me.callParent([Ext.apply({
            down: me.down,
            enter: me.enter,
            esc: me.escape,
            left: me.left,
            right: me.right,
            space: me.enter,
            tab: me.tab,
            up: me.up
        }, config)]);
    },
 
    down: function(e) {
        var me = this,
            fi = me.menu.focusedItem;
 
        if (fi && e.getKey() == e.DOWN && me.isWhitelisted(fi)) {
            return true;
        }
        me.focusNextItem(1);
    },
 
    enter: function(e) {
        var menu = this.menu,
            focused = menu.focusedItem;
 
        if (menu.activeItem) {
            menu.onClick(e);
        } else if (focused && focused.isFormField) {
            // prevent stopEvent being called 
            return true;
        }
    },
 
    escape: function(e) {
        Ext.menu.Manager.hideAll();
    },
 
    focusNextItem: function(step) {
        var menu = this.menu,
            items = menu.items,
            focusedItem = menu.focusedItem,
            startIdx = focusedItem ? items.indexOf(focusedItem) : -1,
            idx = startIdx + step,
            len = items.length,
            count = 0,
            item;
 
        // Limit the count, since we might not be able to find something to focus 
        while (count < len && idx !== startIdx) {
            if (idx < 0) {
                idx = len - 1;
            } else if (idx >= len) {
                idx = 0;
            }
 
            item = items.getAt(idx);
            if (menu.canActivateItem(item)) {
                menu.setActiveItem(item);
                break;
            }
            idx += step;
            ++count;
        }
    },
 
    isWhitelisted: function(item) {
        return !!item.needArrowKeys;
    },
 
    left: function(e) {
        var menu = this.menu,
            fi = menu.focusedItem;
 
        if (fi && this.isWhitelisted(fi)) {
            return true;
        }
 
        if (menu.parentMenu) {
            menu.hide();
            menu.parentMenu.focus();
        }
    },
 
    right: function(e) {
        var menu = this.menu,
            fi = menu.focusedItem,
            ai = menu.activeItem,
            am;
 
        if (fi && this.isWhitelisted(fi)) {
            return true;
        }
 
        if (ai) {
            am = menu.activeItem.menu;
            if (am) {
                ai.expandMenu(0);
                am.setActiveItem(am.child(':focusable'));
            }
        }
    },
 
    tab: function(e) {
        var me = this;
 
        if (e.shiftKey) {
            me.up(e);
        } else {
            me.down(e);
        }
    },
 
    up: function(e) {
        var me = this,
            fi = me.menu.focusedItem;
 
        if (fi && e.getKey() == e.UP && me.isWhitelisted(fi)) {
            return true;
        }
        me.focusNextItem(-1);
    }
});