/*****************************************************************

Solar-Lunar date calculation for Pagan Book of Hours project.

Joshua Tenpenny - josh@cauldronfarm.com             2003-04-04

Dates are an array [month, day, year]
Months are 1-base indexing. Month 0 indicates days out of time.

In JavaScript, Dates are created Date(Year, 0-base-Month, Day)
To convert from solar date to JS Date type, 
	jsd = new Date(d[2], d[0]-1, d[1])

*****************************************************************/

var lunar_lookup = new Array(10);


// lunar_lookup[y][m]
// y = year-2000  m = month, using 1-based indexing
// m=0 is # of days out of time that FOLLOW this year.
// m=14 is the day after the year ends. m=15 is an arbitrarily high boundary.
lunar_lookup[02] = [0,  0,  0,  0,  0,  0,   0,    0,   0,   0,   0,   0,   0, 339, 356, 1000] 
lunar_lookup[03] = [0, -8,  2, 33, 62,  91, 121, 151, 180, 210, 239, 269, 298, 327, 356, 1000]
lunar_lookup[04] = [0, -8, 21, 51, 80, 110, 140, 169, 199, 229, 258, 288, 317, 347, 357, 1000] 
lunar_lookup[05] = [8, -8, 10, 39, 69,  98, 128, 157, 187, 217, 246, 276, 306, 335, 356, 1000] 
lunar_lookup[06] = [0,  0, 29, 59, 88, 117, 147, 176, 206, 235, 265, 295, 324, 354, 356, 1000] 
lunar_lookup[07] = [0, -8, 19, 48, 78, 107, 136, 166, 195, 224, 254, 284, 313, 343, 356, 1000]
lunar_lookup[08] = [5, -8,  8, 38, 67,  97, 126, 155, 185, 214, 243, 273, 302, 332, 357, 1000]
lunar_lookup[09] = [0, -3, 26, 56, 85, 115, 144, 173, 203, 232, 261, 291, 320, 350, 356, 1000]
lunar_lookup[10] = [0, -8, 15, 45, 74, 104, 134, 163, 192, 222, 251, 280, 310, 339, 356, 1000]

function getSolarDate(d) {
  return [d.getMonth()+1, d.getDate(), d.getFullYear()]
}

function daysInYear(y) {
  d1 = new Date(y, 0, 1); 
  d2 = new Date(y+1, 0, 1); 
  return(Math.round(((d2.valueOf() - d1.valueOf())/86400000)))
}

function SolarToDayInYear(d) {
  d1 = new Date(d[2], 0, 1); 
  d2 = new Date(d[2], d[0]-1, d[1]); 
  return(Math.round((1 + (d2.valueOf() - d1.valueOf())/86400000)))
}

function DayInYearToSolar(n, y) {
  d = new Date(y, 0, 1); 
  return getSolarDate(new Date(d.valueOf() + (n-1)*86400000));
}

function numDaysSolarMon(m,y) {
  var d1 = new Date(y, m-1, 1);
  var d2 = new Date(y, m, 1);
  return (Math.round((d2.valueOf() - d1.valueOf())/86400000))
}

function numDaysLunarMon(m,y) {
  if (m==0) return lunar_lookup[y-2000][m];
  m = parseInt(m)
  return lunar_lookup[y-2000][m+1] - lunar_lookup[y-2000][m] ;

}

function BethOffset(m,y) {
  // calculate Beth offest, if needed.
  // on short years, it goes something like : Ruis 15, Ruis 16, Beth 1, Beth 18, Beth 19...
  // so Beth ends on the 29th or 30th.
  if ((m==1) && (numDaysLunarMon(m, y)<29)) {
    return numDaysLunarMon(13, y-1);
  }
  return 0;
}

function solar_to_lunar(s) {
  n = SolarToDayInYear(s);

  lunyear = s[2];
  var months = lunar_lookup[s[2]-2000];

  lunmon= 1;
  while (months[lunmon] <= n) lunmon++;

  lunmon--;
  lunday = 1 + n - months[lunmon];
  
  if (lunmon==14) { // if we've past the end of the year
    if (lunday <= months[0]) lunmon=0 ;
    else { // we are in the days out of time
      lunmon = 1;
      lunyear++;
      lunday -= months[0]
    }
  }
  if (lunday != 1) lunday += BethOffset(lunmon, lunyear);
  
  return new Array(lunmon, lunday, lunyear);
}

function lunar_to_solar(l) {
  if (l[0] == 0)  { //day out of time
    // start of *next* year - num days out *this* year + day of month
    n = parseInt(lunar_lookup[l[2]-1999][1]) - parseInt(lunar_lookup[l[2]-2000][0]) + parseInt(l[1]);
    return DayInYearToSolar(n, parseInt(l[2]))
  }
  
  // start of this month + day of month
  n = parseInt(lunar_lookup[l[2]-2000][l[0]]) + parseInt(l[1]);
  
  if ((l[0] == 1) && (l[1] != 1) && (BethOffset(l[0],l[2])!=0))  
     n =  n - BethOffset(l[0],l[2]); 

  return DayInYearToSolar(n-1, l[2])
}



