import Cartesian3D from './grid3D/Cartesian3D';
import Axis3D from './grid3D/Axis3D';
import * as echarts from 'echarts/lib/echarts';
import {getLayoutRect} from 'echarts/lib/util/layout';
import ViewGL from '../core/ViewGL';
import retrieve from '../util/retrieve';

function resizeCartesian3D(grid3DModel, api) {
    // Use left/top/width/height
    var boxLayoutOption = grid3DModel.getBoxLayoutParams();

    var viewport = getLayoutRect(boxLayoutOption, {
        width: api.getWidth(),
        height: api.getHeight()
    });

    // Flip Y
    viewport.y = api.getHeight() - viewport.y - viewport.height;

    this.viewGL.setViewport(viewport.x, viewport.y, viewport.width, viewport.height, api.getDevicePixelRatio());

    var boxWidth = grid3DModel.get('boxWidth');
    var boxHeight = grid3DModel.get('boxHeight');
    var boxDepth = grid3DModel.get('boxDepth');

    if (process.env.NODE_ENV !== 'production') {
        ['x', 'y', 'z'].forEach(function (dim) {
            if (!this.getAxis(dim)) {
                throw new Error('Grid' + grid3DModel.id + ' don\'t have ' + dim + 'Axis');
            }
        }, this);
    }
    this.getAxis('x').setExtent(-boxWidth / 2, boxWidth / 2);
    // From near to far
    this.getAxis('y').setExtent(boxDepth / 2, -boxDepth / 2);
    this.getAxis('z').setExtent(-boxHeight / 2, boxHeight / 2);

    this.size = [boxWidth, boxHeight, boxDepth];
}

function updateCartesian3D(ecModel, api) {
    var dataExtents = {};
    function unionDataExtents(dim, extent) {
        dataExtents[dim] = dataExtents[dim] || [Infinity, -Infinity];
        dataExtents[dim][0] = Math.min(extent[0], dataExtents[dim][0]);
        dataExtents[dim][1] = Math.max(extent[1], dataExtents[dim][1]);
    }
    // Get data extents for scale.
    ecModel.eachSeries(function (seriesModel) {
        if (seriesModel.coordinateSystem !== this) {
            return;
        }
        var data = seriesModel.getData();
        ['x', 'y', 'z'].forEach(function (coordDim) {
            data.mapDimensionsAll(coordDim, true).forEach(function (dataDim) {
                unionDataExtents(
                    coordDim, data.getDataExtent(dataDim, true)
                );
            });
        });
    }, this);

    ['xAxis3D', 'yAxis3D', 'zAxis3D'].forEach(function (axisType) {
        ecModel.eachComponent(axisType, function (axisModel) {
            var dim = axisType.charAt(0);
            var grid3DModel = axisModel.getReferringComponents('grid3D').models[0];

            var cartesian3D = grid3DModel.coordinateSystem;
            if (cartesian3D !== this) {
                return;
            }

            var axis = cartesian3D.getAxis(dim);
            if (axis) {
                if (process.env.NODE_ENV !== 'production') {
                    console.warn('Can\'t have two %s in one grid3D', axisType);
                }
                return;
            }
            var scale = echarts.helper.createScale(
                dataExtents[dim] || [Infinity, -Infinity], axisModel
            );
            axis = new Axis3D(dim, scale);
            axis.type = axisModel.get('type');
            var isCategory = axis.type === 'category';
            axis.onBand = isCategory && axisModel.get('boundaryGap');
            axis.inverse = axisModel.get('inverse');

            axisModel.axis = axis;
            axis.model = axisModel;

            // override `echarts/coord/Axis#getLabelModel`
            axis.getLabelModel = function () {
                return axisModel.getModel('axisLabel', grid3DModel.getModel('axisLabel'));
            };
            // override `echarts/coord/Axis#getTickModel`
            axis.getTickModel = function () {
                return axisModel.getModel('axisTick', grid3DModel.getModel('axisTick'));
            };

            cartesian3D.addAxis(axis);
        }, this);
    }, this);

    this.resize(this.model, api);
}

var grid3DCreator = {

    dimensions: Cartesian3D.prototype.dimensions,

    create: function (ecModel, api) {

        var cartesian3DList = [];

        ecModel.eachComponent('grid3D', function (grid3DModel) {
            // FIXME
            grid3DModel.__viewGL = grid3DModel.__viewGL || new ViewGL();

            var cartesian3D = new Cartesian3D();
            cartesian3D.model = grid3DModel;
            cartesian3D.viewGL = grid3DModel.__viewGL;

            grid3DModel.coordinateSystem = cartesian3D;
            cartesian3DList.push(cartesian3D);

            // Inject resize and update
            cartesian3D.resize = resizeCartesian3D;

            cartesian3D.update = updateCartesian3D;
        });

        var axesTypes = ['xAxis3D', 'yAxis3D', 'zAxis3D'];
        function findAxesModels(seriesModel, ecModel) {
            return axesTypes.map(function (axisType) {
                var axisModel = seriesModel.getReferringComponents(axisType).models[0];
                if (axisModel == null) {
                    axisModel = ecModel.getComponent(axisType);
                }
                if (process.env.NODE_ENV !== 'production') {
                    if (!axisModel) {
                        throw new Error(axisType + ' "' + retrieve.firstNotNull(
                            seriesModel.get(axisType + 'Index'),
                            seriesModel.get(axisType + 'Id'),
                            0
                        ) + '" not found');
                    }
                }
                return axisModel;
            });
        }

        ecModel.eachSeries(function (seriesModel) {
            if (seriesModel.get('coordinateSystem') !== 'cartesian3D') {
                return;
            }
            var firstGridModel = seriesModel.getReferringComponents('grid3D').models[0];

            if (firstGridModel == null) {
                var axesModels = findAxesModels(seriesModel, ecModel);
                var firstGridModel = axesModels[0].getCoordSysModel();
                axesModels.forEach(function (axisModel) {
                    var grid3DModel = axisModel.getCoordSysModel();
                    if (process.env.NODE_ENV !== 'production') {
                        if (!grid3DModel) {
                            throw new Error(
                                'grid3D "' + retrieve.firstNotNull(
                                    axisModel.get('gridIndex'),
                                    axisModel.get('gridId'),
                                    0
                                ) + '" not found'
                            );
                        }
                        if (grid3DModel !== firstGridModel) {
                            throw new Error('xAxis3D, yAxis3D, zAxis3D must use the same grid');
                        }
                    }
                });
            }

            var coordSys = firstGridModel.coordinateSystem;
            seriesModel.coordinateSystem = coordSys;
        });

        return cartesian3DList;
    }
};

export default grid3DCreator;