<?php
namespace App\Utils\Calendar\ICal;

class Writer
{
	/**
	 * Store the calendar object
	 * @var \App\Utils\Calendar\Calendar
	 */
	protected $calendar = NULL;

	public function __construct(\App\Utils\Calendar\Calendar $calendar)
	{
		$this->calendar = $calendar;
	}


	public function generate()
	{
		// get calendar events
		$eventsList = $this->calendar->getEventsList();

		if (!$this->calendar->getNumberOfEvents()) {
			throw new \RuntimeException("Cannot write calendar file with no events. Please add at least one event.");
		}

		// compose file
		$output = '';

		$output .= "BEGIN:VCALENDAR\n";
		$output .= "VERSION:2.0\n";
		$output .= "CALSCALE:GREGORIAN\n";
		$output .= sprintf("PRODID:-//%s//%s\n", "DCS", __CLASS__);

		foreach ($eventsList as $eventObject) {
			/* @var $eventObject \App\Utils\Calendar\Event */

			$output .= "BEGIN:VEVENT\n";

			$eventUid = $eventObject->getUid();
			if (!$eventUid) {
				throw new \RuntimeException("The event id(`uid`) is mandatory.");
			}

			$output .= sprintf("UID:%s\n", $eventUid);

			$eventDtStamp = $eventObject->getDtStamp();
			if ($eventDtStamp === NULL or !$eventDtStamp->isValid()) {
				throw new \RuntimeException('The event date stamp is not valid.');
			}

			$output .= sprintf("DTSTAMP:%sZ\n", $eventDtStamp->format('Ymd\THis'));
			$output .= sprintf("SUMMARY:%s\n", $this->escapeString($eventObject->getSummary()));
			$output .= sprintf("DESCRIPTION:%s\n", $this->escapeString($eventObject->getDescription()));
			$output .= sprintf("X-ALT-DESC;FMTTYPE=text/html:%s\n", $this->escapeString($eventObject->getDescription()));
			$output .= sprintf("LOCATION:%s\n", $this->escapeString($eventObject->getLocation()));
			$output .= sprintf("CLASS:%s\n", $eventObject->getClass());

			// populate event start date
			$eventStartDateObject = $eventObject->getDtStart();
			if ($eventStartDateObject === NULL or !$eventStartDateObject->isValid()) {
				throw new \RuntimeException('The event start date is not valid.');
			}

			if (!$eventObject->getAllDay()) {
				$output .= sprintf("DTSTART:%sZ\n", $eventStartDateObject->setTimeZone(new \DateTimeZone('UTC'))->format('Ymd\THis'));
			}
			else {
				$output .= sprintf("DTSTART;VALUE=DATE:%s\n", $eventStartDateObject->format('Ymd'));
			}

			// populate duration or event end date
			$eventDurationObject = $eventObject->getDuration();
			$eventEndDateObject = $eventObject->getDtEnd();
			if ($eventDurationObject === NULL and ($eventEndDateObject === NULL or !$eventEndDateObject->isValid())) {
				throw new \RuntimeException('The event duration or event end date must be set.');
			}

			if ($eventDurationObject) {
				$output .= sprintf("DURATION:%s\n", $eventDurationObject->formatIso());
			}
			else {
				if (!$eventObject->getAllDay()) {
					$output .= sprintf("DTEND:%sZ\n", $eventEndDateObject->setTimeZone(new \DateTimeZone('UTC'))->format('Ymd\THis'));
				}
				else {
					$output .= sprintf("DTEND;VALUE=DATE:%s\n", $eventEndDateObject->format('Ymd'));
				}
			}

			// add event reminder
			foreach ($eventObject->getRemindersList() as $reminderObject) {
				/* @var $reminderObject \App\Utils\Calendar\Reminder */

				$reminderTriggerObject = 	$reminderObject->getTrigger();
				$reminderAction = 			$reminderObject->getAction();
				$reminderDescription = 		$this->escapeString($reminderObject->getDescription());

				if ($reminderTriggerObject === NULL) {
					throw new \RuntimeException("The reminder trigger is not valid.");
				}

				if ($reminderAction == 'DISPLAY' and !$reminderDescription) {
					throw new \RuntimeException("The reminder description must be set if `ACTION` is set as `DISPLAY`.");
				}

				$output .= "BEGIN:VALARM\n";

				// add trigger
				$output .= sprintf("TRIGGER:-%s\n", $reminderTriggerObject->formatIso());

				// add action
				$output .= sprintf("ACTION:%s\n", $reminderAction);

				// add description
				$output .= sprintf("DESCRIPTION:%s\n", $reminderDescription);

				$output .= "END:VALARM\n";
			}

			$output .= "END:VEVENT\n";
		}

		$output .= 'END:VCALENDAR';

		return $output;
	}

	private function escapeString($string)
	{
		$string = str_replace(["\r\n", "\n", "\r"], "\\n", $string);
		return preg_replace('/([\,;])/','\\\$1', $string);
	}

}
