<?php

/**
 * file: getEvents.php
 * date: 20.11.2022
 * user: bernd@nr18.space
 */

ini_set('log_errors', 1);
ini_set('error_log', '../log/events.log');
error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING & ~E_DEPRECATED);

use Sabre\VObject;

include '/usr/share/php/Sabre/VObject/autoload.php';

define ("ICAL_FILE", "krautspace.ics");

/**
 * Reads the content of the given ical file and creates with this string a
 * vcalendar object.
 *
 * @return vCalendar object | null
 */
function initCalendar(): ?object {
    try {
        $data = file_get_contents(ICAL_FILE);
        $vcalendar = VObject\Reader::read(
            $data
        );
        return $vcalendar;
    } catch (Throwable $th) {
        error_log("Failed to read calendar file.");
        error_log($th->getMessage(), 0);
        return null;
    }
}

/**
 * Grabs all VEVENTS from vCalendar object, append they to a list and
 * returns the list.
 *
 * @param vCalendar object
 * @return list | null
 */
function grabEvents(object $vcalendar): ?array {
    $eventlist = [];
    try {
        foreach ($vcalendar->VEVENT as $event) {
            $eventlist[] = $event;
        }
    } catch (Throwable $th) {
        error_log("Failed to grab evens.");
        error_log($th->getMessage(), 0);
        return null;
    }
    return $eventlist;
}

/**
 * Becomes an instance from recurrency rule iterator and an datetime object.
 * Until the iterators date is in future, we call for the next date.
 *
 * @param iterator object
 * @param datetime
 * @return datetime | null
 */
function getNextDate(object $rriterator, DateTime $now): ?DateTime {
    foreach ($rriterator as $item) {
        if ($item > $now) {
            return $item;
        }
    }
    return null;
}

/**
 * Main function to display the events.
 *
 * @param array
 * @param object
 */
function printEvent(array $eventarray, object $date_helper) {
    $start_datetime = $eventarray[0];
    $event = $eventarray[1];
    $day = toGerman($start_datetime->format("l"));
    $start = $start_datetime->format("d.m.Y H:i");
    $hyph = " - ";
    $uhr = " Uhr";
    $komma = ", ";
    $dateline = "{$day}{$komma}{$start}{$uhr}{$hyph}";

    if ($event->URL != "") {
        printHeadline($dateline, $event->SUMMARY, $event->URL);
    } else {
        printHeadline($dateline, $event->SUMMARY);
    }
    if ($event->DESCRIPTION != "") {
        printDescription($event->DESCRIPTION);
    }
    if ($event->LOCATION != "") {
        printLocation($event->LOCATION);
    }
    printBottomline();
}

/**
 * Outputs the opening tags for section and ul, the date and time, and
 * summary. ifi a url is given, the summary is a link to the corresponding
 * wiki page.
 *
 * @param string
 * @param string
 */
function printHeadline(string $dateline, string $summary, string $url=null) {
    echo("\n<section class='termin'>");
    if ($url == null) {
        echo("\n<p class='headline'>$dateline$summary</p>");
    } else {
        echo("\n<p class='headline'>$dateline<a href='$url'>$summary</a></p>");
    }
    echo("\n<ul class='events'>");
}

/**
 * Outputs the events description.
 *
 * @param string
 */
function printDescription(string $description) {
    echo("\n<li>$description</li>");
}

/**
 * Outputs the events location.
 *
 * @param string
 */
function printLocation(string $location) {
    $location = makeLinks($location);
    echo("\n<li>$location</li>");
}

/**
 * Outputs the closing tags for ul and section.
 */
function printBottomline() {
    echo("\n</ul>");
    echo("\n</section>\n");
}

/**
 * Helper function to sort the events chronologically. Compares the first
 * entry from the event array (DateTime object).
 *
 * @param array
 * @param array
 * @return integer
 */
