'use strict';

var Lib = require('../../lib');
var Axes = require('../../plots/cartesian/axes');

var constants = require('./constants');
var helpers = require('./helpers');


module.exports = function calcAutorange(gd) {
    var fullLayout = gd._fullLayout;
    var shapeList = Lib.filterVisible(fullLayout.shapes);

    if(!shapeList.length || !gd._fullData.length) return;

    for(var i = 0; i < shapeList.length; i++) {
        var shape = shapeList[i];
        shape._extremes = {};

        var ax; var bounds;
        var xRefType = Axes.getRefType(shape.xref);
        var yRefType = Axes.getRefType(shape.yref);

        // paper and axis domain referenced shapes don't affect autorange
        if(shape.xref !== 'paper' && xRefType !== 'domain') {
            ax = Axes.getFromId(gd, shape.xref);

            bounds = shapeBounds(ax, shape, constants.paramIsX);
            if(bounds) {
                shape._extremes[ax._id] = Axes.findExtremes(ax, bounds, calcXPaddingOptions(shape));
            }
        }

        if(shape.yref !== 'paper' && yRefType !== 'domain') {
            ax = Axes.getFromId(gd, shape.yref);

            bounds = shapeBounds(ax, shape, constants.paramIsY);
            if(bounds) {
                shape._extremes[ax._id] = Axes.findExtremes(ax, bounds, calcYPaddingOptions(shape));
            }
        }
    }
};

function calcXPaddingOptions(shape) {
    return calcPaddingOptions(shape.line.width, shape.xsizemode, shape.x0, shape.x1, shape.path, false);
}

function calcYPaddingOptions(shape) {
    return calcPaddingOptions(shape.line.width, shape.ysizemode, shape.y0, shape.y1, shape.path, true);
}

function calcPaddingOptions(lineWidth, sizeMode, v0, v1, path, isYAxis) {
    var ppad = lineWidth / 2;
    var axisDirectionReverted = isYAxis;

    if(sizeMode === 'pixel') {
        var coords = path ?
            helpers.extractPathCoords(path, isYAxis ? constants.paramIsY : constants.paramIsX) :
            [v0, v1];
        var maxValue = Lib.aggNums(Math.max, null, coords);
        var minValue = Lib.aggNums(Math.min, null, coords);
        var beforePad = minValue < 0 ? Math.abs(minValue) + ppad : ppad;
        var afterPad = maxValue > 0 ? maxValue + ppad : ppad;

        return {
            ppad: ppad,
            ppadplus: axisDirectionReverted ? beforePad : afterPad,
            ppadminus: axisDirectionReverted ? afterPad : beforePad
        };
    } else {
        return {ppad: ppad};
    }
}

function shapeBounds(ax, shape, paramsToUse) {
    var dim = ax._id.charAt(0) === 'x' ? 'x' : 'y';
    var isCategory = ax.type === 'category' || ax.type === 'multicategory';
    var v0;
    var v1;
    var shiftStart = 0;
    var shiftEnd = 0;

    var convertVal = isCategory ? ax.r2c : ax.d2c;

    var isSizeModeScale = shape[dim + 'sizemode'] === 'scaled';
    if(isSizeModeScale) {
        v0 = shape[dim + '0'];
        v1 = shape[dim + '1'];
        if(isCategory) {
            shiftStart = shape[dim + '0shift'];
            shiftEnd = shape[dim + '1shift'];
        }
    } else {
        v0 = shape[dim + 'anchor'];
        v1 = shape[dim + 'anchor'];
    }

    if(v0 !== undefined) return [convertVal(v0) + shiftStart, convertVal(v1) + shiftEnd];
    if(!shape.path) return;

    var min = Infinity;
    var max = -Infinity;
    var segments = shape.path.match(constants.segmentRE);
    var i;
    var segment;
    var drawnParam;
    var params;
    var val;

    if(ax.type === 'date') convertVal = helpers.decodeDate(convertVal);

    for(i = 0; i < segments.length; i++) {
        segment = segments[i];
        drawnParam = paramsToUse[segment.charAt(0)].drawn;
        if(drawnParam === undefined) continue;

        params = segments[i].substr(1).match(constants.paramRE);
        if(!params || params.length < drawnParam) continue;

        val = convertVal(params[drawnParam]);
        if(val < min) min = val;
        if(val > max) max = val;
    }
    if(max >= min) return [min, max];
}
