/* *
 *
 *  (c) 2009-2019 Øystein Moseng
 *
 *  Accessibility component for exporting menu.
 *
 *  License: www.highcharts.com/license
 *
 * */

'use strict';

import H from '../../../parts/Globals.js';
import U from '../../../parts/Utilities.js';
var extend = U.extend;

import AccessibilityComponent from '../AccessibilityComponent.js';
import KeyboardNavigationHandler from '../KeyboardNavigationHandler.js';


/**
 * Show the export menu and focus the first item (if exists).
 *
 * @private
 * @function Highcharts.Chart#showExportMenu
 */
H.Chart.prototype.showExportMenu = function () {
    if (this.exportSVGElements && this.exportSVGElements[0]) {
        this.exportSVGElements[0].element.onclick();
        this.highlightExportItem(0);
    }
};


/**
 * @private
 * @function Highcharts.Chart#hideExportMenu
 */
H.Chart.prototype.hideExportMenu = function () {
    var chart = this,
        exportList = chart.exportDivElements;

    if (exportList && chart.exportContextMenu) {
        // Reset hover states etc.
        exportList.forEach(function (el) {
            if (el.className === 'highcharts-menu-item' && el.onmouseout) {
                el.onmouseout();
            }
        });
        chart.highlightedExportItemIx = 0;
        // Hide the menu div
        chart.exportContextMenu.hideMenu();
        // Make sure the chart has focus and can capture keyboard events
        chart.container.focus();
    }
};


/**
 * Highlight export menu item by index.
 *
 * @private
 * @function Highcharts.Chart#highlightExportItem
 *
 * @param {number} ix
 *
 * @return {true|undefined}
 */
H.Chart.prototype.highlightExportItem = function (ix) {
    var listItem = this.exportDivElements && this.exportDivElements[ix],
        curHighlighted =
            this.exportDivElements &&
            this.exportDivElements[this.highlightedExportItemIx],
        hasSVGFocusSupport;

    if (
        listItem &&
        listItem.tagName === 'DIV' &&
        !(listItem.children && listItem.children.length)
    ) {
        // Test if we have focus support for SVG elements
        hasSVGFocusSupport = !!(
            this.renderTo.getElementsByTagName('g')[0] || {}
        ).focus;

        // Only focus if we can set focus back to the elements after
        // destroying the menu (#7422)
        if (listItem.focus && hasSVGFocusSupport) {
            listItem.focus();
        }
        if (curHighlighted && curHighlighted.onmouseout) {
            curHighlighted.onmouseout();
        }
        if (listItem.onmouseover) {
            listItem.onmouseover();
        }
        this.highlightedExportItemIx = ix;
        return true;
    }
};


/**
 * Try to highlight the last valid export menu item.
 *
 * @private
 * @function Highcharts.Chart#highlightLastExportItem
 */
H.Chart.prototype.highlightLastExportItem = function () {
    var chart = this,
        i;

    if (chart.exportDivElements) {
        i = chart.exportDivElements.length;
        while (i--) {
            if (chart.highlightExportItem(i)) {
                return true;
            }
        }
    }
    return false;
};


/**
 * @private
 * @param {Highcharts.Chart} chart
 */
function exportingShouldHaveA11y(chart) {
    var exportingOpts = chart.options.exporting;
    return exportingOpts &&
        exportingOpts.enabled !== false &&
        exportingOpts.accessibility &&
        exportingOpts.accessibility.enabled &&
        chart.exportSVGElements &&
        chart.exportSVGElements[0] &&
        chart.exportSVGElements[0].element;
}


/**
 * The MenuComponent class
 *
 * @private
 * @class
 * @name Highcharts.MenuComponent
 */
