#include "..\std_lib_facilities.h"





/**bool operator==(const Month& lhs, const Month& rhs) {
    if(Month::lhs == Month::rhs) return true;
    return false;
}*/

vector<string> month_tbl{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};



enum class Day {
    Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
};




class Date {
public:
    class Invalid { }; /// to be used as exception
    enum class Month {
        Jan=1, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
    };
    Date(int y, Month m, int d); /// check for valid date and initialize
    Date();                      /// default constructor
    void add_one_day(); /// called by add_days()
    void add_days(int n); /// increase the Date by n days
    Month month() const { return Month(int(m)-1); }
    int day() const { return d; }
    int year() const { return y; }
    void showDate();
    void operator=(Date& dd) {
        y = dd.year(); m = dd.month(); d = dd.day();
    }
    //Month operator++(Month& m);
private:
    int y,d;  /// year, day
    Month m;
    bool is_valid();  /// return true if date is valid
};

/*void Date::showDate() {
    cout << '(' << m << "," << d << "," << y << ')';
}*/

ostream& operator<<(ostream& os, Date::Month m) {
    return os << month_tbl[int(m)];
}

ostream& operator<<(ostream& os, const Date& dd) {
    return os << '(' << dd.year()
              << ',' << dd.month()
              << ',' << dd.day()
              << ')';
}

Date::Month int_to_month(int x) {
    if (x<int(Date::Month::Jan) || int(Date::Month::Dec)<x) error("bad month");
    return Date::Month(x);
}

Date::Month operator++(Date::Month& m) { /// prefix increment operator
   m = (m==Date::Month::Dec) ? Date::Month::Jan : Date::Month(int(m)+1); /// "wrap around"
   return m;
}

bool isLeapYear(int y) {
    if(y%4!=0) return false;
    else if(y%100!=0) return true;
    else if(y%400!=0) return false;
    else return true;
}



bool Date::is_valid() {
    if(m>Month::Dec || m < Month::Jan || d<1) return false;
    if(m==Month::Feb) {
        if(isLeapYear(y))
            if(d>29) return false;
        else if(d>28) return false;
    }
    if(m==Month::Jan||m==Month::Mar||m==Month::May
     ||m==Month::Jul||m==Month::Aug||m==Month::Oct||m==Month::Dec)
        if(d>31) return false;
    else if (d>30) return false;
    return true;
}

/*bool is_valid(int y, Date::Month  m, int d) {
    /// assume that y is valid
    if (d<1) return false;            /// d must be positive
    int days_in_month = 31;           /// most months have 31 days
    switch (m) {
    case Date::Month::Feb:                        // the length of February varies
        days_in_month = (isLeapYear(y))?29:28;
        break;
    case Date::Month::Apr: case Date::Month::Jun: case Date::Month::Sep: case Date::Month::Nov:
        days_in_month = 30;                // the rest have 30 days
        break;
    }

    if (d>days_in_month) return false;

    return true;
}*/

Date::Date(int yy, Month mm, int dd): y(yy), m(mm), d(dd) {
/// check that (y,m,d) is a valid date
    if (!is_valid()) throw Invalid{}; /// check for validity
}

///================================================
/// increase dd by 1 day
void Date::add_one_day() {
    if(m==Month::Feb) {
        if(d > 29) {
            d=0;
            ++m;
            ///cout << '*' << endl;
        }
        else if(isLeapYear(y) && d==28) {
            ++d;  /// Note that this will cause it to become Feb. 30 after the next inc.
        }
        else if(d==28) {
            d=0;
            ++m;
            ///cout << '^' << endl;
        }
    }
    else if((m==Month::Jan||m==Month::Mar||m==Month::May
           ||m==Month::Jul||m==Month::Aug||m==Month::Oct)&&d==31) {
        d=0;
        ++m;
        ///cout << '%' << endl;
    }
    else if(m==Month::Dec && d==31) {
        ///cout << "\nhaha\n";
        d=0;
        m=Month::Jan;
        ++y;
    }
    else if((m==Month::Apr||m==Month::Jun||m==Month::Sep||m==Month::Nov) && d==30) {
        d=0;
        ++m;
        ///cout << '+' << endl;
    }
    ++d;
    ///cout << '.';
}

/// increase dd by n days
void Date::add_days(int n) {
    for(int i = 0; i < n; ++i)
        add_one_day();
}

//------------------------------------------------------------------------------

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

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

Date::Date() ///Default contructor uses default date
    :y(default_date().year()),
     m(default_date().month()),
     d(default_date().day())
{}

