/*  time.js  – exports: {record, number, text}  */

var time = this;

/* -------- host helpers -------------------------------------------------- */

var now           = time.now;             // seconds since Misty epoch (C stub)
var computer_zone = time.computer_zone;   // integral hours, no DST
var computer_dst  = time.computer_dst;    // true ↔ DST in effect

delete time.now;
delete time.computer_zone;
delete time.computer_dst;

/* -------- units & static tables ----------------------------------------- */

time.second   = 1;
time.minute   = 60;
time.hour     = 3_600;
time.day      = 86_400;
time.week     = 604_800;

time.weekdays = [
  "Sunday", "Monday", "Tuesday",
  "Wednesday", "Thursday", "Friday", "Saturday"
];

time.monthstr = [
  "January", "February", "March", "April", "May", "June",
  "July", "August", "September", "October", "November", "December"
];

time.epoch = 1970;

/* ratios (kept K&R-style) */
time.hour2minute    = function() { return time.hour / time.minute; };
time.day2hour       = function() { return time.day  / time.hour;   };
time.minute2second  = function() { return time.minute / time.second; };
time.week2day       = function() { return time.week / time.day;    };

/* leap-year helpers */
time.yearsize = function yearsize(y)
{
  if (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)) return 366;
  return 365;
};
time.isleap = function(y) { return time.yearsize(y) == 366; };

/* timecode utility */
time.timecode = function(t, fps = 24)
{
  var s = Math.trunc(t);
  var frac = t - s;
  return `${s}:${Math.trunc(frac * fps)}`;
};

/* per-month day counts (non-leap) */
time.monthdays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];

/* -------- core converters ----------------------------------------------- */

function record(num = now(),
                zone = computer_zone(),
                dst  = computer_dst())
{
  /* caller passed an existing record → return it verbatim */
  if (typeof num == "object") return num;

  /* -------------------------------------------------------------------- */
  /* convert seconds-since-epoch → broken-down record                      */

  var monthdays = time.monthdays.slice();
  var rec = {
    second : 0,  minute : 0,  hour : 0,
    yday   : 0,  year   : 0,
    weekday: 0,  month  : 0,  day  : 0,
    zone   : zone,       dst   : !!dst,
    ce     : "AD"
  };

  /* include DST in the effective offset */
  var offset = zone + (dst ? 1 : 0);
  num += offset * time.hour;

  /* split into day + seconds-of-day */
  var hms = num % time.day;
  var day = Math.floor(num / time.day);
  if (hms < 0) { hms += time.day; day--; }

  rec.second  = hms % time.minute;
  var tmp     = Math.floor(hms / time.minute);
  rec.minute  = tmp % time.minute;
  rec.hour    = Math.floor(tmp / time.minute);
  rec.weekday = (day + 4_503_599_627_370_496 + 2) % 7;   /* 2 → 1970-01-01 was Thursday */

  /* year & day-of-year */
  var y       = time.epoch;
  if (day >= 0) {
    for (y = time.epoch; day >= time.yearsize(y); y++)
      day -= time.yearsize(y);
  } else {
    for (y = time.epoch; day < 0; y--)
      day += time.yearsize(y - 1);
  }
  rec.year  = y;
  rec.ce    = (y <= 0) ? "BC" : "AD";
  rec.yday  = day;

  /* month & month-day */
  if (time.yearsize(y) == 366) monthdays[1] = 29;
  var m = 0;
  for (; day >= monthdays[m]; m++) day -= monthdays[m];
  rec.month = m;
  rec.day   = day + 1;

  return rec;
}