function solstring (d) {
  return "" + solmonth(d[0]) + " " + d[1] + " " + d[2];}

function lunstring (d) {
  return "" + lunmonth(d[0]) + " " + d[1] + " " + d[2];
}

function solmonth(n) {
  if (n==12) return "Yulmonath"
  if (n==1) return "Wolfmonath"
  if (n==2) return "Solmonath"
  if (n==3) return "Hrethemonath"
  if (n==4) return "Eostremonath"
  if (n==5) return "Thrimilchimonath"
  if (n==6) return "Lithemonath"
  if (n==7) return "Haymonath"
  if (n==8) return "Weodmonath"
  if (n==9) return "Halegmonath"
  if (n==10) return "Winterfyllith"
  if (n==11) return "Blutmonath"
  return "[ERROR]"
}

function lunmonth(n) {
    if (n==1) return "Beth"
    if (n==2) return "Luis"
    if (n==3) return "Nion"
    if (n==4) return "Fearn"
    if (n==5) return "Saille"
    if (n==6) return "Huath"
    if (n==7) return "Duir"
    if (n==8) return "Tinne"
    if (n==9) return "Coll"
    if (n==10) return "Muin"
    if (n==11) return "Gort"
    if (n==12) return "Ngetal"
    if (n==13) return "Ruis"
    if (n==0) return "Day out of Time"
    return "[ERROR]"
}


function setLunDropdown(f) {

  var s = new Array(f.sol_mon.value, f.sol_day.value, f.sol_year.value);
  var l = solar_to_lunar(s);
  
  // alert(solstring(s) + " => " + lunstring(l));

  f.lun_mon.selectedIndex = l[0];
  f.lun_day.selectedIndex = l[1]-1;
  f.lun_year.selectedIndex = l[2]-2003;
  validateLun(f)
  return 1
}

function setSolDropdown(f) {

  var l = new Array(f.lun_mon.value, f.lun_day.value, f.lun_year.value);
  var s = lunar_to_solar(l);
  
  // alert(solstring(s) + " <= " + lunstring(l));

  f.sol_mon.selectedIndex = (s[0]-1) % 12;
  f.sol_day.selectedIndex = s[1]-1;
  f.sol_year.selectedIndex = s[2]-2003;
  validateSol(f);
  return 1
}

function validateSol(f) {
  n = numDaysSolarMon(f.sol_mon.value, f.sol_year.value);
 
  if (f.sol_day.length < n) 
   for (i=f.sol_day.length; i<n; i++)
     f.sol_day.options[i] = new Option(i+1, i+1);
  if (f.sol_day.length != n)
    f.sol_day.length = n

  return 1;
}


function validateLun(f) {
  n = numDaysLunarMon(f.lun_mon.value, f.lun_year.value);
  b = BethOffset(f.lun_mon.value, f.lun_year.value);
  sel = f.lun_day.selectedIndex; 

  if (b != 0) { // Beth offest
    f.lun_day.length = n;
      
    for (i=1; i <= b; i++) 
      f.lun_day.options[i] = new Option("--", 1);

    for (i=b+1; i < (n+b); i++) 
      f.lun_day.options[i] = new Option(i+1, i+1);

    f.lun_day.selectedIndex = sel;

    return 1
  }

  // redraw numbers
  f.lun_day.length = 1;
  for (i=1; i<n; i++)
     f.lun_day.options[i] = new Option(i+1, i+1);
  f.lun_day.length = n
  f.lun_day.selectedIndex = sel; 

  return 1;
}

/*
Code to help generate:
document.write("<p>[0, ");
// change to 366 when previous year was a leap year
document.write(SolarToDayInYear([12,  1, 2007])-365 + ", ");
document.write(SolarToDayInYear([ 1, 21, 2007]) + ", ");
document.write(SolarToDayInYear([ 2, 20, 2007]) + ", ");
document.write(SolarToDayInYear([ 3, 20, 2007]) + ", ");
document.write(SolarToDayInYear([ 4, 19, 2007]) + ", ");
document.write(SolarToDayInYear([ 5, 19, 2007]) + ", ");
document.write(SolarToDayInYear([ 6, 17, 2007]) + ", ");
document.write(SolarToDayInYear([ 7, 17, 2007]) + ", ");
document.write(SolarToDayInYear([ 8, 16, 2007]) + ", ");
document.write(SolarToDayInYear([ 9, 14, 2007]) + ", ");
document.write(SolarToDayInYear([10, 14, 2007]) + ", ");
document.write(SolarToDayInYear([11, 12, 2007]) + ", ");
document.write(SolarToDayInYear([12, 12, 2007]) + ", ");
document.write(SolarToDayInYear([12, 22, 2007]) + "1000]");
*/


