#include "oswald.h"

#include "calendar.h"

unsigned char is_leap(const unsigned int year)
{
	/* the rule is, everything that can be devided by 4 is leap.
	 * Exception: the year can be devided by 100, then it is not,
	 * except it canbe devided by 400, then it is again.
	 */

	if ((year % 400) == 0)
		return 1;
	else if ((year % 100) == 0)
		return 0;
	else if ((year % 4) == 0)
		return 1;

	return 0;
}

unsigned short days_of_month(const unsigned int uMonat, const unsigned int uJahr)
{
	// invalid,January,Febuary,March,April,May,June,July,August,September,October,November,December
	int arrTageImMonat[13] = {  0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

	if (uMonat == 2) {
		// Febuary: distinguish leap
		if (is_leap(uJahr))
			return 29;
		else
			return 28;
	}

	if ((uMonat >= 1) && (uMonat <= 12))
		return arrTageImMonat[uMonat];
	else {
		return 0;
	}
}

short getAnzahlTageImJahr(const unsigned int uJahr)
{
	return (is_leap(uJahr)) ? 366 : 365;
}

short getWochentag(const unsigned int uTag, const unsigned int uMonat, const unsigned int uJahr)
{
	//                       ungült Jan Feb Mrz Apr Mai Jun Jul Aug Sep Okt Nov Dez 
	unsigned char arrMonatsOffset[13] = {  0,  0,  3,  3,  6,  1,  4,  6,  2,  5,  0,  3,  5};
	short nErgebnis = 0;

	// Monat / Tag - Plausi prüfen:
	if ((uTag > 31) || (uMonat > 12) || (uMonat <= 0) 
		|| (uTag <= 0) || (uJahr <= 0)) {
		return -1;
	}

	unsigned char cbTagesziffer = (uTag % 7);
	unsigned char cbMonatsziffer = arrMonatsOffset[uMonat];
	unsigned char cbJahresziffer = ((uJahr % 100) + ((uJahr % 100) / 4)) % 7;
	unsigned char cbJahrhundertziffer = (3 - ((uJahr / 100) % 4)) * 2;

	// Schaltjahreskorrektur:
	if ((uMonat <= 2) && (is_leap(uJahr)))
		cbTagesziffer = cbTagesziffer + 6;
  
	nErgebnis = (cbTagesziffer + cbMonatsziffer + cbJahresziffer + cbJahrhundertziffer) % 7;

	// Ergebnis:
	// 0 = Sonntag
	// 1 = Montag
	// 2 = Dienstag
	// 3 = Mittwoch
	// 4 = Donnerstag
	// 5 = Freitag
	// 6 = Samstag
	return nErgebnis;
}

short getTagDesJahres(const unsigned int uTag, const unsigned int uMonat, const unsigned int uJahr)
{
	// Der wievielte Tag des Jahres ist dieser Tag
	if ((uMonat == 0) || (uMonat > 12)) {
		return -1;
	}

	unsigned int uLokalTag = uTag;
	unsigned int uLokalMonat = uMonat;

	while (uLokalMonat > 1) {
		uLokalMonat--;
		uLokalTag += days_of_month(uLokalMonat, uJahr);
	}

	return uLokalTag;
}

short getKalenderwoche(short uTag, short uMonat, short uJahr)
{
	// Berechnung erfolgt analog DIN 1355, welche besagt:
	// Der erste Donnerstag im neuen Jahr liegt immer in der KW 1.
	// "Woche" ist dabei definiert als [Mo, ..., So].
	short nTagDesJahres = getTagDesJahres(uTag, uMonat, uJahr);

	// Berechnen des Wochentags des 1. Januar:
	short nWochentag1Jan = getWochentag(1, 1, uJahr);

	// Sonderfälle Freitag und Samstag
	if (nWochentag1Jan >= 5) 
		nWochentag1Jan = nWochentag1Jan - 7;

	// Sonderfälle "Jahresanfang mit KW - Nummer aus dem Vorjahr"
	if ( (nTagDesJahres + nWochentag1Jan) <= 1) {
		return getKalenderwoche(31, 12, uJahr - 1);
	}

	short nKalenderWoche = ((nTagDesJahres + nWochentag1Jan + 5) / 7);

	// 53 Kalenderwochen hat grundsätzlich nur ein Jahr, 
	// welches mit einem Donnerstag anfängt !
	// In Schaltjahren ist es auch mit einem Mittwoch möglich, z.B. 1992
	// Andernfalls ist diese KW schon die KW1 des Folgejahres.
	if (nKalenderWoche == 53) {
		boolean bIstSchaltjahr = is_leap(uJahr);

		if ((nWochentag1Jan  ==  4) // Donnerstag
			||  (nWochentag1Jan  == -3) // auch Donnerstag
			||  ((nWochentag1Jan ==  3) && bIstSchaltjahr)
			||  ((nWochentag1Jan == -4) && bIstSchaltjahr)) {
			; // Das ist korrekt und erlaubt
		} else
			nKalenderWoche = 1; // Korrektur des Wertes
	}

	return nKalenderWoche;
}

void getOsterdatum(const unsigned int uJahr, unsigned int *uTag, unsigned int *uMonat)
{
	// Berechnet für ein beliebiges Jahr das Osterdatum.

	// Quelle des Gauss - Algorithmus: Stefan Gerth, 
	// "Die Gauß'sche Osterregel", Nürnberg, Februar 2003. 
	// http://krapfen.org/content/paper/Schule/Facharbeit/Berechnung_des_Osterfestes.pdf

	unsigned int a = uJahr % 19;
	unsigned int b = uJahr %  4;
	unsigned int c = uJahr %  7;

	int k = uJahr / 100;
	int q = k / 4;
	int p = ((8 * k) + 13) / 25;
	unsigned int Egz = (38 - (k - q) + p) % 30; // Die Jahrhundertepakte
	unsigned int M = (53 - Egz) % 30;
	unsigned int N = (4 + k - q) % 7;

	unsigned int d = ((19 * a) + M) % 30;
	unsigned int e = ((2 * b) + (4 * c) + (6 * d) + N) % 7;

	// Ausrechnen des Ostertermins:
	if ((22 + d + e) <= 31) {
		*uTag = 22 + d + e;
		*uMonat = 3;
	} else {
		*uTag = d + e - 9;
		*uMonat = 4;

		// Zwei Ausnahmen berücksichtigen:
		if (*uTag == 26)
			*uTag = 19;
		else if ((*uTag == 25) && (d == 28) && (a > 10))
			*uTag = 18;
	}

	// Offsets für andere Feiertage:

	// Schwerdonnerstag / Weiberfastnacht -52
	// Rosenmontag -48
	// Fastnachtsdienstag -47
	// Aschermittwoch -46
	// Gründonnerstag -3
	// Karfreitag -2
	// Ostersonntag 0
	// Ostermontag +1
	// Christi Himmelfahrt +39
	// Pfingstsonntag +49
	// Pfingstmontag +50
	// Fronleichnam +60

	// Mariä Himmelfahrt ist stets am 15. August (Danke an Michael Plugge!)
}

void getViertenAdvent(const unsigned int uJahr, unsigned int *uTag, unsigned int *uMonat)
{
	// Berechnet für ein beliebiges Jahr das Datum des 4. Advents-Sonntags.
	// Der 4. Adventssonntag ist stets der Sonntag vor dem 1. Weihnachtsfeiertag,
	// muß also stets in der Periode [18. - 24.12.] liegen:
  
	*uMonat = 12; // Das steht jedes Jahr fest :-)
  
	short nWoTag = getWochentag(24, 12, uJahr); // Wochentag des 24.12. ermitteln
  
	*uTag = 24 - nWoTag;

	// Offsets: Der Buß- und Bettag liegt stets 32 Tage vor dem  4. Advent
}