function number(rec = now())
{
  /* fall through for numeric input or implicit “now” */
  if (typeof rec == "number") return rec;

  /* -------------------------------------------------------------------- */
  /* record → seconds-since-epoch                                          */

  log.console(typeof rec)
  log.console(rec)  
  log.console(rec.minute)

  var c      = 0;
  var year   = rec.year   || 0;
  var hour   = rec.hour   || 0;
  var minute = rec.minute || 0;
  var second = rec.second || 0;
  var zone   = rec.zone   || 0;
  var dst    = rec.dst    ? 1 : 0;
  var yday   = rec.yday   || 0;

  if (year > time.epoch) {
    for (var i = time.epoch; i < year; i++)
      c += time.day * time.yearsize(i);
  } else if (year < time.epoch) {
    for (var i = time.epoch - 1; i > year; i--)
      c += time.day * time.yearsize(i);
    c += (time.yearsize(year) - yday - 1) * time.day;
    c += (time.day2hour()    - hour   - 1) * time.hour;
    c += (time.hour2minute() - minute - 1) * time.minute;
    c += time.minute2second() - second;
    c += (zone + dst) * time.hour;
    return -c;                              /* BCE */
  }

  c  =  second;
  c += minute * time.minute;
  c += hour   * time.hour;
  c += yday   * time.day;
  c -= (zone + dst) * time.hour;

  return c;
}

/* -------- text formatting ----------------------------------------------- */

var default_fmt = "vB mB d hh:nn:ss a z y c";   /* includes new DST token */

function text(num = now(),
              fmt = default_fmt,
              zone = computer_zone(),
              dst  = computer_dst())
{
  var rec   = (typeof num == "number") ? record(num, zone, dst) : num;
  zone      = rec.zone;
  dst       = rec.dst;

  /* am/pm */
  if (fmt.includes("a")) {
    if (rec.hour >= 13) { rec.hour -= 12; fmt = fmt.replaceAll("a", "PM"); }
    else if (rec.hour == 12)            { fmt = fmt.replaceAll("a", "PM"); }
    else if (rec.hour == 0)  { rec.hour = 12; fmt = fmt.replaceAll("a", "AM"); }
    else                                   fmt = fmt.replaceAll("a", "AM");
  }

  /* BCE/CE */
  var year = rec.year > 0 ? rec.year : rec.year - 1;
  if (fmt.includes("c")) {
    if (year < 0) { year = Math.abs(year); fmt = fmt.replaceAll("c", "BC"); }
    else                                   fmt = fmt.replaceAll("c", "AD");
  }

  /* substitutions */
  var full_offset = zone + (dst ? 1 : 0);
  fmt = fmt.replaceAll("yyyy", year.toString().padStart(4, "0"));
  fmt = fmt.replaceAll("y",    year);
  fmt = fmt.replaceAll("eee",  rec.yday + 1);
  fmt = fmt.replaceAll("dd",   rec.day.toString().padStart(2, "0"));
  fmt = fmt.replaceAll("d",    rec.day);
  fmt = fmt.replaceAll("hh",   rec.hour.toString().padStart(2, "0"));
  fmt = fmt.replaceAll("h",    rec.hour);
  fmt = fmt.replaceAll("nn",   rec.minute.toString().padStart(2, "0"));
  fmt = fmt.replaceAll("n",    rec.minute);
  fmt = fmt.replaceAll("ss",   rec.second.toFixed(2).padStart(2, "0"));
  fmt = fmt.replaceAll("s",    rec.second);
  fmt = fmt.replaceAll("x",    dst ? "DST" : "");                     /* new */
  fmt = fmt.replaceAll("z",    (full_offset >= 0 ? "+" : "") + full_offset);
  fmt = fmt.replaceAll(/mm[^bB]/g, rec.month + 1);
  fmt = fmt.replaceAll(/m[^bB]/g,  rec.month + 1);
  fmt = fmt.replaceAll(/v[^bB]/g,  rec.weekday);
  fmt = fmt.replaceAll("mb", time.monthstr[rec.month].slice(0, 3));
  fmt = fmt.replaceAll("mB", time.monthstr[rec.month]);
  fmt = fmt.replaceAll("vB", time.weekdays[rec.weekday]);
  fmt = fmt.replaceAll("vb", time.weekdays[rec.weekday].slice(0, 3));

  return fmt;
}

/* -------- docs ---------------------------------------------------------- */

time[cell.DOC] = {
  doc    : "Time utilities; epoch = 0000-01-01 00:00:00 +0000.",
  record : "time.record([num | rec], [zone], [dst]) → detailed record (adds .dst).",
  number : "time.number([rec]) → numeric seconds since epoch.",
  text   : "time.text([val], [fmt], [zone], [dst]) → formatted string (token Z = DST)."
};

/* -------- public exports ------------------------------------------------ */

return { record, number, text };
