
//
// This is example code from Chapter 9.8 "The Date class" of
// "Programming -- Principles and Practice Using C++" by Bjarne Stroustrup
//

#include "Chrono.h"
#include <vector>

namespace Chrono {

// member function definitions:

//------------------------------------------------------------------------------

Date::Date(int yy, Month mm, int dd)
    : y(yy), m(mm), d(dd)
{
    if (!is_date(yy,mm,dd)) throw Invalid();
}

//------------------------------------------------------------------------------

const Date& default_date()
{   static const Date dd(1970,Date::jan,1); // start of 21st century
    return dd;
}

//------------------------------------------------------------------------------

Date::Date()
    :y(default_date().year()),
     m(default_date().month()),
     d(default_date().day())
{}


//------------------------------------------------------------------------------


void Date::add_day(int n) {
    while(n) {
        switch(m) {
        case (jan) : {
            if(d+n>31) {
                n-=(31-d);
                d=0;
                m=feb;
                break;
            }
            else {
                d += n;
                return;
            }
        }
        case (feb) : {
            if(leapyear(y)) {
                if(d+n>29) {
                    n-=(29-d);
                    d=0;
                    m=mar;
                    break;
                }
                else {
                    d += n;
                    return;
                }
            }
            else {
                if(d+n>28) {
                    n-=(28-d);
                    d=0;
                    m=mar;
                    break;
                }
                else {
                    d += n;
                    return;
                }
            }
        }
        case (mar) : {
            if(d+n>31) {
                n-=(31-d);
                d=0;
                m=apr;
                break;
            }
            else {
                d += n;
                return;
            }
        }
        case (apr) : {
            if(d+n>30) {
                n-=(30-d);
                d=0;
                m=may;
                break;
            }
            else {
                d += n;
                return;
            }
        }
        case (may) : {
            if(d+n>31) {
                n-=(31-d);
                d=0;
                m=jun;
                break;
            }
            else {
                d += n;
                return;
            }
        }
        case (jun) : {
            if(d+n>30) {
                n-=(30-d);
                d=0;
                m=jul;
                break;
            }
            else {
                d += n;
                return;
            }
        }
        case (jul) : {
            if(d+n>31) {
                n-=(31-d);
                d=0;
                m=aug;
                break;
            }
            else {
                d += n;
                return;
            }
        }
        case (aug) : {
            if(d+n>31) {
                n-=(31-d);
                d=0;
                m=sep;
                break;
            }
            else {
                d += n;
                return;
            }
        }
        case (sep) : {
            if(d+n>30) {
                n-=(30-d);
                d=0;
                m=oct;
                break;
            }
            else {
                d += n;
                return;
            }
        }
        case (oct) : {
            if(d+n>31) {
                n-=(31-d);
                d=0;
                m=nov;
                break;
            }
            else {
                d += n;
                return;
            }
        }
        case (nov) : {
            if(d+n>30) {
                n-=(30-d);
                d=0;
                m=dcm;
                break;
            }
            else {
                d += n;
                return;
            }
        }
        case (dcm) : {
            if(d+n>31) {
                n-=(31-d);
                d=0;
                m=jan;
                ++y;
                break;
            }
            else {
                d += n;
                return;
            }
        }
    }
    }
}

//------------------------------------------------------------------------------

void Date::add_month(int n)
{
    //...
}

//------------------------------------------------------------------------------

void Date::add_year(int n)
{
    if (m==feb && d==29 && !leapyear(y+n)) { // beware of leap years!
        m = mar;        // use March 1 instead of February 29
        d = 1;
    }
    y+=n;
}

//------------------------------------------------------------------------------

// helper functions:

bool is_date(int y, Date::Month  m, int d) {
    // assume that y is valid

    if (d<=0) return false;            // d must be positive

    int days_in_month = 31;            // most months have 31 days

    switch (m) {
    case Date::feb:                        // the length of February varies
        days_in_month = (leapyear(y))?29:28;
        break;
    case Date::apr: case Date::jun: case Date::sep: case Date::nov:
        days_in_month = 30;                // the rest have 30 days
        break;
    }

    if (days_in_month<d) return false;

    return true;
}

//------------------------------------------------------------------------------

bool leapyear(int y)
{
    /// See exercise ???
    if(y%400==0) return true;
    if(y%100==0) return false;
    if(y%4==0) return true;
    return false;
}

//------------------------------------------------------------------------------

bool operator==(const Date& a, const Date& b) {
    return a.year()==b.year()
        && a.month()==b.month()
        && a.day()==b.day();
}

//------------------------------------------------------------------------------

bool operator!=(const Date& a, const Date& b)
{
    return !(a==b);
}

//------------------------------------------------------------------------------

ostream& operator<<(ostream& os, const Date& d)
{
    return os << '(' << d.year()
              << ',' << d.month()
              << ',' << d.day()
              << ')';
}

//------------------------------------------------------------------------------

istream& operator>>(istream& is, Date& dd)
{
    int y, m, d;
    char ch1, ch2, ch3, ch4;
    is >> ch1 >> y >> ch2 >> m >> ch3 >> d >> ch4;
    if (!is) return is;
    if (ch1!='(' || ch2!=',' || ch3!=',' || ch4!=')') { // oops: format error
        is.clear(ios_base::failbit);                    // set the fail bit
        return is;
    }
    dd = Date(y,Date::Month(m),d);     // update dd
    return is;
}

//------------------------------------------------------------------------------

enum Day {
    sunday, monday, tuesday, wednesday, thursday, friday, saturday
};

//------------------------------------------------------------------------------
/**
1.  Divide the last two digits of the year by 12.
    89 divided by 12 is 7, with 5 left over.
2.  Find how many 4's go into the remainder evenly.
    5 divided by 4 is 1, with a remainder that we ignore.
3.  Add these three numbers. 7 + 5 + 1 = 13.
4.  Divide the sum by 7 and take the remainder.
    13 divided by 7 is 1, with a remainder of 6.
5.  Add the remainder to the century's anchor day to find the
    year's Doomsday. Wednesday + 6 = Tuesday.
6.  Use the month's Doomsday to find the day you need. July's
    Doomsday is 7/11, a Tuesday, so July 13 is a Thursday.

*/
/// Return Day?
///string
/**string day_of_week(const Date& d) {
    vector<string> days{"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};
    if(leapyear(d.year()))
        vector<vector<int> > doom{{1,4},{2,29},{4,4},{5,9},{6,6},{7,11},{8,8},{9,5},{10,10},{11,7},{12,12}};
    else
        vector<vector<int> > doom{{1,3},{2,28},{4,4},{5,9},{6,6},{7,11},{8,8},{9,5},{10,10},{11,7},{12,12}};
    /// step 1.
    int x1 = (d.year()%100)/12;
    int x2 = (d.year()%100)%12;
    /// step 2
    int x3 = x2/4;
    /// step 3
    int x4 = x1+x2+x3;
    /// step 4
    int x5 = x4%7;
    /// step 5
    int i{0};
    if(d.year()/100 == 20) i=2;
    else if(d.year()/100 == 19) i = 3;
    else i = 5;
    cout << "\nhi there!";
    cout << endl << (x5+i)%7 << endl;
    return days[(x5+i)%]
    /*switch ((x5+i)%7) {
    case 0 : return sunday;
    case 1 : return monday;
    case 2 : return tuesday;
    case 3 : return wednesday;
    case 4 : return thursday;
    case 5 : return friday;
    case 6 : return saturday;
    }
    ///return sunday;
}*/

/**
For the Gregorian calendar:
    5 × (c mod 4) mod 7 + Tuesday = anchor.
For the Julian calendar:
    6 × (c mod 7) mod 7 + Sunday = anchor.
*/

string day_of_week(const Date& d) {
    vector<string> days{"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};//---
    int x1 = d.year()%100;
    if(x1%2==1) x1+=11;
    x1/2;
    if(x1%2==1) x1+=11;
    x1/2;
    x1=x1%7;
    x1=7-x1;
    int c = d.year()/100;
    int anchor = (5*(c%4))%7-1;
    return days[(anchor+x1)%7];
}

//---------------------------------------------------------------------------

Date next_Sunday(const Date& d)
{
    // ...
    return d;
}

//------------------------------------------------------------------------------

Date next_weekday(const Date& d)
{
    // ...
    return d;
}

//------------------------------------------------------------------------------

} // Chrono

//------------------------------------------------------------------------------

int main()
try
{
    Chrono::Date holiday(1978, Chrono::Date::jul, 4); // initialization
    Chrono::Date d2 = Chrono::next_Sunday(holiday);
    //Chrono::Day  d  = day_of_week(d2);
    cout << "holiday is " << holiday << " d2 is " << d2 << endl;
    Chrono::Date today(2016, Chrono::Date::may, 5);
    cout << "\nHow many days would you like to add?";
    int n{};
    while(cin >> n) {
        today.add_day(n);
        cout << "\nNow it's ";
        cout << today;
        cout << "\nThat's a " << day_of_week(today);
        cout << "\nHow many days would you like to add?";
    }
    return holiday != d2;
}
catch (Chrono::Date::Invalid&) {
    cerr << "error: Invalid date\n";
    return 1;
}
catch (...) {
    cerr << "Oops: unknown exception!\n";
    return 2;
}

//------------------------------------------------------------------------------
