/* *
 * (c) 2010-2019 Torstein Honsi
 *
 * License: www.highcharts.com/license
 *
 * Highcharts feature to make the Y axis stay fixed when scrolling the chart
 * horizontally on mobile devices. Supports left and right side axes.
 */

'use strict';

import H from './Globals.js';

var addEvent = H.addEvent,
    Chart = H.Chart;

/**
 * Options for a scrollable plot area. This feature provides a minimum width for
 * the plot area of the chart. If the width gets smaller than this, typically
 * on mobile devices, a native browser scrollbar is presented below the chart.
 * This scrollbar provides smooth scrolling for the contents of the plot area,
 * whereas the title, legend and axes are fixed.
 *
 * @sample {highcharts} highcharts/chart/scrollable-plotarea
 *         Scrollable plot area
 *
 * @since     6.1.0
 * @product   highcharts gantt
 * @apioption chart.scrollablePlotArea
 */

/**
 * The minimum width for the plot area. If it gets smaller than this, the plot
 * area will become scrollable.
 *
 * @type      {number}
 * @apioption chart.scrollablePlotArea.minWidth
 */

/**
 * The initial scrolling position of the scrollable plot area. Ranges from 0 to
 * 1, where 0 aligns the plot area to the left and 1 aligns it to the right.
 * Typically we would use 1 if the chart has right aligned Y axes.
 *
 * @type      {number}
 * @apioption chart.scrollablePlotArea.scrollPositionX
 */

/**
 * The opacity of mask applied on one of the sides of the plot
 * area.
 *
 * @sample {highcharts} highcharts/chart/scrollable-plotarea-opacity
 *         Disabled opacity for the mask
 *
 * @type        {number}
 * @default     0.85
 * @since       7.1.1
 * @apioption   chart.scrollablePlotArea.opacity
 */

addEvent(Chart, 'afterSetChartSize', function (e) {

    var scrollablePlotArea = this.options.chart.scrollablePlotArea,
        scrollableMinWidth =
            scrollablePlotArea && scrollablePlotArea.minWidth,
        scrollablePixels;

    if (scrollableMinWidth && !this.renderer.forExport) {

        // The amount of pixels to scroll, the difference between chart
        // width and scrollable width
        this.scrollablePixels = scrollablePixels = Math.max(
            0,
            scrollableMinWidth - this.chartWidth
        );

        if (scrollablePixels) {
            this.plotWidth += scrollablePixels;
            this.clipBox.width += scrollablePixels;

            if (!e.skipAxes) {
                this.axes.forEach(function (axis) {
                    if (axis.side === 1) {
                        // Get the plot lines right in getPlotLinePath,
                        // temporarily set it to the adjusted plot width.
                        axis.getPlotLinePath = function () {
                            var right = this.right,
                                path;

                            this.right = right - axis.chart.scrollablePixels;
                            path = H.Axis.prototype.getPlotLinePath.apply(
                                this,
                                arguments
                            );
                            this.right = right;
                            return path;
                        };

                    } else {
                        // Apply the corrected plotWidth
                        axis.setAxisSize();
                        axis.setAxisTranslation();
                    }
                });
            }
        }
    }
});

addEvent(Chart, 'render', function () {
    if (this.scrollablePixels) {
        if (this.setUpScrolling) {
            this.setUpScrolling();
        }
        this.applyFixed();

    } else if (this.fixedDiv) { // Has been in scrollable mode
        this.applyFixed();
    }
});

/**
 * @private
 * @function Highcharts.Chart#setUpScrolling
 */
Chart.prototype.setUpScrolling = function () {

    // Add the necessary divs to provide scrolling
    this.scrollingContainer = H.createElement('div', {
        'className': 'highcharts-scrolling'
    }, {
        overflowX: 'auto',
        WebkitOverflowScrolling: 'touch'
    }, this.renderTo);

    this.innerContainer = H.createElement('div', {
        'className': 'highcharts-inner-container'
    }, null, this.scrollingContainer);

    // Now move the container inside
    this.innerContainer.appendChild(this.container);

    // Don't run again
    this.setUpScrolling = null;
};

