/*global window , document */
/*jslint indent: 2, evil: true */ /* so that writeln won't be flagged */
var idhebdate;
var oclhebdate = (function () {
  "use strict";
  /*
   *      This script was adapted from C sources written by
   *      Scott E. Lee, which contain the following copyright notice:
   *
   *      Copyright 1993-1995, Scott E. Lee, all rights reserved.
   *      Permission granted to use, copy, modify, distribute and sell so long as
   *      the above copyright and this permission statement are retained in all
   *      copies.  THERE IS NO WARRANTY - USE AT YOUR OWN RISK.
   *
   *      Bill Hastings
   *      RBI Software Systems
   *      bhastings@rbi.com
   */
  var gregday, gregmonth, gregmonthname, gregyear, gregwday, hebmonthname,
    GREG_SDN_OFFSET = 32045,
    DAYS_PER_5_MONTHS = 153,
    DAYS_PER_4_YEARS = 1461,
    DAYS_PER_400_YEARS = 146097,
    HALAKIM_PER_HOUR = 1080,
    HALAKIM_PER_DAY = 25920,
    HALAKIM_PER_LUNAR_CYCLE = ((29 * HALAKIM_PER_DAY) + 13753),
    HALAKIM_PER_METONIC_CYCLE = (HALAKIM_PER_LUNAR_CYCLE * (12 * 19 + 7)),
    HEB_SDN_OFFSET = 347997,
    NEW_MOON_OF_CREATION = 31524,
    NOON = (18 * HALAKIM_PER_HOUR),
    AM3_11_20 = ((9 * HALAKIM_PER_HOUR) + 204),
    AM9_32_43 = ((15 * HALAKIM_PER_HOUR) + 589),
    SUN = 0,
    MON = 1,
    TUES = 2,
    WED = 3,
    /* THUR = 4, unused */
    FRI = 5,
    /* SAT = 6 unused */
    hebrewMonth = 0,
    hebrewDate = 0,
    hebrewYear = 0,
    metonicCycle = 0,
    metonicYear = 0,
    moladDay = 0,
    moladHalakim = 0,
    gWeekday = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
    gMonth = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
    hMonth = ["Tishri", "Heshvan", "Kislev", "Tevet", "Shevat", "AdarI", "AdarII", "Nisan", "Iyyar", "Sivan", "Tammuz", "Av", "Elul"],
    mpy = [12, 12, 13, 12, 12, 13, 12, 13, 12, 12, 13, 12, 12, 13, 12, 12, 13, 12, 13],
    omerday = 0, omereve = 0, datestuffxxdone = false;

  function returnHebrewMonth() {
    if (hebrewMonth === 6 && (mpy[(hebrewYear - 1) % 19] === 12)) {return ("Adar"); }
    return (hMonth[hebrewMonth - 1]);
  }
  function moladOfMetonicCycle() {
    var r1, r2, d1, d2;
    // Start with the time of the first molad after creation.
    r1 = NEW_MOON_OF_CREATION;
    // Calculate gMetonicCycle * HALAKIM_PER_METONIC_CYCLE.  The upper 32
    // bits of the result will be in r2 and the lower 16 bits will be in r1.
    /*jslint bitwise: true */
    r1 += metonicCycle * (HALAKIM_PER_METONIC_CYCLE & 0xFFFF);
    r2 = r1 >> 16;
    r2 += metonicCycle * ((HALAKIM_PER_METONIC_CYCLE >> 16) & 0xFFFF);
    // Calculate r2r1 / HALAKIM_PER_DAY.  The remainder will be in r1,the
    // upper 16 bits of the quotient will be in d2 and the lower 16 bits
    // will be in d1.
    d2 = Math.floor(r2 / HALAKIM_PER_DAY);
    r2 -= d2 * HALAKIM_PER_DAY;
    r1 = (r2 << 16) | (r1 & 0xFFFF);
    d1 = Math.floor(r1 / HALAKIM_PER_DAY);
    r1 -= d1 * HALAKIM_PER_DAY;
    moladDay = (d2 << 16) | d1;
    /*jslint bitwise: false */
    moladHalakim = r1;
  }
  function findTishri1(metonicYear, moladDay, moladHalakim) {
    var tishri1 = moladDay,
      dow = tishri1 % 7,
      leapYear =  metonicYear === 2 || metonicYear === 5 || metonicYear === 7 || metonicYear === 10 || metonicYear === 13 || metonicYear === 16 || metonicYear === 18,
      lastWasLeapYear =   metonicYear === 3 || metonicYear === 6 || metonicYear === 8 || metonicYear === 11 || metonicYear === 14 || metonicYear === 17 || metonicYear === 0;
    // Apply rules 2,3 and 4
    if ((moladHalakim >= NOON) || ((!leapYear) && dow === TUES && moladHalakim >= AM3_11_20) || (lastWasLeapYear && dow === MON && moladHalakim >= AM9_32_43)) {
      tishri1 += 1;
      dow += 1;
      if (dow === 7) {dow = 0; }
    }
    // Apply rule 1 after the others because it can cause an additional delay of one day.
    if (dow === WED || dow === FRI || dow === SUN) {
      tishri1 += 1;
    }
    return tishri1;
  }
  function findTishriMolad(inputDay) {
    // Estimate the metonic cycle number.  Note that this may be an under
    // estimate because there are 6939.6896 days in a metonic cycle not
    // 6940,but it will never be an over estimate.   The loop below will
    // correct for any error in this estimate.
    metonicCycle = Math.floor((inputDay + 310) / 6940);
    // Calculate the time of the starting molad for this metonic cycle.
    moladOfMetonicCycle();
    // If the above was an under estimate,increment the cycle number until
    // the correct one is found.  For modern dates this loop is about 98.6%
    // likely to not execute,even once,because the above estimate is
    // really quite close.
    while (moladDay < inputDay - 6940 + 310) {
      metonicCycle += 1;
      moladHalakim += HALAKIM_PER_METONIC_CYCLE;
      moladDay += Math.floor(moladHalakim / HALAKIM_PER_DAY);
      moladHalakim = moladHalakim % HALAKIM_PER_DAY;
    }
    // Find the molad of Tishri closest to this date.
    for (metonicYear = 0; metonicYear < 18; metonicYear += 1) {
      if (moladDay > inputDay - 74) {break; }
      moladHalakim += HALAKIM_PER_LUNAR_CYCLE * mpy[metonicYear];
      moladDay += Math.floor(moladHalakim / HALAKIM_PER_DAY);
      moladHalakim = moladHalakim % HALAKIM_PER_DAY;
    }
  }
  function gregorianToSdn(inputYear, inputMonth, inputDay) {
    var sdn, year = 0, month = 0;
    // Make year a positive number
    if (inputYear < 0) {
      year = inputYear + 4801;
    } else {year = inputYear + 4800; }
    // Adjust the start of the year
    if (inputMonth > 2) {
      month = inputMonth - 3;
    } else {
      month = inputMonth + 9;
      year -= 1;
    }
    sdn = Math.floor((Math.floor(year / 100) * DAYS_PER_400_YEARS) / 4);
    sdn += Math.floor(((year % 100) * DAYS_PER_4_YEARS) / 4);
    sdn += Math.floor((month * DAYS_PER_5_MONTHS + 2) / 5);
    sdn += inputDay - GREG_SDN_OFFSET;
    gregwday = gWeekday[(sdn + 1) % 7];
    return sdn;
  }
  function sdnToHebrew(sdn) {
    var tishri1 = 0,
      tishri1After = 0,
      yearLength = 0,
      inputDay = sdn - HEB_SDN_OFFSET;
    findTishriMolad(inputDay);
    tishri1 = findTishri1(metonicYear, moladDay, moladHalakim);
    if (inputDay >= tishri1) {
      // It found Tishri 1 at the start of the year.
      hebrewYear = metonicCycle * 19 + metonicYear + 1;
      if (inputDay < tishri1 + 59) {
        if (inputDay < tishri1 + 30) {
          hebrewMonth = 1;
          hebrewDate = inputDay - tishri1 + 1;
        } else {
          hebrewMonth = 2;
          hebrewDate = inputDay - tishri1 - 29;
        }
        return;
      }
      // We need the length of the year to figure this out,so find Tishri 1 of the next year.
      moladHalakim += HALAKIM_PER_LUNAR_CYCLE * mpy[metonicYear];
      moladDay += Math.floor(moladHalakim / HALAKIM_PER_DAY);
      moladHalakim = moladHalakim % HALAKIM_PER_DAY;
      tishri1After = findTishri1((metonicYear + 1) % 19, moladDay, moladHalakim);
    } else {
      // It found Tishri 1 at the end of the year.
      hebrewYear = metonicCycle * 19 + metonicYear;
      if (inputDay >= tishri1 - 177) {
        // It is one of the last 6 months of the year.
        if (inputDay > tishri1 - 30) {
          hebrewMonth = 13;
          hebrewDate = inputDay - tishri1 + 30;
        } else if (inputDay > tishri1 - 60) {
          hebrewMonth = 12;
          hebrewDate = inputDay - tishri1 + 60;
        } else if (inputDay > tishri1 - 89) {
          hebrewMonth = 11;
          hebrewDate = inputDay - tishri1 + 89;
        } else if (inputDay > tishri1 - 119) {
          hebrewMonth = 10;
          hebrewDate = inputDay - tishri1 + 119;
        } else if (inputDay > tishri1 - 148) {
          hebrewMonth = 9;
          hebrewDate = inputDay - tishri1 + 148;
        } else {
          hebrewMonth = 8;
          hebrewDate = inputDay - tishri1 + 178;
        }
        return;
      } else {
        if (mpy[(hebrewYear - 1) % 19] === 13) {
          hebrewMonth = 7;
          hebrewDate = inputDay - tishri1 + 207;
          if (hebrewDate > 0) {return; }
          hebrewMonth -= 1;
          hebrewDate += 30;
          if (hebrewDate > 0) {return; }
          hebrewMonth -= 1;
          hebrewDate += 30;
        } else {
          hebrewMonth = 6;
          hebrewDate = inputDay - tishri1 + 207;
          if (hebrewDate > 0) {return; }
          hebrewMonth -= 1;
          hebrewDate += 30;
        }
        if (hebrewDate > 0) {return; }
        hebrewMonth -= 1;
        hebrewDate += 29;
        if (hebrewDate > 0) {return; }
        // We need the length of the year to figure this out,so find Tishri 1 of this year.
        tishri1After = tishri1;
        findTishriMolad(moladDay - 365);
        tishri1 = findTishri1(metonicYear, moladDay, moladHalakim);
      }
    }
    yearLength = tishri1After - tishri1;
    moladDay = inputDay - tishri1 - 29;
    if (yearLength === 355 || yearLength === 385) {
      // Heshvan has 30 days
      if (moladDay <= 30) {
        hebrewMonth = 2;
        hebrewDate = moladDay;
        return;
      }
      moladDay -= 30;
    } else {
      // Heshvan has 29 days
      if (moladDay <= 29) {
        hebrewMonth = 2;
        hebrewDate = moladDay;
        return;
      }
      moladDay -= 29;
    }
    // It has to be Kislev.
    hebrewMonth = 3;
    hebrewDate = moladDay;
  }

  function getAnyDate(inputYear, inputMonth, inputDay) {
    gregyear = inputYear; gregmonth = inputMonth; gregday = inputDay;
    sdnToHebrew(gregorianToSdn(inputYear, inputMonth + 1, inputDay));
    //hebrewMonth = 9; hebrewDate = 15; // for testing omer dates
    omerday = omereve = 0;
    if (hebrewMonth === 8  && hebrewDate > 14) {
      omerday = hebrewDate - 15; omereve = omerday + 1;
    } else if (hebrewMonth === 9) {
      omerday = hebrewDate + 15; omereve = omerday + 1;
    } else if (hebrewMonth === 10 && hebrewDate < 6) {omerday = hebrewDate + 44; if (hebrewDate < 5) {omereve = omerday + 1; } }
    hebmonthname = returnHebrewMonth();
    gregmonthname = gMonth[gregmonth];
  }
  function getTodayDate() {
    var today = new Date();
    getAnyDate(today.getFullYear(), today.getMonth(), today.getDate());
  }

  function isidthere() {
    if (document.getElementById) {
      if (!idhebdate) {return document.getElementById("hebdate"); }
      if (idhebdate !== "no") {return document.getElementById(idhebdate); }
    }
  }
  function createElement(ns, element) {
    return ns ? document.createElementNS(ns, element) : document.createElement(element);
  }
  function putOmer(elemhebdate) {
    var ns, b1, b2, b3, o1, o2, omerdays = String(omerday), omereves = String(omereve);
    if (omerday !== 0 || omereve !== 0) {
      o1 = o2 = "";
      if (omerday === 0) {
        o2 = "Omer " + omereves + " (eve)";
      } else if (omereve === 0) {
        o1 = "Omer " + omerdays + " (day)";
      } else {o1 = "Omer " + omerdays + " (day)"; o2 = omereves + " (eve)"; }
      if (document.getElementById && document.childNodes) {
        if (!elemhebdate || !document.createTextNode) {return; }
        ns = document.createElementNS ? elemhebdate.namespaceURI : false;
        if (!ns && !document.createElement) {return; }
        b1 = createElement(ns, "br");
        elemhebdate.appendChild(b1);
        if (omerday !== 0) {
          b2 = document.createTextNode(o1);
          b3 = createElement(ns, "a");
          b3.href = "omer.php?omerday=" + omerdays;
          b3.appendChild(b2);
          elemhebdate.appendChild(b3);
          if (o2) {elemhebdate.appendChild(document.createTextNode(" / ")); }
        }
        if (omereve !== 0) {
          b2 = document.createTextNode(o2);
          b3 = createElement(ns, "a");
          b3.href = "omer.php?omerday=" + omereves;
          b3.appendChild(b2);
          elemhebdate.appendChild(b3);
        }
      } else {
        document.writeln("<br>");
        if (o1) {
          document.writeln("<a href='omer.php?omerday=" + omerdays + "'>" + o1 + "</a>");
          if (o2) {document.writeln(" . "); }
        }
        if (o2) {document.writeln("<a href='omer.php?omerday=" + omereves + "'>" + o2 + "</a>"); }
      }
    }
  }
  function datestuffxx() {
    if (datestuffxxdone) {return; }
    datestuffxxdone = true;
    getTodayDate();
    var elemhebdate,
      gap = String.fromCharCode(32, 160, 160),
      s = gregmonthname + " " + gregday + ", " + gregyear + gap + gregwday + gap + String(hebrewDate) + " " + hebmonthname + " " + String(hebrewYear);
    if (document.getElementById && document.childNodes) {
      elemhebdate = isidthere();
      if (elemhebdate) {elemhebdate.firstChild.data = s; }
    } else {document.writeln(s); }
    putOmer(elemhebdate);
  }
  function datestuff() {
    if (document.getElementById && document.childNodes && (window.addEventListener || window.attachEvent)) {return; }
    datestuffxx();
  }

  return {
    datestuffxx: datestuffxx,
    datestuff: datestuff,
    isidthere: isidthere
  };
}());

if (document.getElementById && document.childNodes) {
  if (oclhebdate.isidthere()) {
    oclhebdate.datestuffxx();
  } else if (window.addEventListener) {
    document.addEventListener("DOMContentLoaded", oclhebdate.datestuffxx, false);
    window.addEventListener("load", oclhebdate.datestuffxx, false);
  } else if (window.attachEvent) {window.attachEvent("onload", oclhebdate.datestuffxx); }
}