function compareEventStart(array $event_a, array $event_b): int {
    // echo("Compare " . $event_a[0] . " and " . $event_b[0] . "\n");
    $a = $event_a[0];
    $b = $event_b[0];
    return $a <=> $b;
}

/**
 * Becomes the description string and makes urls contained in this string
 * clickable. If anything goes wrong it returns the origin string.
 *
 * @param string
 * @return string
 */
function makeLinks(string $description) {
    try {
        $reg_pattern = "/(((http|https)\:\/\/)|(www\.))[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,5}(\:[a-zA-Z0-9]+)?(\/\S*)?/";
        return $new = preg_replace($reg_pattern, '<a href="$0" target="_blank" rel="noopener noreferrer">$0</a>', $description);
    } catch (Throwable $th) {
        return $description;
    }
}

/**
 * Translates the english weekday names to german.
 *
 * @param string
 * @return string | null
 */
function toGerman(string $day): ?string
{
    switch ($day)
    {
        case 'Monday':
            return 'Montag';
        case 'Tuesday':
            return 'Dienstag';
        case 'Wednesday':
            return 'Mittwoch';
        case 'Thursday':
            return 'Donnerstag';
        case 'Friday':
            return 'Freitag';
        case 'Saturday':
            return 'Samstag';
        case 'Sunday':
            return 'Sonntag';
        default:
            return null;
    }
}


///////////////////////////////////////////////////////////////////////////
/// start des programmes
///////////////////////////////////////////////////////////////////////////

/**
 * The main function.
 *
 */
function printEventList(): bool {
    // error_log("getEvents called");
    date_default_timezone_set("Europe/Berlin");
    $today_datetime = new DateTime();
    date_time_set($today_datetime, 0, 0, 0, 0);
    $date_helper = new VObject\DateTimeParser();
    $eventlist = [];
    $next_events = [];

    // einlesen der kalenderdatei

    $vcalendar = initCalendar();
    if ($vcalendar == null) {
        error_log("getEvents unsuccessful terminated");
        return false;
    }

    // die events aus dem kalender herausziehen

    $eventlist = grabEvents($vcalendar);
    if ($eventlist == null) {
        error_log("grabEvent returns null");
        return false;
    }
    elseif (count($eventlist) == 0) {
        echo("\n<p>Keine Termine gefunden</p>\n");
        return false;
    }

    // durch die liste der events laufen. wir holen uns den starttermin und
    // wandeln ihn in ein datetime objekt um. liegt das in der vergangenheit
    // und der termin hat eine rrule, holen wir uns einen iterator über die
    // rrule (dazu brauchen wir die uid) und versuche den nächsten gültigen
    // termin zu bekommen. gibt es einen gültigen termin, wird das event mit
    // dem neuen starttermin an die liste der relevanten termine angehängt.
    // ist der starttermin von anfang an in der zukunft, dann wird er
    // natürlich auch angehängt.

    foreach ($eventlist as $event) {
        $next_event = [];
        $e_uid = $event->UID;
        $e_start = $event->DTSTART;
        $e_summary = $event->SUMMARY;
        $e_description = $event->DESCRIPTION;
        $e_location = $event->LOCATION;
        $e_url = $event->URL;
              $start_datetime = $date_helper->parseDateTime($e_start);
              if ($start_datetime < $today_datetime) {
                  if (isset($event->RRULE)) {
                      $rriter = new VObject\RecurrenceIterator($vcalendar, $e_uid);
                      $start_datetime = getNextDate($rriter, $today_datetime);
                      if ($start_datetime == null) {
                          continue;
                      } else {
                          array_push($next_events, array($start_datetime, $event));
                      }
                  }
              } else {
                  array_push($next_events, array($start_datetime, $event));
              }
        }

        // liste der anstehenden events nach dem begin sortieren

        usort($next_events, "compareEventStart");

        // die sortierte liste ausgeben

        foreach ($next_events as $event) {
            printEvent($event, $date_helper);
        }
        // error_log("getEvents successful terminated");
        return true;
}

