import "../arrays/bisect";
import "../arrays/range";
import "../core/identity";
import "../core/true";
import "../scale/linear";
import "../scale/nice";
import "format";
import "day";
import "hour";
import "minute";
import "month";
import "second";
import "time";
import "week";
import "year";

function d3_time_scale(linear, methods, format) {

  function scale(x) {
    return linear(x);
  }

  scale.invert = function(x) {
    return d3_time_scaleDate(linear.invert(x));
  };

  scale.domain = function(x) {
    if (!arguments.length) return linear.domain().map(d3_time_scaleDate);
    linear.domain(x);
    return scale;
  };

  function tickMethod(extent, count) {
    var span = extent[1] - extent[0],
        target = span / count,
        i = d3.bisect(d3_time_scaleSteps, target);
    return i == d3_time_scaleSteps.length ? [methods.year, d3_scale_linearTickRange(extent.map(function(d) { return d / 31536e6; }), count)[2]]
        : !i ? [d3_time_scaleMilliseconds, d3_scale_linearTickRange(extent, count)[2]]
        : methods[target / d3_time_scaleSteps[i - 1] < d3_time_scaleSteps[i] / target ? i - 1 : i];
  }

  scale.nice = function(interval, skip) {
    var domain = scale.domain(),
        extent = d3_scaleExtent(domain),
        method = interval == null ? tickMethod(extent, 10)
          : typeof interval === "number" && tickMethod(extent, interval);

    if (method) interval = method[0], skip = method[1];

    function skipped(date) {
      return !isNaN(date) && !interval.range(date, d3_time_scaleDate(+date + 1), skip).length;
    }

    return scale.domain(d3_scale_nice(domain, skip > 1 ? {
      floor: function(date) {
        while (skipped(date = interval.floor(date))) date = d3_time_scaleDate(date - 1);
        return date;
      },
      ceil: function(date) {
        while (skipped(date = interval.ceil(date))) date = d3_time_scaleDate(+date + 1);
        return date;
      }
    } : interval));
  };

  scale.ticks = function(interval, skip) {
    var extent = d3_scaleExtent(scale.domain()),
        method = interval == null ? tickMethod(extent, 10)
          : typeof interval === "number" ? tickMethod(extent, interval)
          : !interval.range && [{range: interval}, skip]; // assume deprecated range function

    if (method) interval = method[0], skip = method[1];

    return interval.range(extent[0], d3_time_scaleDate(+extent[1] + 1), skip < 1 ? 1 : skip); // inclusive upper bound
  };

  scale.tickFormat = function() {
    return format;
  };

  scale.copy = function() {
    return d3_time_scale(linear.copy(), methods, format);
  };

  return d3_scale_linearRebind(scale, linear);
}

function d3_time_scaleDate(t) {
  return new Date(t);
}

var d3_time_scaleSteps = [
  1e3,    // 1-second
  5e3,    // 5-second
  15e3,   // 15-second
  3e4,    // 30-second
  6e4,    // 1-minute
  3e5,    // 5-minute
  9e5,    // 15-minute
  18e5,   // 30-minute
  36e5,   // 1-hour
  108e5,  // 3-hour
  216e5,  // 6-hour
  432e5,  // 12-hour
  864e5,  // 1-day
  1728e5, // 2-day
  6048e5, // 1-week
  2592e6, // 1-month
  7776e6, // 3-month
  31536e6 // 1-year
];

var d3_time_scaleLocalMethods = [
  [d3_time.second, 1],
  [d3_time.second, 5],
  [d3_time.second, 15],
  [d3_time.second, 30],
  [d3_time.minute, 1],
  [d3_time.minute, 5],
  [d3_time.minute, 15],
  [d3_time.minute, 30],
  [d3_time.hour, 1],
  [d3_time.hour, 3],
  [d3_time.hour, 6],
  [d3_time.hour, 12],
  [d3_time.day, 1],
  [d3_time.day, 2],
  [d3_time.week, 1],
  [d3_time.month, 1],
  [d3_time.month, 3],
  [d3_time.year, 1]
];

var d3_time_scaleLocalFormat = d3_time_format.multi([
  [".%L", function(d) { return d.getMilliseconds(); }],
  [":%S", function(d) { return d.getSeconds(); }],
  ["%I:%M", function(d) { return d.getMinutes(); }],
  ["%I %p", function(d) { return d.getHours(); }],
  ["%a %d", function(d) { return d.getDay() && d.getDate() != 1; }],
  ["%b %d", function(d) { return d.getDate() != 1; }],
  ["%B", function(d) { return d.getMonth(); }],
  ["%Y", d3_true]
]);

var d3_time_scaleMilliseconds = {
  range: function(start, stop, step) { return d3.range(Math.ceil(start / step) * step, +stop, step).map(d3_time_scaleDate); },
  floor: d3_identity,
  ceil: d3_identity
};

d3_time_scaleLocalMethods.year = d3_time.year;

d3_time.scale = function() {
  return d3_time_scale(d3.scale.linear(), d3_time_scaleLocalMethods, d3_time_scaleLocalFormat);
};
