Date/Date/Date.swift

//
//  Date.swift
//  Date
//
//  Created by Mark Meretzky on 10/17/18.
//  Copyright © 2018 New York University School of Professional Studies. All rights reserved.
//

import Foundation;

class Date: CustomStringConvertible {

    //year, month, and day are stored properties.
    var year: Int;

    var month: Int {
        willSet(newMonth) {    //a property observer
            if newMonth < 1 || newMonth > 12 {
                print("bad month \(newMonth)");
            }
        }
    }

    var day: Int {
        willSet(newDay) {
            if newDay < 1 || newDay > monthLength() {
                print("bad day \(newDay) for month \(month)");
            }
        }
    }

    //description is a read-only computed property.

    var description: String {
        return "\(month)/\(day)/\(year)";
    }

    init(month: Int, day: Int, year: Int) {
        self.year = year;    //self.year is the property, year is the argument
        self.month = month;
        self.day = day;
    }

    //Put today's year, month, and day into the newborn Date object.

    init() {
        let calendar: Calendar = Calendar.current;
        let today: Foundation.Date = Foundation.Date();   //Apple's class Date, not ours.
        let dateComponents: DateComponents = calendar.dateComponents([.year, .month, .day], from: today);
        year  = dateComponents.year!;
        month = dateComponents.month!;
        day   = dateComponents.day!;
    }

    /*
     Return the number of days in the month described by the year and month properties. For example,
     If year = 2018 and month = 2, then this method returns 28.
     if year = 2020 and month = 2, then this method returns 29.
     */

    func monthLength() -> Int {
        let calendar: Calendar = Calendar.current;
        let dateComponents: DateComponents = DateComponents(year: year, month: month, day: day);
        let date: Foundation.Date = calendar.date(from: dateComponents)!;
        let range: Range = calendar.range(of: .day, in: .month, for: date)!;
        return range.count;
    }

    //Advance this Date one day into the future.
    //This method accepts no arguments.

    func next() -> Void {
        if day < monthLength() {
            day += 1;    //means day = day + 1;
            return;
        }

        day = 1;
        if month < Date.yearLength() {
            month += 1;
            return;
        }

        month = 1;
        year += 1;
    }

    /*
     Advance this Date many days into the future.
     This method accepts one argument.
     It does the bulk of its work by calling the above method over and over.
     */

    func next(_ distance: Int) {
        if distance < 0 {
            print("argument \(distance) of next must be non-negative");
            return;
        }

        for _ in 1 ... distance {    //Loops distance times.  No need to name the counting variable.
            next();
        }
    }

    // Return the number of months in a year.  A type method is marked with the keyword "class".

    class func yearLength() -> Int {
        return 12;
    }
}