#include <stdlib.h>
#include <stdio.h>
#include <string.h>

char *tage[] = { "Sonntag", "Montag", "Dienstag",
                 "Mittwoch", "Donnerstag", "Freitag", "Samstag" };

struct datum {                    /* t m j = tag monat jahr              */
  int t; int m; int j; int n;     /* n = Nummer des Tages im Jahr        */
};
typedef struct datum datum;

/* Hier ist die zweite Komponente ein int-Array der Laenge 2: */
typedef struct monat { char *name;  int n[2]; } monat;

/* Array aus struct monat : Die Zahlen geben die Anzahl Tage an,
   die nach Ende das Monats verflossen sind - in der zweiten Spalte fuer
   Schaltjahre */
monat mon[] = {
  {"",        {  0,  0}},
  {"Januar",  { 31, 31}},   {"Februar",   {59, 60}},  {"Maerz",    { 90, 91}},
  {"April",   {120,121}},   {"Mai",      {151,152}},  {"Juni",     {181,182}},
  {"Juli",    {212,213}},   {"August",   {243,244}},  {"September",{273,274}},
  {"Oktober", {304,305}},   {"November", {334,335}},  {"Dezember", {365,366}}
};

int schalt(int j) { /* gregorianisch */
  return  ( j % 4 == 0 &&  j % 100 != 0 || j % 400 == 0 );
}
int jahr_laenge(int j)  { return 365 + schalt(j); }

int mon_laenge(datum d) { /* braucht "zulaessiges" d.m */
  return mon[d.m].n[ schalt(d.j) ] - mon[d.m-1].n[ schalt(d.j) ];
}

void printdatum(datum d) { /* braucht "zulaessiges" d.m */
  printf("%d. %s, %d\n", d.t, mon[d.m].name, d.j);
}

/* berechnet die Nummer des Tages im Jahr, braucht "zulaessiges" Datum*/
void normalize(datum *p) {
  p->n = p->t + mon[ p->m - 1 ].n[ schalt(p->j) ];
}

/* Baut ein Datum und kontrolliert Zulaessigkeit: */
datum makedatum(int tag, int monat, int jahr) {
  datum d = { tag, monat, jahr, 0 };
  if ( 1 <= d.m && d.m <= 12 && 1 <= d.t && d.t <= mon_laenge(d) ) {
    normalize(&d);
    return d;
  }
  printf("Das Datum gibt es nicht : %d.%d.%d\n", tag,monat,jahr);
  exit(1);
}

/* Liefert zum Tag n >= 1 und Jahr j das Datum des n-ten Tages von j */
datum dat(int j, int n) {
  int i;
  while (n > jahr_laenge(j))
    n -= jahr_laenge(j++);
  for (i=1; i <= 12; i++)
    if (n <= mon[i].n[ schalt(j) ] )
      return makedatum( n - mon[i-1].n[ schalt(j) ], i, j);
}

/* Berechne u - v */
int diff(datum u, datum v) {
  int n, i;
  if (u.j < v.j || (u.j == v.j && u.n < v.n ) ) return -diff(v,u);
  n = u.n;
  for(i = u.j - 1; i >= v.j; i--)
    n += jahr_laenge(i);
  n -=  v.n;
  return n;
}

/* Liefert das Datum des folgenden Tages */
datum inc_tag(datum v) {
  return dat(v.j, v.n+1 );
}

datum add(datum u, int t) {
  if (t >= 0)
    return dat(u.j, u.n + t);
  printf ("Nicht implementiert (t negativ) : %d\n", t);
  exit(1);
}

int wochentag(datum d) {
  /* Gauss Formel für den 1.1. im Jahr d.j */
  int w = (1 + 5*((d.j-1)%4) + 4*((d.j-1)%100) + 6*((d.j-1)%400)) % 7;
  return (w + d.n - 1) % 7;
}

int main() {
  int t, m, j;
  datum d, dd;

  printf("Bitte Tag Monat Jahr (als Zahlen) eingeben : ");
  scanf("%d %d %d", &t, &m, &j);
  d = makedatum(t,m,j);
  printf("Das Datum ist : "); printdatum(d);
  printf("Es ist der %d-te Tag im Jahr %d.\n", d.n, d.j);
  printf("Es ist ein %s.\n", tage[ wochentag(d) ] );
  printf("Der naechste Tag ist : ");  printdatum( inc_tag(d) );
  printf ("wieviele Tage dazu ? "); scanf("%d", &t);
  dd = add(d,t);
  printdatum( dd );
  printf("Unterschied nach diff : %d\n", diff(d,dd));
}
