//=============================================================================
// Vorlaeufige Version der Klasse "date"
// Achtung - man kann noch einiges besser machen - kommt noch.

#include <array>
#include <cstdlib>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <limits>
using namespace std;

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

class date
{
public:
   date() { init(); }
   date(int d, int m, int y) { init(d, m, y); }

   void init();
   void init(int d, int m, int y);

   void print() const;
   void input();

   bool eq(const date& d) const;
   bool ne(const date& d) const { return !eq(d); }
   bool lt(const date& d) const;
   bool le(const date& d) const { return !d.lt(*this); }
   bool gt(const date& d) const { return d.lt(*this); }
   bool ge(const date& d) const { return !lt(d); }

   bool correct_date(int d, int m, int y) const;

private:
   void test() const;

   int day_;
   int month_;
   int year_;
};

//-------------------------------------------------------------------------------
// Anzahl an Tagen im Monat
// Beim Februar max. Anzahl, d.h. 29 Tage.

array<int, 12> const days { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

//-------------------------------------------------------------------------------
// Initialisiert das Datum mit dem aktuellen Datum (heute).

void date::init()
{
   time_t timer = time(0);
   tm* tblock = localtime(&timer);
   day_ = tblock->tm_mday;
   month_ = tblock->tm_mon + 1;
   year_ = tblock->tm_year + 1900;
}

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

void date::init(int d, int m, int y)
{
   day_ = d;
   month_ = m;
   year_ = y;
   test();
}

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

void date::print() const
{
   char c = cout.fill('0');
   cout << setw(2) << day_ << '.' << setw(2) << month_ << '.' << setw(4) << year_;
   cout.fill(c);
}

//-------------------------------------------------------------------------------
// Liest ein Datum in der Form t.m.j ein.
// Konnte kein sinnvolles Datum eingelesen werden, behaelt das Objekt
// seinen alten Wert, und "cin" wird auf "fail" gesetzt.

void date::input()
{
   if (cin.fail())
   {
      return;
   }

   // Tag einlesen
   int d;
   cin >> d;
   if (cin.fail() || d < 1 || d>31)
   {
      cin.setstate(ios::failbit);
      return;
   }

   // Trennzeichen '.' einlesen
   char c;
   cin >> c;
   if (cin.fail() || c != '.')
   {
      cin.setstate(std::ios::failbit);
      return;
   }

   // Monat einlesen
   // Auch gleichzeitig die Tage gegen den Monat abgleichen
   int m;
   cin >> m;
   if (cin.fail() || m < 1 || m>12 || d > days[m - 1])
   {
      cin.setstate(ios::failbit);
      return;
   }

   // Trennzeichen '.' einlesen
   cin >> c;
   if (cin.fail() || c != '.')
   {
      cin.setstate(std::ios::failbit);
      return;
   }

   // Jahr einlesen
   int y;
   cin >> y;
   if (cin.fail() || y == 0)
   {
      cin.setstate(ios::failbit);
      return;
   }

   // Passt alles zusammen?
   if (!correct_date(d, m, y))
   {
      cin.setstate(std::ios::failbit);
      return;
   }

   // Wenn ja, Eingabe uebernehmen
   year_ = y;
   month_ = m;
   day_ = d;
}

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

bool date::eq(const date& d) const
{
   if (day_ != d.day_) return false;
   if (month_ != d.month_) return false;
   return year_ == d.year_;
}

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

bool date::lt(const date& d) const
{
   if (year_ < d.year_) return true;
   if (year_ > d.year_) return false;
   if (month_ < d.month_) return true;
   if (month_ > d.month_) return false;
   return day_ < d.day_;
}

//-------------------------------------------------------------------------------
// Gibt zurueck, ob die uebergebenen Daten ein korrektes Datum ergeben.

bool date::correct_date(int d, int m, int y) const
{
   if (y == 0 || m < 1 || m>12 || d<1 || d>days[m - 1])
   {
      return false;
   }

   if (m != 2 || d != 29) return true;

   // Ueberpruefen auf Schaltjahr:
   // Ein Jahr ist ein Schaltjahr, wenn
   // - durch 4 teilbar
   // - nicht durch 100 teilbar
   // - ausser wenn durch 400 teilbar
   if (y % 400 == 0) return true;
   return (y % 4) == 0 && (y % 100) != 0;
}

//-------------------------------------------------------------------------------
// Testet das Datum, und beendet das Programm hart mit einer Fehlermeldung,
// wenn das Datum nicht okay ist.

void date::test() const
{
   if (correct_date(day_, month_, year_))
   {
      return;
   }

   cout << "Datums-Objekt ";
   print();
   cout << " ist nicht korrekt\n";
   exit(1);
}

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

void compare(const date& d1, const date& d2)
{
   cout << "\nd1: ";
   d1.print();
   cout << '\n';

   cout << "d2: ";
   d2.print();
   cout << '\n';

   cout << "- d1 ist " << (d1.eq(d2) ? "" : "nicht ") << "gleich d2\n";
   cout << "- d1 ist " << (d1.ne(d2) ? "" : "nicht ") << "ungleich d2\n";
   cout << "- d1 ist " << (d1.lt(d2) ? "" : "nicht ") << "kleiner d2\n";
   cout << "- d1 ist " << (d1.le(d2) ? "" : "nicht ") << "kleiner-gleich d2\n";
   cout << "- d1 ist " << (d1.gt(d2) ? "" : "nicht ") << "groesser d2\n";
   cout << "- d1 ist " << (d1.ge(d2) ? "" : "nicht ") << "groesser-gleich d2\n";
}

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

int main()
{
   cout << "Aufg_14_01_Klasse_Date\n\n";

   date today;
   date christmas(24, 12, 2020);

   cout << "Programm-Abbruch mit Eingabe von: ";
   christmas.print();
   cout << "\\nnHeute: ";
   today.print();
   cout << "\n\n";

   compare(today, christmas);

   for (date d; d.ne(christmas); )
   {
      cout << "\nDatum: ";
      d.input();
      if (cin.fail())
      {
         cout << "- Fehler\n- d: ";
         d.print();
         cout << '\n';
         cin.clear();
         cin.ignore(numeric_limits<streamsize>::max(), '\n');
      }
      else
      {
         cout << "- okay\n- d: ";
         d.print();
         cout << '\n';
         compare(today, d);
      }
   }

   cout << "\nFertig\n";
}