/**
 * These elements are moved over to the fixed renderer and stay fixed when the
 * user scrolls the chart
 * @private
 */
Chart.prototype.moveFixedElements = function () {
    var container = this.container,
        fixedRenderer = this.fixedRenderer;

    ([
        this.inverted ?
            '.highcharts-xaxis' :
            '.highcharts-yaxis',
        this.inverted ?
            '.highcharts-xaxis-labels' :
            '.highcharts-yaxis-labels',
        '.highcharts-contextbutton',
        '.highcharts-credits',
        '.highcharts-legend',
        '.highcharts-reset-zoom',
        '.highcharts-subtitle',
        '.highcharts-title',
        '.highcharts-legend-checkbox'
    ]).forEach(function (className) {
        [].forEach.call(
            container.querySelectorAll(className),
            function (elem) {
                (
                    elem.namespaceURI === fixedRenderer.SVG_NS ?
                        fixedRenderer.box :
                        fixedRenderer.box.parentNode
                ).appendChild(elem);
                elem.style.pointerEvents = 'auto';
            }
        );
    });
};

/**
 * @private
 * @function Highcharts.Chart#applyFixed
 */
Chart.prototype.applyFixed = function () {
    var fixedRenderer,
        scrollableWidth,
        firstTime = !this.fixedDiv,
        scrollableOptions = this.options.chart.scrollablePlotArea;

    // First render
    if (firstTime) {

        this.fixedDiv = H.createElement(
            'div',
            {
                className: 'highcharts-fixed'
            },
            {
                position: 'absolute',
                overflow: 'hidden',
                pointerEvents: 'none',
                zIndex: 2
            },
            null,
            true
        );
        this.renderTo.insertBefore(
            this.fixedDiv,
            this.renderTo.firstChild
        );
        this.renderTo.style.overflow = 'visible';

        this.fixedRenderer = fixedRenderer = new H.Renderer(
            this.fixedDiv,
            0,
            0
        );

        // Mask
        this.scrollableMask = fixedRenderer.path()
            .attr({
                fill: H.color(
                    this.options.chart.backgroundColor || '#fff'
                ).setOpacity(
                    H.pick(scrollableOptions.opacity, 0.85)
                ).get(),
                zIndex: -1
            })
            .addClass('highcharts-scrollable-mask')
            .add();

        this.moveFixedElements();

        addEvent(this, 'afterShowResetZoom', this.moveFixedElements);
    }

    // Set the size of the fixed renderer to the visible width
    this.fixedRenderer.setSize(
        this.chartWidth,
        this.chartHeight
    );

    // Increase the size of the scrollable renderer and background
    scrollableWidth = this.chartWidth + this.scrollablePixels;
    H.stop(this.container);
    this.container.style.width = scrollableWidth + 'px';
    this.renderer.boxWrapper.attr({
        width: scrollableWidth,
        height: this.chartHeight,
        viewBox: [0, 0, scrollableWidth, this.chartHeight].join(' ')
    });
    this.chartBackground.attr({ width: scrollableWidth });

    // Set scroll position
    if (firstTime) {

        if (scrollableOptions.scrollPositionX) {
            this.scrollingContainer.scrollLeft =
                this.scrollablePixels * scrollableOptions.scrollPositionX;
        }
    }

    // Mask behind the left and right side
    var axisOffset = this.axisOffset,
        maskTop = this.plotTop - axisOffset[0] - 1,
        maskBottom = this.plotTop + this.plotHeight + axisOffset[2],
        maskPlotRight = this.plotLeft + this.plotWidth -
            this.scrollablePixels;

    this.scrollableMask.attr({
        d: this.scrollablePixels ? [
            // Left side
            'M', 0, maskTop,
            'L', this.plotLeft - 1, maskTop,
            'L', this.plotLeft - 1, maskBottom,
            'L', 0, maskBottom,
            'Z',

            // Right side
            'M', maskPlotRight, maskTop,
            'L', this.chartWidth, maskTop,
            'L', this.chartWidth, maskBottom,
            'L', maskPlotRight, maskBottom,
            'Z'
        ] : ['M', 0, 0]
    });
};
