
//
// This is example code from Chapter 9.4.2 "Member functions and constructors" of 
// "Programming -- Principles and Practice Using C++" by Bjarne Stroustrup
//

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

#include <iostream>
#include <vector>
#include <string>
using namespace std;

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

enum class Month {
	Jan = 1, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
};

// simple Date:
// guarantee initialization with constructor
// provide some notational convenience
class Date {
    int y, d;                        // year, day
	Month m;
public:
	Date(int y, Month m, int d);          // check for valid date and initialize
	Date() { d = 1; m = Month::Jan; y = 1970; }
    void add_day(int n);                // increase the Date by n days
	bool isValid();
	Month month() const { return m; }
	int day() const  { return d; }
	int year() const { return y; }
};



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

Month int_to_month(int m) {
	if (m > 0 && m < 13) return Month(m);
	else runtime_error("bad month");
}
//------------------------------------------------------------------------------

vector<string> MonthTbl{"", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };

Month operator++(Month& m) {
	m = (m == Month::Dec) ? Month::Jan : Month(int(m) + 1);
	return m;
}

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

ostream& operator<<(ostream& osref, Month m) {
	osref << MonthTbl[int(m)];
	return osref;
}

//------------------------------------------------------------------------------
Date::Date(int y, Month m, int d) : y(y), m(m), d(d) {
	if (!isValid()) {
		y = 1970;
		d = 1;
		m = Month::Jan;
	}
}

bool isLeapYr(int y) {
	if (y % 400 == 0) return true;
	if (y % 100 == 0) return false;
	if (y % 4 == 0) return true;
	return false;
}

void Date::add_day(int n) {
	if (n == 0) return;   // base case
	if (m == Month::Dec && d == 31) {  // is it New Year's eve?
		++y;
		d = 1;
		m = Month::Jan;
		return add_day(n - 1);
	}
	switch (int(m)) {
		case 1:
		case 3:
		case 5:
		case 7:
		case 8:
		case 10:
		case 12: {
			if (d == 31) { // is it the end of the month?
				++m;
				d == 1;
				return add_day(n - 1);
				break;
			}
			++d;
			return add_day(n - 1);
			break;
		}
		case 4:
		case 6:
		case 9:
		case 11: {
			if (d == 30) { // is it the end of the month
				++m;
				d == 1;
				return add_day(n - 1);
				break;
			}
			++d;
			return add_day(n - 1);
			break;
		}
		case 2: // February
			if (isLeapYr(y) && d == 29) {
				++m;
				d = 1;
				return add_day(n - 1);
			}
			if (d = 28) {
				++m;
				d = 1;
				return add_day(n - 1);
			}
			++d;
			return add_day(n - 1);
		default:
			return;
	} // end switch
}

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



// helper functions:
//------------------------------------------------------------------------------

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

// member functions:
//------------------------------------------------------------------------------

bool Date::isValid() {
	switch (int(m)) {
	case 1:
	case 3:
	case 5:
	case 7:
	case 8:
	case 10:
	case 12: {
		if (d > 0 && d < 32) return true;
		return false;
		break;
	}
	case 4:
	case 6:
	case 9:
	case 11: {
		if (d > 0 && d < 31) return true;
		return false;
		break;
	}
	case 2:
		if (isLeapYr(y) && d > 0 && d < 30) {
			return true;
			return false;
		}
		if (isLeapYr(y) && d > 0 && d < 29) {
			return true;
			return false;
		}
	default:
		return false;
	}
}
//------------------------------------------------------------------------------

int main()
{
    Date my_birthday;                 // error: my_birthday not initialized
	cout << "my_birthday = " << my_birthday << '\n';
    //Date today(12,24,2007);             // oops! run-time error 
    Date last(2000, Month::Dec, 31);            // ok (colloquial style)
    Date christmas = Date(1976,Month::Dec,24);  // also ok (verbose style)

    //We can now try to use our newly defined variables:
	cout << "last (before increment) = " << last << '\n';
    last.add_day(1);
	cout << "last (after increment) = " << last;
	for (int i = 1; i < 5; ++i) {
		last.add_day(i);
		cout << "last (after increment) = " << last << '\n';

	}
    //add_day(2);                       // error: what day?
	Date today = Date(2019, Month(2), 11);
	Date todayCopy = today;
	cout << "Today is " << todayCopy << '\n';
	Date d0 = Date( 2019,Month(3),10 );
	cout << "March 10th will be " << d0;
	cin.get();
}

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