/*
 * == BSD2 LICENSE ==
 * Copyright (c) 2014, Tidepool Project
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the associated License, which is identical to the BSD 2-Clause
 * License as published by the Open Source Initiative at opensource.org.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the License for more details.
 *
 * You should have received a copy of the License along with this program; if
 * not, you can obtain one from Tidepool Project at tidepool.org.
 * == BSD2 LICENSE ==
 */

/* jshint esversion:6 */

var d3 = require('d3');
var _ = require('lodash');

var commonbolus = require('./commonbolus');
var { MGDL_PER_MMOLL, MGDL_UNITS, MMOLL_UNITS } = require('../../data/util/constants');

var scales = function(opts) {
  opts = _.assign({}, opts) || {};

  var defaults = {
    bgUnits: MGDL_UNITS,
    bolusRatio: 0.35,
    MIN_CBG: 39,
    MAX_CBG: 401,
    TARGET_BG_BOUNDARY: 180,
    carbRadius: 14
  };
  _.defaults(opts, defaults);

  if (opts.bgUnits === MMOLL_UNITS) {
    opts.MIN_CBG = opts.MIN_CBG/MGDL_PER_MMOLL;
    opts.MAX_CBG = opts.MAX_CBG/MGDL_PER_MMOLL;
    opts.TARGET_BG_BOUNDARY = opts.TARGET_BG_BOUNDARY/MGDL_PER_MMOLL;
  }

  return {
    MIN_CBG: opts.MIN_CBG,
    MAX_CBG: opts.MAX_CBG,
    bgClamped: function(domain, pool, pad) {
      return d3.scale.linear()
        .domain(domain)
        .range([pool.height() - pad, pad])
        .clamp(true);
    },
    bg: function(data, pool, pad) {
      var ext = d3.extent(data, function(d) { return d.value; });
      var targetBoundary = _.get(opts, 'bgClasses.target.boundary', opts.TARGET_BG_BOUNDARY);

      // We need to ensure that the top of the bgScale is at least at the the target upper bound
      // for proper rendering of datasets with no BG values above this mark.
      ext[1] = _.max([ext[1], targetBoundary]);

      if (ext[1] > this.MAX_CBG || ext[0] === ext[1]) {
        return d3.scale.linear()
          .domain([0, this.MAX_CBG])
          .range([pool.height() - pad, pad])
          .clamp(true);
      }
      else {
        return d3.scale.linear()
          .domain([0, ext[1]])
          .range([pool.height() - pad, pad]);
      }
    },
    bgLog: function(data, pool, pad) {
      var ext = d3.extent(data, function(d) { return d.value; });
      if (ext[1] > this.MAX_CBG) {
        return d3.scale.log()
          .domain([ext[0], this.MAX_CBG])
          .range([pool.height() - pad, pad])
          .clamp(true);
      }
      else if (ext[0] === ext[1]) {
        return d3.scale.log()
          .domain([this.MIN_CBG, this.MAX_CBG])
          .range([pool.height() - pad, pad])
          .clamp(true);
      }
      else {
        return d3.scale.log()
          .domain(ext)
          .range([pool.height() - pad, pad]);
      }
    },
    bgTicks: function(data) {
      if ((!data) || (data.length === 0)) {
        return [];
      }
      var defaultTicks = _.map(_.values(_.omit(opts.bgClasses, ['very-high', 'very-low'])), function(n) {
        return _.get(n, 'boundary');
      }).sort(function (a, b) { return a - b; });

      var ext = d3.extent(data, function(d) { return d.value; });
      if (ext[0] === ext[1]) {
        return defaultTicks;
      }
      // if the min of our data is greater than any of the defaultTicks, remove that tick
      defaultTicks.forEach(function(tick) {
        if (ext[0] > tick) {
          defaultTicks.shift();
        }
      });
      defaultTicks.reverse();
      // same thing for max
      defaultTicks.forEach(function(tick) {
        if (ext[1] < tick) {
          defaultTicks.shift();
        }
      });
      return defaultTicks.reverse();
    },
    carbs: function(data, pool) {
      var scale = d3.scale.linear()
        .domain([0, d3.max(data, function(d) { return d.carbInput ? d.carbInput : 0; })])
        .range([opts.carbRadius, opts.carbRadius + ((1 - opts.bolusRatio) * pool.height())/4]);
      return scale;
    },
    bolus: function(data, pool) {
      var scale = d3.scale.linear()
        // for boluses the recommended can exceed the value
        .domain([0, d3.max(data, function(d) {
          return commonbolus.getMaxValue(d);
        })])
        .range([pool.height(), opts.bolusRatio * pool.height()]);
      return scale;
    },
    basal: function(data, pool) {
      var scale = d3.scale.linear()
        .domain([0, d3.max(data, function(d) {
          return d.rate;
        }) * 1.1])
        .rangeRound([pool.height(), 0]);
      return scale;
    }
  };
};

module.exports = scales;

@@ -3370,35 +3370,152 @@
         
-return defaultTicks
+var targetBoundary = _.get(opts, 'bgClasses.target.boundary', opts.TARGET_BG_BOUNDARY);%0A        ext%5B1%5D = _.max(%5Bext%5B1%5D, targetBoundary%5D)
 ;%0A      
@@ -3650,191 +3650,11 @@
 tick
-) %7B%0A        if (ext%5B0%5D %3E tick) %7B%0A          defaultTicks.shift();%0A        %7D%0A      %7D);%0A      defaultTicks.reverse();%0A      // same thing for max%0A      defaultTicks.forEach(function(tick
+, i
 ) %7B%0A
@@ -3709,13 +3709,11 @@
 cks.
-shift
+pop
 ();%0A
@@ -3761,18 +3761,8 @@
 icks
-.reverse()
 ;%0A  

Fix for low y-scale tick missing when all data in range