#pragma once

#include <string>

class Date {
 private:
    int year;
    int month;
    int day;
    bool leap;
    int lengths[13] = {0, 31, 28, 31, 30, 31, 31, 30, 31, 30, 31, 30, 31};

 public:
    Date(int year, int month, int day) {
        this->year = year;
        this->month = month;
        this->day = day;
        this->leap = this->updateLeap(this->year);
        if (this->leap) {
            this->lengths[2] = 29;
        }
    }

    bool updateLeap(int year) {
        bool l;
        if (year % 400 == 0) {
            l = true;
        } else if (year % 100 == 0) {
            l = false;
        } else if (year % 4 == 0) {
            l = true;
        } else {
            l = false;
        }
        return l;
    }

    bool IsLeap() const {
        return this->leap;
    }

    std::string ToString() const {
        std::string date;
        if (this->day < 10) {
            date += "0";
        }
        date += std::to_string(this->day);
        date += ".";
        if (this->month < 10) {
            date += "0";
        }
        date += std::to_string(this->month);
        date += ".";
        for (unsigned int i = 4; i > std::to_string(this->year).length(); i--) {
            date += "0";
        }
        date += std::to_string(this->year);
        return date;
    }

    Date DaysLater(int days) const {
        Date newDate(this->year, this->month, this->day);
        for (int i = days; i > 0; i--) {
            newDate.day++;
            if (newDate.day > newDate.lengths[newDate.month]) {
                newDate.day -= newDate.lengths[newDate.month];
                newDate.month++;
                if (newDate.month > 12) {
                    newDate.month = 1;
                    newDate.year++;
                    newDate.leap = newDate.updateLeap(newDate.year);
                }
            }
        }
        return newDate;
    }

    int DaysLeft(const Date& date) {
        int difference = 0;
        int dir;
        this->year < date.year ? dir = 1 : dir = -1;
        for (int y = this->year; y != date.year; y += dir) {
            if (this->month < 3 && this->updateLeap(y)) {
                difference += 366 * dir;
            } else if (this->updateLeap(y + dir)) {
                difference += 366 * dir;
            } else {
                difference += 365 * dir;
            }
        }

        this->month < date.month ? dir = 1 : dir = -1;
        for (int m = this->month; m != date.month; m += dir) {
            if (m == 2 && this->updateLeap(date.year) && dir == 1) {
                difference += 29;
            } else if (m == 3 && this->updateLeap(date.year) && dir == -1) {
                difference -= 29;
            } else if (dir == 1) {
                difference += this->lengths[m];
            } else if (dir == -1) {
                difference -= this->lengths[m - 1];
            }
        }

        this->day < date.day ? dir = 1 : dir = -1;
        for (int d = this->day; d != date.day; d += dir) {
            difference += dir;
        }
        return difference;
    }
};