text: 'use strict';
  
  var Registry = require('../../registry');
  var Lib = require('../../lib');
  
  var colorscaleDefaults = require('../../components/colorscale/defaults');
  var attributes = require('./attributes');
  
  var MIN = 0.1; // Note: often we don't want the data cube to be disappeared
  
  function createWave(n, minOpacity) {
      var arr = [];
      var steps = 32; // Max: 256
      for(var i = 0; i < steps; i++) {
          var u = i / (steps - 1);
          var v = minOpacity + (1 - minOpacity) * (1 - Math.pow(Math.sin(n * u * Math.PI), 2));
          arr.push([
              u,
              Math.max(0, Math.min(1, v))
          ]);
      }
      return arr;
  }
  
  function isValidScaleArray(scl) {
      var highestVal = 0;
  
      if(!Array.isArray(scl) || scl.length < 2) return false;
  
      if(!scl[0] || !scl[scl.length - 1]) return false;
  
      if(+scl[0][0] !== 0 || +scl[scl.length - 1][0] !== 1) return false;
  
      for(var i = 0; i < scl.length; i++) {
          var si = scl[i];
  
          if(si.length !== 2 || +si[0] < highestVal) {
              return false;
          }
  
          highestVal = +si[0];
      }
  
      return true;
  }
  
  function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
      var i, j;
  
      function coerce(attr, dflt) {
          return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
      }
  
      var x = coerce('x');
      var y = coerce('y');
  
      var z = coerce('z');
      if(!z || !z.length ||
         (x ? (x.length < 1) : false) ||
         (y ? (y.length < 1) : false)
      ) {
          traceOut.visible = false;
          return;
      }
  
      traceOut._xlength = (Array.isArray(x) && Lib.isArrayOrTypedArray(x[0])) ? z.length : z[0].length;
      traceOut._ylength = z.length;
  
      var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults');
      handleCalendarDefaults(traceIn, traceOut, ['x', 'y', 'z'], layout);
  
      coerce('text');
      coerce('hovertext');
      coerce('hovertemplate');
      coerce('xhoverformat');
      coerce('yhoverformat');
      coerce('zhoverformat');
  
      // Coerce remaining properties
      [
          'lighting.ambient',
          'lighting.diffuse',
          'lighting.specular',
          'lighting.roughness',
          'lighting.fresnel',
          'lightposition.x',
          'lightposition.y',
          'lightposition.z',
          'hidesurface',
          'connectgaps',
          'opacity'
      ].forEach(function(x) { coerce(x); });
  
      var surfaceColor = coerce('surfacecolor');
  
      var dims = ['x', 'y', 'z'];
      for(i = 0; i < 3; ++i) {
          var contourDim = 'contours.' + dims[i];
          var show = coerce(contourDim + '.show');
          var highlight = coerce(contourDim + '.highlight');
  
          if(show || highlight) {
              for(j = 0; j < 3; ++j) {
                  coerce(contourDim + '.project.' + dims[j]);
              }
          }
  
          if(show) {
              coerce(contourDim + '.color');
              coerce(contourDim + '.width');
              coerce(contourDim + '.usecolormap');
          }
  
          if(highlight) {
              coerce(contourDim + '.highlightcolor');
              coerce(contourDim + '.highlightwidth');
          }
  
          coerce(contourDim + '.start');
          coerce(contourDim + '.end');
          coerce(contourDim + '.size');
      }
  
      // backward compatibility block
      if(!surfaceColor) {
          mapLegacy(traceIn, 'zmin', 'cmin');
          mapLegacy(traceIn, 'zmax', 'cmax');
          mapLegacy(traceIn, 'zauto', 'cauto');
      }
  
      // TODO if contours.?.usecolormap are false and hidesurface is true
      // the colorbar shouldn't be shown by default
  
      colorscaleDefaults(
          traceIn, traceOut, layout, coerce, {prefix: '', cLetter: 'c'}
      );
  
      opacityscaleDefaults(traceIn, traceOut, layout, coerce);
  
      // disable 1D transforms - currently surface does NOT support column data like heatmap does
      // you can use mesh3d for this use case, but not surface
      traceOut._length = null;
  }
  
  function opacityscaleDefaults(traceIn, traceOut, layout, coerce) {
      var opacityscale = coerce('opacityscale');
      if(opacityscale === 'max') {
          traceOut.opacityscale = [[0, MIN], [1, 1]];
      } else if(opacityscale === 'min') {
          traceOut.opacityscale = [[0, 1], [1, MIN]];
      } else if(opacityscale === 'extremes') {
          traceOut.opacityscale = createWave(1, MIN);
      } else if(!isValidScaleArray(opacityscale)) {
          traceOut.opacityscale = undefined;
      }
  }
  
  function mapLegacy(traceIn, oldAttr, newAttr) {
      if(oldAttr in traceIn && !(newAttr in traceIn)) {
          traceIn[newAttr] = traceIn[oldAttr];
      }
  }
  
  module.exports = {
      supplyDefaults: supplyDefaults,
      opacityscaleDefaults: opacityscaleDefaults
  };
  