/**
* Copyright 2012-2018, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

var Axes = require('../../plots/cartesian/axes');
var isArray1D = require('../../lib').isArray1D;
var cheaterBasis = require('./cheater_basis');
var arrayMinmax = require('./array_minmax');
var calcGridlines = require('./calc_gridlines');
var calcLabels = require('./calc_labels');
var calcClipPath = require('./calc_clippath');
var clean2dArray = require('../heatmap/clean_2d_array');
var smoothFill2dArray = require('./smooth_fill_2d_array');
var convertColumnData = require('../heatmap/convert_column_xyz');
var setConvert = require('./set_convert');

module.exports = function calc(gd, trace) {
    var xa = Axes.getFromId(gd, trace.xaxis);
    var ya = Axes.getFromId(gd, trace.yaxis);
    var aax = trace.aaxis;
    var bax = trace.baxis;

    var x = trace.x;
    var y = trace.y;
    var cols = [];
    if(x && isArray1D(x)) cols.push('x');
    if(y && isArray1D(y)) cols.push('y');

    if(cols.length) {
        convertColumnData(trace, aax, bax, 'a', 'b', cols);
    }

    var a = trace._a = trace._a || trace.a;
    var b = trace._b = trace._b || trace.b;
    x = trace._x || trace.x;
    y = trace._y || trace.y;

    var t = {};

    if(trace._cheater) {
        var avals = aax.cheatertype === 'index' ? a.length : a;
        var bvals = bax.cheatertype === 'index' ? b.length : b;
        x = cheaterBasis(avals, bvals, trace.cheaterslope);
    }

    trace._x = x = clean2dArray(x);
    trace._y = y = clean2dArray(y);

    // Fill in any undefined values with elliptic smoothing. This doesn't take
    // into account the spacing of the values. That is, the derivatives should
    // be modified to use a and b values. It's not that hard, but this is already
    // moderate overkill for just filling in missing values.
    smoothFill2dArray(x, a, b);
    smoothFill2dArray(y, a, b);

    setConvert(trace);

    // create conversion functions that depend on the data
    trace.setScale();

    // This is a rather expensive scan. Nothing guarantees monotonicity,
    // so we need to scan through all data to get proper ranges:
    var xrange = arrayMinmax(x);
    var yrange = arrayMinmax(y);

    var dx = 0.5 * (xrange[1] - xrange[0]);
    var xc = 0.5 * (xrange[1] + xrange[0]);

    var dy = 0.5 * (yrange[1] - yrange[0]);
    var yc = 0.5 * (yrange[1] + yrange[0]);

    // Expand the axes to fit the plot, except just grow it by a factor of 1.3
    // because the labels should be taken into account except that's difficult
    // hence 1.3.
    var grow = 1.3;
    xrange = [xc - dx * grow, xc + dx * grow];
    yrange = [yc - dy * grow, yc + dy * grow];

    Axes.expand(xa, xrange, {padded: true});
    Axes.expand(ya, yrange, {padded: true});

    // Enumerate the gridlines, both major and minor, and store them on the trace
    // object:
    calcGridlines(trace, 'a', 'b');
    calcGridlines(trace, 'b', 'a');

    // Calculate the text labels for each major gridline and store them on the
    // trace object:
    calcLabels(trace, aax);
    calcLabels(trace, bax);

    // Tabulate points for the four segments that bound the axes so that we can
    // map to pixel coordinates in the plot function and create a clip rect:
    t.clipsegments = calcClipPath(trace._xctrl, trace._yctrl, aax, bax);

    t.x = x;
    t.y = y;
    t.a = a;
    t.b = b;

    return [t];
};
