#include <iostream.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

class Date {
public:
  // constructors;
  Date();
  Date(int, int, int);
  Date(int, const char *, int);
  ~Date();
  // member functions
  int year(void);
  int month(void);
  int day(void);
  Date &operator++(void);
  Date &operator--(void);
  Date &add_day(int n);
  Date &add_month(int m);
  Date &add_year(int y);
  ostream &operator<<(ostream &);
  static char *month_names[];
private: 
  int Year, Month, Day;
  static int months_in_year;
  static int days_in_months[];
  bool is_skottyear(void);
  int maxdays_thismonth(void);
};

int
Date::months_in_year = 12;

int
Date::days_in_months[13] =
  { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

char
* Date::month_names[12] =
  { "jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec" };

// 3 different constructors

Date::Date()
{
  Year=0;
  Month = Day = 1;
}

Date::Date(int y, int m, int d)
{
  Year = y;
  Month = m;
  Day = d;
}

Date::Date(int y, const char *m, int d)
{
  Year = y;
  Day = d;
  Month = 1;

  for (int i=0;i<13;i++)
    {
      if (strncmp(m, month_names[i], 3) == 0)
         {
           Month = i;
           break;
         }
    }
}
     


Date::~Date()
{
  cout << "Date object destroyed\n";
}

int
Date::year(void)
{
  return Year;
}

int
Date::month(void)
{
  return Month;
}

int
Date::day(void)
{
  return Day;
}

Date Fakedate;

ostream &operator<<(ostream &stream, Date &date)
{
  stream << date.year();
  stream.form("-%02d", date.month());
  stream.form("-%02d", date.day());
  return stream;
}

Date &Date::operator++(void)
{
  add_day(1);
  return *this;
}

Date &Date::operator--(void)
{
  add_day(-1);
  return *this;
}
   

Date &Date::add_year(int y)
{
  for (int i=0;i<abs(y);i++)
    {
      if (is_skottyear())
         add_day((int) 366 * abs(y)/y);
      else
         add_day((int) 365 * abs(y)/y);
    }
  return *this;
}

Date &Date::add_month(int m)
{
  div_t quotrem;
 
  quotrem = div(m, months_in_year);
  add_year(quotrem.quot);
 
  for (int i=0;i<abs(quotrem.rem);i++)
    add_day((int) maxdays_thismonth() * quotrem.rem/abs(quotrem.rem));
 
  return *this;
}

Date &Date::add_day(int d)
{
  while ((Day + d) < 1)
    {
      if ((Month -= 1) < 1)
         {
           Month = months_in_year;
           Year -= 1;
         }
      d += maxdays_thismonth();
    }
  while((Day + d) > maxdays_thismonth())
    {
      d -= maxdays_thismonth() - Day + 1;
      Day = 1;
      Month += 1;
      if (Month > months_in_year)
         {
           Month -= months_in_year;
           Year += 1;
         }
    }
  Day += d;
  return *this;
}

int
Date::maxdays_thismonth(void)
{
  if (Month == 2 && is_skottyear())
    return 29;
  else
    return days_in_months[Month];
}

bool
Date::is_skottyear(void)
{
  // i know february had 29 days 1984.
  int refyear;
  refyear = 1984;
  if (((refyear - Year) % 4) == 0)
    return true;
  else
    return false;
}

// prototype this
void  get_initdate(int& Y, int& M, int& D);


int
main(int argc, char *argv[])
{
  Date *dateptr;
  Date &somedate = *dateptr;
  char choice;
  int y, m, d;

  get_initdate(y, m, d);
  dateptr = new Date(y,m,d);
  somedate = *dateptr;  // why do i have to do this again? seems unnessecary.

  while (1)
    {
      cout << "<q to quit> or:\n";
      cout << "d x = add x days, m x = add x months, y x = add x years,\n";
      cout << "a x y z adds x years, y months, z days\n$ ";
      cin >> choice;
      switch (choice)
         {
         case 'd':
           cin >> d;
           somedate.add_day(d);
           break;
         case 'm':
           cin >> m;
           somedate.add_month(m);
           break;
         case 'y':
           cin >> y;
           somedate.add_year(y);
           break;
         case 'a':
           cin >> y >> m >> d;
           somedate.add_year(y).add_month(m).add_day(d);
           break;
         case '+':
           ++somedate;
           break;
         case '-':
           --somedate;
           break;
         case 'q':
           exit(false);
           break;
         default:
           cout << "Invalid command\n";
           break;
         }
      cout << "Date: " << somedate << endl;

    }
  exit (false);
}
            
void  get_initdate(int &Y, int &M, int &D)
{
  char *nptr, **endptr;
  char readvalue[256];
  int val, y, m, d;
  FILE *p;

  memset(readvalue, 0, 256);

  cout << endl << "Enter initial date on form \'year month day\'" << endl;
  cout << "Example: \'1974 2 20\'  or \'1974 feb 20\'  or simply \'today\'" << endl;
  cout << "If input is invalid, todays date will be used" << endl;
  cout << "$ ";
  cin >> readvalue;

  // Don't try the below at home folks ;)
  nptr = &readvalue[0];
  endptr = (char **) malloc(4); // make room for a 32 bit address
  *endptr = (char *) malloc(4);
  val = strtol(nptr, endptr, 10);
  if (**endptr != '\0')
    { // input was not entirely numeric! use todays date
      p = popen("date +\"\%Y \%m \%d\"", "r");
      fscanf(p, "%d %d %d", &y, &m, &d);
      pclose(p);
    }
  else
    {
      y = val;
      cin >> readvalue;
      nptr = &readvalue[0];
      val =  strtol(nptr, endptr, 10);
        if (**endptr != '\0')
           { // input was not entirely numeric! is it a name?
             for (int z=0;z<12;z++)
               {
                  if (strncmp(readvalue, Fakedate.month_names[z],3) == 0)
                    {
                      m=z+1;
                      break;
                    }
               }
           }
         else
           m=val;
         // and the day...
         cin >> readvalue;
         nptr = &readvalue[0];
         val =  strtol(nptr, endptr, 10);
         if (**endptr != '\0')
           { // input was not entirely numeric! use todays date
             p = popen("date +\"\%Y \%m \%d\"", "r");
             fscanf(p, "%d %d %d", &y, &m, &d);
             pclose(p);
           }
         else
           d=val;
    }
 
  Y=y;
  M=m;
  D=d;
  return;
}