#include <signal.h>
#include <time.h>
#include "Date8a.h"
#include <iomanip>
using namespace std;

bool Date::printyear = true;

Date::Date(int day, int month, int year) {
  struct tm t;

  if (!day || !month || !year) {
    time_t clk = time(0);
    t = *localtime(&clk);
  }
  d = day   ? day   : t.tm_mday;
  m = month ? month : t.tm_mon+1;
  y = year  ? year  : t.tm_year+1900;
  j = jday(d, m, y);
}

Date Date::previous(int desiredDayOfWeek) const {
  int delta = (weekday() + 6 - desiredDayOfWeek) % 7 + 1;
  return Date(*this) -= delta;
}

const Date& Date::operator+=(long days) {
  j += days;
  dmy();
  return *this;
}

const Date Date::operator+(long days) {
  return Date(*this) += days;
}

const Date& Date::operator-=(long days) {
  j -= days;
  dmy();
  return *this;
}

const Date Date::operator-(long days) {
  return Date(*this) -= days;
}


ostream& operator<<(ostream& ostr, const Date& d) {
  if (Date::printyear)
    return ostr << setw(2) << d.day() << '.' << d.monthname() << '.' << d.year();
  else
    return ostr << setw(2) << d.day() << '.' << d.monthname();
}

// Algorithm from K & R, "The C Programming Language", 1st ed.
bool leapYear(int year) {
  return (year&3) == 0 && (year%100 != 0 || year % 400 == 0);
}

// Is a day (1-31) within a given month?
bool Date::dayWithinMonth(int day, int month, int year) const {
  if (day <= 0 || month<1 || month>12 ) return 0;
  int dh = daysInMonth[month-1];
  if (leapYear(year) && month == 2) dh++;
  return day <= dh;
}

/*
 * Convert Gregorian calendar date to the corresponding Julian day
 * number j.  Algorithm 199 from Communications of the ACM, Volume 6, No.
 * 8, (Aug. 1963), p. 444.  Gregorian calendar started on Sep. 14, 1752.
 * This function not valid before that.
 * Returns 0 if the date is invalid.
 */

unsigned long Date::jday(int day, int month, int year) const {
  if( !dayWithinMonth(day, month, year) ) return 0UL;

  if (month > 2) {
    month -= 3;  // wash out the leap day
  } else {
    month += 9;
    year--;
  } 
  unsigned long c  = year / 100;
  unsigned long ya = year - 100*c;
  unsigned long jd = ((146097*c)>>2) + ((1461*ya)>>2)
                                     + (153*month + 2)/5 + day + 1721119;
  return jd;
} 

/*
 * Convert a Julian day number to its corresponding Gregorian calendar
 * date.  Algorithm 199 from Communications of the ACM, Volume 6, No. 8,
 * (Aug. 1963), p. 444.  Gregorian calendar started on Sep. 14, 1752.
 * This function not valid before that.  
 */

void Date::dmy() {
  unsigned long jh = j - 1721119;
  y  = ((jh<<2) - 1) / 146097;
  jh = (jh<<2) - 1 - 146097*y;
  unsigned long dh = (jh>>2);
  jh = ((dh<<2) + 3) / 1461;
  dh = (dh<<2) + 3 - 1461*jh;
  dh = (dh + 4)>>2;
  m  = (5*dh - 3)/153;
  dh = 5*dh - 3 - 153*m;
  d  = (dh + 5)/5;
  y  = 100*y + jh;
  if (m < 10)
    m += 3;
  else {
    m -= 9;
    y++;
  } 
} 

Date easter(int year) {
  long a, b, c, d, e, f, g, h, i, j, k, m, n;

  a = year % 19;
  j = year % 4;
  m = 0;
  if (year > 1582) {
    b = year / 25 >> 2;
    c = year % 100;
    d = b >> 2;
    e = b % 4;
    f = (b + 8) / 25;

    g = (b - f + 1) / 3;
    h = (19 * a + b - d - g + 15) % 30;
    i = c >> 2;

    k = (32 + 2 * e + 2 * i - h - j) % 7;
    m = (a + 11 * h + 22 * k) / 451;
  } else {
    b = year % 7;
    h = (19 * a + 15) % 30;
    k = (2 * j + 4 * b - h + 34) % 7;
  }
  n = h + k - 7 * m + 21;

  return Date(n % 31 + 1, 3 + n / 31, year);
}