var MenuComponent = function () {};
MenuComponent.prototype = new AccessibilityComponent();
extend(MenuComponent.prototype, /** @lends Highcharts.MenuComponent */ {

    /**
     * Init the component
     */
    init: function () {
        var chart = this.chart,
            component = this;

        this.addEvent(chart, 'exportMenuShown', function () {
            component.onMenuShown();
        });

        this.addEvent(chart, 'exportMenuHidden', function () {
            component.onMenuHidden();
        });
    },


    /**
     * @private
     */
    onMenuHidden: function () {
        var menu = this.chart.exportContextMenu;
        if (menu) {
            menu.setAttribute('aria-hidden', 'true');
        }
        this.setExportButtonExpandedState('false');
    },


    /**
     * @private
     */
    onMenuShown: function () {
        var menu = this.chart.exportContextMenu;
        if (menu) {
            this.addAccessibleContextMenuAttribs();
            this.unhideElementFromScreenReaders(menu);
            this.chart.highlightExportItem(0);
        }
        this.setExportButtonExpandedState('true');
    },


    /**
     * @private
     * @param {string} stateStr
     */
    setExportButtonExpandedState: function (stateStr) {
        var button = this.exportButtonProxy;
        if (button) {
            button.setAttribute('aria-expanded', stateStr);
        }
    },


    /**
     * Called on each render of the chart. We need to update positioning of the
     * proxy overlay.
     */
    onChartRender: function () {
        var chart = this.chart,
            a11yOptions = chart.options.accessibility;

        // Always start with a clean slate
        this.removeElement(this.exportProxyGroup);

        // Set screen reader properties on export menu
        if (exportingShouldHaveA11y(chart)) {
            // Proxy button and group
            this.exportProxyGroup = this.addProxyGroup(
                // Wrap in a region div if verbosity is high
                a11yOptions.landmarkVerbosity === 'all' ? {
                    'aria-label': chart.langFormat(
                        'accessibility.exporting.exportRegionLabel',
                        { chart: chart }
                    ),
                    'role': 'region'
                } : null
            );

            var button = this.chart.exportSVGElements[0];
            this.exportButtonProxy = this.createProxyButton(
                button,
                this.exportProxyGroup,
                {
                    'aria-label': chart.langFormat(
                        'accessibility.exporting.menuButtonLabel',
                        { chart: chart }
                    ),
                    'aria-expanded': 'false'
                }
            );
        }
    },


    /**
     * @private
     */
    addAccessibleContextMenuAttribs: function () {
        var chart = this.chart,
            exportList = chart.exportDivElements;

        if (exportList && exportList.length) {
            // Set tabindex on the menu items to allow focusing by script
            // Set role to give screen readers a chance to pick up the contents
            exportList.forEach(function (item) {
                if (item.tagName === 'DIV' &&
                    !(item.children && item.children.length)) {
                    item.setAttribute('role', 'listitem');
                    item.setAttribute('tabindex', 0);
                } else {
                    item.setAttribute('aria-hidden', 'true');
                }
            });

            // Set accessibility properties on parent div
            var parentDiv = exportList[0].parentNode;
            parentDiv.setAttribute('role', 'list');
            parentDiv.removeAttribute('aria-hidden');
            parentDiv.setAttribute(
                'aria-label',
                chart.langFormat(
                    'accessibility.exporting.chartMenuLabel', { chart: chart }
                )
            );
        }
    },


    /**
     * Get keyboard navigation handler for this component.
     * @return {Highcharts.KeyboardNavigationHandler}
     */
    getKeyboardNavigation: function () {
        var keys = this.keyCodes,
            chart = this.chart,
            component = this;

        return new KeyboardNavigationHandler(chart, {
            keyCodeMap: [
                // Arrow prev handler
                [[keys.left, keys.up], function () {
                    component.onKbdPrevious(this);
                }],

                // Arrow next handler
                [[keys.right, keys.down], function () {
                    component.onKbdNext(this);
                }],

                // Click handler
                [[keys.enter, keys.space], function () {
                    component.onKbdClick(this);
                }],

                // ESC handler
                [[keys.esc], function () {
                    return this.response.prev;
                }]
            ],

            // Only run exporting navigation if exporting support exists and is
            // enabled on chart
            validate: function () {
                return chart.exportChart &&
                    chart.options.exporting.enabled !== false &&
                    chart.options.exporting.accessibility.enabled !== false;
            },

            // Show export menu
            init: function (direction) {
                chart.showExportMenu();

                // If coming back to export menu from other module, try to
                // highlight last item in menu
                if (direction < 0) {
                    chart.highlightLastExportItem();
                }
            },

            // Hide the menu
            terminate: function () {
                chart.hideExportMenu();
            }
        });
    },


    /**
     * @private
     * @param {Highcharts.KeyboardNavigationHandler} keyboardNavigationHandler
     * @return {number} Response code
     */
    onKbdPrevious: function (keyboardNavigationHandler) {
        var chart = this.chart,
            a11yOptions = chart.options.accessibility,
            response = keyboardNavigationHandler.response,
            i = chart.highlightedExportItemIx || 0;

        // Try to highlight prev item in list. Highlighting e.g.
        // separators will fail.
        while (i--) {
            if (chart.highlightExportItem(i)) {
                return response.success;
            }
        }

        // We failed, so wrap around or move to prev module
        if (a11yOptions.keyboardNavigation.wrapAround) {
            chart.highlightLastExportItem();
            return response.success;
        }
        return response.prev;
    },


    /**
     * @private
     * @param {Highcharts.KeyboardNavigationHandler} keyboardNavigationHandler
     * @return {number} Response code
     */
    onKbdNext: function (keyboardNavigationHandler) {
        var chart = this.chart,
            a11yOptions = chart.options.accessibility,
            response = keyboardNavigationHandler.response,
            i = (chart.highlightedExportItemIx || 0) + 1;

        // Try to highlight next item in list. Highlighting e.g.
        // separators will fail.
        for (;i < chart.exportDivElements.length; ++i) {
            if (chart.highlightExportItem(i)) {
                return response.success;
            }
        }

        // We failed, so wrap around or move to next module
        if (a11yOptions.keyboardNavigation.wrapAround) {
            chart.highlightExportItem(0);
            return response.success;
        }
        return response.next;
    },


    /**
     * @private
     * @param {Highcharts.KeyboardNavigationHandler} keyboardNavigationHandler
     * @return {number} Response code
     */
    onKbdClick: function (keyboardNavigationHandler) {
        var chart = this.chart;
        this.fakeClickEvent(
            chart.exportDivElements[chart.highlightedExportItemIx]
        );
        return keyboardNavigationHandler.response.success;
    }

});

export default MenuComponent;
