#include <stdio.h>
#include <acl/file.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>

// der Makro "panicio" gibt einen Ein-/ Ausgabefehler aus und beendet das Programm mit einem Fehlercode.
#define panicio	{\
	perror(*filepath);\
	return 1;\
}

/* Ein Makro, der die nächste Zahl der Eingabedatei zurückgibt.
 * In der Eingabedatei werden die Zahlen immer durch ein anderes Zeichen getrennt (Leerzeichen, newline-character, Punkt).
 * Da die Funktion "strtol" den Zeiger "nextnumber" immer auf das erste Zeichen nach der verarbeiteten Zahl zeigen lässt,
 * muss "nextnumber" um eins erhöht werden, um auf die nächste Zahl zu zeigen.*/
#define getNumber strtol(nextnumber + 1, &nextnumber, 10)

// Struktur, die ein Hotel darstellt.
struct hotel {
	uint_least16_t minutes;
	uint_least8_t rating;
};

int main(int argc, char *argv[]) {
	// Schleife iteriert über die Argumente, die Dateipfade sind.
	for(char **filepath = argv + 1; filepath - argv < argc; ++filepath) {
		uint_least16_t numberHotels, totalMinutes;
		struct hotel *hotels;
		printf("%s:\n", *filepath);
		{
			char *inputText;
			{
				FILE *inputFile = fopen(*filepath, "r"); // öffnet die Datei
				if(inputFile == NULL) panicio; // gibt möglichen Fehler aus
				bool success;

				/* Die Funktion "acl_ReadTextFile" liest die Datei aus und gibt den Inhalt als String zurück.
				 * Die Variable "success" wird je nach Erfolg modifiziert.*/
				inputText = acl_ReadTextFile(inputFile, &success);
				if(!success) panicio;
				fclose(inputFile); // schließt die Datei
			}
			{
				char *nextnumber;

				/* Die Anzahl der Hotels muss noch den Makro "getNumber" verarbeitet werden,
				 * da dieser vorraussetzt, das der Zeiger "nextnumber" bereits definiert ist.
				 * Zudem wird noch Eins addiert, weil noch ein "Hotel" hinzugefügt wird,
				 * das den Zielpunkt darstellt.*/
				numberHotels = strtol(inputText, &nextnumber, 10) + 1;
				totalMinutes = getNumber; // gesamte Fahrzeit

				// belegt den Speicher für ein Array mit allen Hotels
				hotels = malloc(numberHotels * sizeof *hotels);

				// jedem Hotel des Arrays werden die jeweiligen Werte zugewiesen
				for(uint_least16_t i = 0; i < numberHotels; ++i) {
					hotels[i].minutes = getNumber;

					/* "strtol" verarbeitet nur ganze Zahlen. Deshalb werden die beiden Ziffern einzeln
					 * verarbeitet. Da für die Bewertung ein Integer-Typ verwendet wird,
					 * ist die resultierende Bewertung das zehnfache der Bewertung in der Eingabedatei.
					 * Diese wird bei der Ausgabe wieder umgewandelt.*/
					hotels[i].rating = 10 * getNumber;
					hotels[i].rating += getNumber;
				}
			}
			free(inputText); // freigeben des Speichers der ausgelesenen Textdatei

			/* fügt den Zielort als "Hotel" hinzu, da der Algorithmus ein Hotel erst dann berücksichtigt,
			 * wenn das nächste Hotel außer Fahrtreichweite ist. Es gibt allerdings kein nächstes Hotel
			 * vom letzten Hotel, weshalb das letzte Hotel als Haltepunkt nicht in Betracht gezogen wird.
			 * Also wird der Zielort als "Hotel" hinmzugefügt, damit auch das letzte richtige Hotel
			 * verwendet werden kann.*/
			hotels[numberHotels - 1] = (struct hotel){ .minutes = totalMinutes, .rating = 50};
		}
		{
			struct hotel hotelRoute[4]; // speichert die Hotels, an denen gehalten wird
			uint_least8_t stoppedHotels; // Anzahl der gemachten Stops

			/* In der Schleife wird versucht eine Route mit der Bewertung von mindestens 50 zu bilden.
			 * Wenn dies nicht erfolgrecih ist, wird es mit 49, 48, ... versucht bis eine Route gefunden wurde.*/
			{
				uint_least8_t rating = 50;
				do {
					// Minuten die das letzte Hotel, bei dem angehalten wurde, vom Start entfertn ist
					uint_least16_t lastHotelMinutes = 0;
					// Zeiger zum letzten Hotel, dessen Bewertung mindestens "rating" ist.
					struct hotel *possibleStop = NULL;
					stoppedHotels = 0; // setzt Anzahl der Stops zurück

					// iteriert über die Hotels
					for(struct hotel *currentHotel = hotels; currentHotel - hotels < numberHotels; ++currentHotel) {

						/* Wenn das aktuelle Hotel mehr als 360 Minuten vom letzten Hotel entfernt ist,
						 * muss das letzte Hotel verwendet werden an dem das Halten möglich war.*/
						if(currentHotel->minutes - lastHotelMinutes > 360) {
							/* Wenn das Halten nicht möglich ist oder bereits schon viermal
							 * gehalten wurde, muss es mit einer niedrigeren Bewertung versucht
							 * werden.*/
							if(possibleStop == NULL || stoppedHotels >= 4) break;
							// hängt das Hotel der Route an
							hotelRoute[stoppedHotels] = *possibleStop;
							lastHotelMinutes = possibleStop->minutes;
							// springt in der Schleife zurück zum Hotel, an dem gestoppt wurde
							currentHotel = possibleStop;
							++stoppedHotels;
						}
						// Wenn die Bewertung hoch genug ist, kann gestoppt werden.
						if(currentHotel->rating >= rating) possibleStop = currentHotel;
					}
					--rating;
				/* Schleife läuft solange "rating" mindestens Eins ist, da es keine niedrigeren Bewertungen
				 * gibt. Wenn das letzte Hotel höchtens 360 Minuten vom Zielort entfernt ist, wurde
				 * erfolgreich eine Route gefunden und die Schleife wird beendet. Allerdings wird dies nur
				 * überprüft, wenn überhaupt an einem Hotel gehalten wurde, um den Zugriff auf nicht
				 * definierten Speicher zu vermeiden.*/
				} while(rating && (!stoppedHotels || totalMinutes - hotelRoute[stoppedHotels - 1].minutes > 360));
			}
			// gibt die Hotels der Route aus.
			for(struct hotel *hotelPrint = hotelRoute; hotelPrint - hotelRoute < stoppedHotels; ++hotelPrint) {
				printf("Entfernung vom Start: %" PRIuLEAST16 " Minuten, Bewertung: %.1f\n", hotelPrint->minutes, (double)hotelPrint->rating / 10);
			}
		}
		free(hotels); // gibt den Speicher des Hotelarrays frei
	}
}
