/**
 * The Navigator Container is a component used to lay out the chart and its
 * {@link Ext.chart.navigator.Navigator navigator}, where the navigator is docked
 * to the top/bottom, and the chart fills the rest of the container's space.
 *
 * For example:
 *
 *     @example
 *     Ext.create({
 *         xtype: 'chartnavigator',
 *         renderTo: Ext.getBody(),
 *         width: 600,
 *         height: 400,
 *
 *         chart: {
 *             xtype: 'cartesian',
 *
 *             store: {
 *                 data: (function () {
 *                     var data = [];
 *                     for (var i = 0; i < 360; i++) {
 *                         data.push({
 *                             x: i,
 *                             y: Math.sin(i / 45 * Math.PI)
 *                         });
 *                     }
 *                     return data;
 *                 })()
 *             },
 *             axes: [
 *                 {
 *                     id: 'navigable-axis',
 *
 *                     type: 'numeric',
 *                     position: 'bottom'
 *                 },
 *                 {
 *                     type: 'numeric',
 *                     position: 'left'
 *                 }
 *             ],
 *             series: {
 *                 type: 'line',
 *                 xField: 'x',
 *                 yField: 'y'
 *             }
 *         },
 *
 *         navigator: {
 *             axis: 'navigable-axis'
 *         }
 *     });
 *
 */
Ext.define('Ext.chart.navigator.Container', {
    // We are interested in the docking functionality that's available in
    // the Container in Modern and in the Panel in Classic.
    extend: 'Ext.chart.navigator.ContainerBase',
 
    requires: [
        'Ext.chart.CartesianChart',
        'Ext.chart.navigator.Navigator'
    ],
 
    xtype: 'chartnavigator',
 
    config: {
        /**
         * @cfg {Ext.chart.CartesianChart} chart
         * The chart to make navigable.
         */
        chart: null,
 
        /**
         * @cfg {Ext.chart.navigator.Navigator} navigator
         */
        navigator: {}
    },
 
    layout: 'fit',
 
    applyChart: function (chart, oldChart) {
        if (oldChart) {
            oldChart.destroy();
        }
        if (chart) {
            if (chart.isCartesian) {
                Ext.raise('Only cartesian charts are supported.');
            }
            if (!chart.isChart) {
                chart.$initParent = this;
                chart = new Ext.chart.CartesianChart(chart);
                delete chart.$initParent;
            }
        }
        return chart;
    },
 
    legendStore: null,
    surfaceRects: null,
 
    updateChart: function (chart, oldChart) {
        var me = this;
 
        if (chart) {
            me.legendStore = chart.getLegendStore();
 
            if (!me.items && me.initItems) {
                me.initItems();
            }
            me.add(chart);
        }
    },
 
    applyNavigator: function (navigator, oldNavigator) {
        var instance;
 
        if (oldNavigator) {
            oldNavigator.destroy();
        }
 
        if (navigator) {
            navigator.navigatorContainer = navigator.parent = this;
            instance = new Ext.chart.navigator.Navigator(navigator);
        }
 
        return instance;
    },
 
    preview: function () {
        this.getNavigator().preview(this.getImage());
    },
 
    download: function (config) {
        config = config || {};
        config.data = this.getImage().data;
 
        this.getNavigator().download(config);
    },
 
    setVisibleRange: function (visibleRange) {
        this.getNavigator().setVisibleRange(visibleRange);
    },
 
    getImage: function (format) {
        var me = this,
            chart = me.getChart(),
            navigator = me.getNavigator(),
            docked = navigator.getDocked(),
            chartImageSize = chart.bodyElement.getSize(),
            navigatorImageSize = navigator.bodyElement.getSize(),
            chartSurfaces = chart.getSurfaces(true),
            navigatorSurfaces = navigator.getSurfaces(true),
            size = {
                width: chartImageSize.width,
                height: chartImageSize.height + navigatorImageSize.height
            },
            image, imageElement,
            surfaces, surface;
 
        if (docked === 'top') {
            me.shiftSurfaces(chartSurfaces, 0, navigatorImageSize.height);
        } else {
            me.shiftSurfaces(navigatorSurfaces, 0, chartImageSize.height);
        }
 
        surfaces = chartSurfaces.concat(navigatorSurfaces);
        surface = surfaces[0];
 
        if ((Ext.isIE || Ext.isEdge) && surface.isSVG) {
            // SVG data URLs don't work in IE/Edge as a source for an 'img' element,
            // so we need to render SVG the usual way.
            image = {
                data: surface.toSVG(size, surfaces),
                type: 'svg-markup'
            };
        } else {
            image = surface.flatten(size, surfaces);
 
            if (format === 'image') {
                imageElement = new Image();
                imageElement.src = image.data;
                image.data = imageElement;
                return image;
            }
            if (format === 'stream') {
                image.data = image.data.replace(/^data:image\/[^;]+/, 'data:application/octet-stream');
                return image;
            }
        }
 
        me.unshiftSurfaces(surfaces);
 
        return image;
    },
 
    shiftSurfaces: function (surfaces, x, y) {
        var ln = surfaces.length,
            i = 0,
            surface;
 
        this.surfaceRects = {};
 
        for (; i < ln; i++) {
            surface = surfaces[i];
            this.shiftSurface(surface, x, y);
        }
    },
 
    shiftSurface: function (surface, x, y) {
        var rect = surface.getRect();
 
        this.surfaceRects[surface.getId()] = rect.slice();
 
        rect[0] += x;
        rect[1] += y;
    },
 
    unshiftSurfaces: function (surfaces) {
        var rects = this.surfaceRects,
            ln = surfaces.length,
            i = 0,
            surface, rect, oldRect;
 
        if (rects) {
            for (; i < ln; i++) {
                surface = surfaces[i];
                rect = surface.getRect();
                oldRect = rects[surface.getId()];
                if (oldRect) {
                    rect[0] = oldRect[0];
                    rect[1] = oldRect[1];
                }
            }
        }
 
        this.surfaceRects = null;
    }
 
});