<?php

namespace Ig\IgReservations\Domain\Model;

use Ig\IgReservations\Domain\Repository\EventRepository;
use Ig\IgReservations\Utility\EventUtility;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;

/**
 * Calendar
 */
class Calendar extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity
{
    protected int $pageUid = 0;
    protected bool $isPublic = true;
    protected bool $hideInList = false;
    protected string $name = '';
    protected string $label = '';
    protected int $maxReservations = 0;
    protected int $timeslotStartTime = 0;
    protected int $timeslotEndTime = 0;
    protected int $timeslotBlockDuration = 0;
    protected string $timeslotBuffer = '';
    protected string $conf = '';
    protected ?array $confParsed = null;
    protected ?array $calendarRow = null;
    protected string $defaultView = 'week';
    protected array $viewDays = [1, 2, 3, 4, 5, 6, 7];
    protected ?CalendarType $calendarType = null;
    protected ?ObjectStorage $calendarTimeslots = null;
    protected ?ObjectStorage $calendarAcl = null;
    protected ?CalendarAcl $acl = null;
    protected ?array $fullcalendarSettings = null;
    protected string $mailsToSend = '';
    protected ?array $mailsToSendArray = null;
    protected ?Room $room = null;
    protected ?ObjectStorage $blockingTimes = null;
    protected ?FeUser $frontendUser = null;
    protected ?array $events = null;
    protected ?array $permissionsByName = null;

    /**
     * __construct
     */
    public function __construct()
    {
        //Do not remove the next line: It would break the functionality
        $this->initStorageObjects();
    }

    /*
     * Entries sind mit startDate,startTime und endDate, endTime gespeichert
     */
    public function getIsDateTimeMode(): bool
    {
        return $this->calendarType->getMode() == 2;
    }
    /*
     * Entries sind mit timeslot Uid(s) gespeichert
     */
    public function getIsTimeslotMode(): bool
    {
        return $this->calendarType->getMode() == 1;
    }

    /**
     * Returns the name
     *
     * @return string
     */
    public function getName(): string
    {
        return $this->name;
    }

    /**
     * Sets the name
     *
     * @param string $name
     */
    public function setName($name): void
    {
        $this->name = $name;
    }

    /**
     * Get the value of maxReservations
     */
    public function getMaxReservations(): int
    {
        return $this->maxReservations;
    }

    /**
     * Set the value of maxReservations
     *
     * @param int $maxReservations
     */
    public function setMaxReservations($maxReservations): void
    {
        $this->maxReservations = $maxReservations;
    }
    /**
     * Returns the timeslotStartTime
     *
     * @return int timeslotStartTime
     */
    public function getTimeslotStartTime(): int
    {
        return $this->timeslotStartTime;
    }

    /**
     * Sets the timeslotStartTime
     */
    public function setTimeslotStartTime(int $timeslotStartTime): void
    {
        $this->timeslotStartTime = $timeslotStartTime;
    }

    /**
     * Returns the timeslotEndTime
     *
     * @return int timeslotEndTime
     */
    public function getTimeslotEndTime(): int
    {
        return $this->timeslotEndTime;
    }

    /**
     * Sets the timeslotEndTime
     */
    public function setTimeslotEndTime(int $timeslotEndTime): void
    {
        $this->timeslotEndTime = $timeslotEndTime;
    }

    /**
     * Returns the timeslotBlockDuration
     *
     * @return int timeslotBlockDuration
     */
    public function getTimeslotBlockDuration(): int
    {
        return $this->timeslotBlockDuration;
    }
    public function getTimeslotBlockDurationTime(): int
    {
        return $this->timeslotBlockDuration;
    }

    /**
     * Sets the timeslotBlockDuration
     */
    public function setTimeslotBlockDuration(int $timeslotBlockDuration): void
    {
        $this->timeslotBlockDuration = $timeslotBlockDuration;
    }

    /**
     * Returns the calendarType
     *
     * @return \Ig\IgReservations\Domain\Model\CalendarType
     */
    public function getCalendarType(): ?CalendarType
    {
        return $this->calendarType;
    }

    /**
     * Sets the calendarType
     */
    public function setCalendarType(CalendarType $calendarType): void
    {
        $this->calendarType = $calendarType;
    }

    /**
     * Adds a CalendarTimeslots
     */
    public function addCalendarTimeslot(CalendarTimeslots $calendarTimeslot): void
    {
        $this->calendarTimeslots->attach($calendarTimeslot);
    }

    /**
     * Removes a CalendarTimeslots
     *
     * @param \Ig\IgReservations\Domain\Model\CalendarTimeslots $calendarTimeslotToRemove The CalendarTimeslots to be removed
     */
    public function removeCalendarTimeslot(CalendarTimeslots $calendarTimeslotToRemove): void
    {
        $this->calendarTimeslots->detach($calendarTimeslotToRemove);
    }

    /**
     * Returns the calendarTimeslots
     *
     * @return \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Ig\IgReservations\Domain\Model\CalendarTimeslots> $calendarTimeslots
     */
    public function getCalendarTimeslots(): ObjectStorage
    {
        return $this->calendarTimeslots;
    }

    /**
     * Sets the calendarTimeslots
     *
     * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Ig\IgReservations\Domain\Model\CalendarTimeslots> $calendarTimeslots
     */
    public function setCalendarTimeslots(ObjectStorage $calendarTimeslots): void
    {
        $this->calendarTimeslots = $calendarTimeslots;
    }

    /**
     * Returns the timeslots
     *
     * @param int $weekday Weekday to match
     *
     * @return \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Ig\IgReservations\Domain\Model\Timeslot> $timeslots
     */
    public function getTimeslots(int $weekday): ?ObjectStorage
    {
        foreach ($this->calendarTimeslots as $calendarTimeslots) {
            if ($calendarTimeslots->isMatchByWeekday($weekday)) {
                return $calendarTimeslots->getTimeslots();
            }
        }
        foreach ($this->calendarTimeslots as $calendarTimeslots) {
            if ($calendarTimeslots->isMatchNoRestrictions()) {
                return $calendarTimeslots->getTimeslots();
            }
        }
        return null;
    }

    /**
     * Adds a CalendarAcl
     */
    public function addCalendarAcl(CalendarAcl $calendarAcl): void
    {
        $this->calendarAcl->attach($calendarAcl);
    }

    /**
     * Removes a CalendarAcl
     *
     * @param \Ig\IgReservations\Domain\Model\CalendarAcl $calendarAclToRemove The CalendarAcl to be removed
     */
    public function removeCalendarAcl(CalendarAcl $calendarAclToRemove): void
    {
        $this->calendarAcl->detach($calendarAclToRemove);
    }

    /**
     * Returns the calendarAcl
     *
     * @return \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Ig\IgReservations\Domain\Model\CalendarAcl> $calendarAcl
     */
    public function getCalendarAcl(): ObjectStorage
    {
        return $this->calendarAcl;
    }
    public function getFullcalendarSettings(): array
    {
        if ($this->fullcalendarSettings === null) {
            $acl = $this->getAcl();
            $this->fullcalendarSettings = [];
            $items = $GLOBALS['TCA']['tx_igreservations_domain_model_calendaracl']['columns']['fullcalendar']['config']['items'];
            foreach ($items as $item) {
                $name = $item['value'] ?? $item[1];
                $this->fullcalendarSettings[$name] = false;
            }
            $this->fullcalendarSettings = array_merge($this->fullcalendarSettings, $acl->getFullcalendarArray());
        }
        return $this->fullcalendarSettings;
    }

    /**
     * returns an array of all permissions in the form ['permissionName' => true/false, ...]
     */
    public function getPermissions(): array
    {
        return $this->getAcl()->getPermissionsByName();
    }

    
    public function getAcl(): ?CalendarAcl
    {
        if ($this->acl === null) {
            $context = GeneralUtility::makeInstance(Context::class);
            $feUserGroups = $context->getPropertyFromAspect('frontend.user', 'groupIds');
            $isLoggedIn = $context->getPropertyFromAspect('frontend.user', 'isLoggedIn');
            foreach ($this->calendarAcl as $calendarAcl) {
                if ($isLoggedIn && $calendarAcl->getFrontendGroup() && in_array(
                    $calendarAcl->getFrontendGroup(),
                    $feUserGroups
                )) {
                    if ($this->acl !== null) {
                        if ($calendarAcl->getIsAdmin()) {
                            $this->acl = $calendarAcl;
                        } else {
                            // @todo merge correctily
                            // merge permissions
                            $permissions1 = $calendarAcl->getPermissionsByName();
                            $permissions2 = $this->acl->getPermissionsByName();
                            $permissions = $permissions1;
                            foreach ($permissions2 as $name  => $value) {
                                if ($value) {
                                    $permissions[$name] = $value;
                                }
                            }
                            //$permissions = array_unique(array_merge($permissions1, $permissions2));
                            $this->acl->setPermissionsByName($permissions);


                            // merge fullacalendar default settings
                            $fullcalendar1 = $calendarAcl->getFullcalendarArray();
                            $fullcalendar2 = $this->acl->getFullcalendarArray();
                            $fullcalendar = $fullcalendar1;
                            foreach ($fullcalendar2 as $name  => $value) {
                                if ($value) {
                                    $fullcalendar[$name] = $value;
                                }
                            }
                            $this->acl->setFullcalendarArray($fullcalendar);
                            

                            if ($calendarAcl->getShowBeginDateTime() < $this->acl->getShowBeginDateTime()) {
                                $this->acl->setShowBeginDateTimeModifier($calendarAcl->getShowBeginDateTimeModifier());
                            }
                            if ($calendarAcl->getShowEndDateTime() > $this->acl->getShowEndDateTime()) {
                                $this->acl->setShowEndDateTimeModifier($calendarAcl->getShowEndDateTimeModifier());
                            }
                            if ($calendarAcl->getBookBeginDateTime() < $this->acl->getBookBeginDateTime()) {
                                $this->acl->setBookBeginDateTimeModifier($calendarAcl->getBookBeginDateTimeModifier());
                            }
                            if ($calendarAcl->getBookEndDateTime() > $this->acl->getBookEndDateTime()) {
                                $this->acl->setBookEndDateTimeModifier($calendarAcl->getBookEndDateTimeModifier());
                            }
                            if ($calendarAcl->getCancelBeginDateTime() < $this->acl->getCancelBeginDateTime()) {
                                $this->acl->setCancelBeginDateTimeModifier(
                                    $calendarAcl->getCancelBeginDateTimeModifier()
                                );
                            }
                            if ($calendarAcl->getCancelEndDateTime() > $this->acl->getCancelEndDateTime()) {
                                $this->acl->setCancelEndDateTimeModifier($calendarAcl->getCancelEndDateTimeModifier());
                            }
                            if ($this->acl->getNumberReservationPerTimeslot() != 0 && ($calendarAcl->getNumberReservationPerTimeslot() == 0 || $calendarAcl->getNumberReservationPerTimeslot() > $this->acl->getNumberReservationPerTimeslot())) {
                                $this->acl->setNumberReservationPerTimeslot(
                                    $calendarAcl->getNumberReservationPerTimeslot()
                                );
                            }
                        }
                    } else {
                        $this->acl = $calendarAcl;
                    }
                } elseif (!$isLoggedIn && !$calendarAcl->getFrontendGroup()) {
                    $this->acl = $calendarAcl;
                }
            }
        }
        return $this->acl;
    }

    /**
     * Sets the calendarAcl
     *
     * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Ig\IgReservations\Domain\Model\CalendarAcl> $calendarAcl
     */
    public function setCalendarAcl(ObjectStorage $calendarAcl): void
    {
        $this->calendarAcl = $calendarAcl;
    }

    /**
     * Adds a BlockingTime
     */
    public function addBlockingTime(BlockingTime $blockingTime): void
    {
        $this->blockingTimes->attach($blockingTime);
    }

    /**
     * Removes a BlockingTime
     *
     * @param \Ig\IgReservations\Domain\Model\BlockingTime $blockingTimeToRemove The BlockingTime to be removed
     */
    public function removeBlockingTime(BlockingTime $blockingTimeToRemove): void
    {
        $this->blockingTimes->detach($blockingTimeToRemove);
    }

    /**
     * Returns the blockingTime
     *
     * @return \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Ig\IgReservations\Domain\Model\BlockingTime> $blockingTimes
     */
    public function getBlockingTimes(): ObjectStorage
    {
        return $this->blockingTimes;
    }

    /**
     * Sets the blockingTime
     *
     * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Ig\IgReservations\Domain\Model\BlockingTime> $blockingTimes
     */
    public function setBlockingTimes(ObjectStorage $blockingTimes): void
    {
        $this->blockingTimes = $blockingTimes;
    }

    /**
     * Returns the conf
     *
     * @return string
     */
    public function getConf()
    {
        return $this->conf;
    }
    /**
     * Returns the conf
     *
     * @return array
     */
    public function getConfParsed(): array
    {
        if ($this->confParsed === null) {
            $this->confParsed = yaml_parse($this->conf); // @todo use typo3 parse to replace EXT:
        }
        return $this->confParsed;
    }

    /**
     * Sets the conf
     */
    public function setConf(string $conf): void
    {
        $this->conf = $conf;
    }


    /**
     * Get the value of permissions
     */
    public function getMailsToSend(): string
    {
        return $this->mailsToSend;
    }

    /**
     * Set the value of permissions
     *
     * @param string $mailsToSend
     *
     * @return self
     */
    public function setMailsToSend($mailsToSend): void
    {
        $this->mailsToSend = $mailsToSend;
    }

    /**
     * Get the value of Mails To Send Array
     */
    public function getMailsToSendArray(): array
    {
        if ($this->mailsToSendArray === null) {
            $this->mailsToSendArray = GeneralUtility::trimExplode(',', $this->mailsToSend);
        }
        return $this->mailsToSendArray;
    }
    /**
     * Checks if mail needs to be sent
     *
     * @return boolean
     */
    public function hasMailToSend(string $mailToSend): bool
    {
        return in_array($mailToSend, $this->getMailsToSendArray());
    }


    /**
     * Get the value of label
     */
    public function getLabel(): string
    {
        return $this->label;
    }

    /**
     * Set the value of label
     *
     * @param string $label
     *
     * @return self
     */
    public function setLabel($label): void
    {
        $this->label = $label;
    }


    /**
     * Get the value of pageUid
     */
    public function getPageUid(): int
    {
        return $this->pageUid;
    }

    /**
     * Set the value of pageUid
     *
     * @param int $pageUid
     *
     * @return self
     */
    public function setPageUid($pageUid): void
    {
        $this->pageUid = $pageUid;
    }


    /**
     * Get the value of room
     */
    public function getRoom(): ?Room
    {
        return $this->room;
    }

    /**
     * Set the value of room
     *
     * @return self
     */
    public function setRoom(Room $room): void
    {
        $this->room = $room;
    }
    public function getRawFullCalendar(): array
    {
        $bookBeginDateTime = null;
        $bookEndDateTime = null;
        $showBeginDateTime = null;
        $showEndDateTime = null;
        $acl = $this->getAcl();
        if ($acl) {
            $showBeginDateTime = $acl->getShowBeginDateTime();
            $showEndDateTime = $acl->getShowEndDateTime();
            $bookBeginDateTime = $acl->getBookBeginDateTime();
            $bookEndDateTime = $acl->getBookEndDateTime();
        }
        return [
            'defaultView' => 'timeGrid' . ucfirst($this->defaultView),
            'slotDuration' => EventUtility::intToTime($this->getTimeslotBlockDuration() ?: 3600),
            'minTime' => EventUtility::intToTime($this->getTimeslotStartTime()),
            'maxTime' => EventUtility::intToTime($this->getTimeslotEndTime() ?: 86400),
            'selectConstraint' => [
                'start' => $bookBeginDateTime ? $bookBeginDateTime->format('Y-m-d H:i:s') : '',
                'end' => $bookEndDateTime ? $bookEndDateTime->format('Y-m-d H:i:s') : '',
            ],
            'validRange' => [
                'start' => $showBeginDateTime ? $showBeginDateTime->format('Y-m-d H:i:s') : '',
                'end' => $showEndDateTime ? $showEndDateTime->format('Y-m-d H:i:s') : '',
            ],
        ];
    }
    public function getDefaultViewFullCalendar(): string
    {
        return 'timeGrid' . ucfirst($this->defaultView);
    }
    public function getDefaultView(): string
    {
        return $this->defaultView;
    }
    public function getViewDays(): array
    {
        return $this->viewDays;
    }
    public function getViewDaysClass(): string
    {
        $rowsCount = $this->defaultView == 'day' ? 1 : count($this->viewDays);
        return 'calendar-' . $this->defaultView . ' calendar-rows-' . $rowsCount;
    }
    public function setView($defaultView, $viewDays, $date): void
    {
        $this->defaultView = $defaultView;
        $this->viewDays = $viewDays;
        $this->startDate = clone $date;
        if ($this->defaultView == 'week') {
            $this->startDate->modify('monday this week');
        } elseif ($this->defaultView == 'month') {
            $this->startDate->modify('first day of this month');
        } elseif ($this->defaultView == 'year') {
            $this->startDate->modify('first day of this year');
        }
        $this->endDate = clone $this->startDate;
        $this->endDate->modify('+1 ' . $this->defaultView);
    }
    public function getData(): array
    {
        if ($this->calendarRow !== null) {
            return $this->calendarRow;
        }
        // We don't use timeslots
        if ($this->getIsDateTimeMode()) {
            return [];
        }

        // was soll dargestellt werden (Von - bis, Blockdauer)
        $startTime = $this->getTimeslotStartTime();
        $endTime = $this->getTimeslotEndTime();
        $blockDuration = $this->getTimeslotBlockDuration();

        //$calendarAcl = $this->getAcl( );
        $this->calendarRow = [];
        $d = clone $this->startDate;
        if (!empty($this->viewDays)) {
            for ($max = 400;$max > 0 && $d < $this->endDate && !in_array(
                (int)$d->format('N'),
                $this->viewDays
            ); $max--,$d->modify(
                '+1 day'
            )) {
            }
        }
        if ($this->defaultView == 'month') {
            // Leer eintraege am Anfang
            $startWeekday = (int) $d->format('N');
            if ($startWeekday > 1) {
                $d->modify('-' . ($startWeekday - 1) . ' days');
                for ($weekday = 1;$weekday < $startWeekday;$weekday++) {
                    if (!empty($this->viewDays) && !in_array($weekday, $this->viewDays)) {
                        continue;
                    }
                    $this->calendarRow[] = [
                        'date' => clone $d,
                        'entries' => [],
                    ];
                    $d->modify('+1 day');
                }
            }
        }

        for ($max = 400;$max > 0 && $d < $this->endDate; $max--,$d->modify('+1 day')) {
            $date = clone $d;
            $weekday = (int) $date->format('N');
            if (!empty($this->viewDays) && !in_array($weekday, $this->viewDays)) {
                continue;
            }
            //echo('add ' . $weekday .'=='. strftime('%A', $d->format('U')).'<br />');
            $row = [
                'date' => clone $date,
                'entries' => [],
            ];
            $calendarTimeslots = $this->getTimeslots($weekday);

            if (!empty($startTime)) {
                if ($blockDuration === null || $blockDuration <= 0) {
                    die('startTime is set! Error: blockDuration must by greater than 0');
                }
                // Zeiten auffüllen
                $calendarTimeslots->rewind();
                $firstTimeslot = $calendarTimeslots->current();
                if ($firstTimeslot && $firstTimeslot->getStartTimeInt() > $startTime) {
                    //var_dump($firstTimeslot->getStartTimeInt(), $startTime);exit(0);
                    for ($time = $startTime;$time < $firstTimeslot->getStartTimeInt();$time += $blockDuration) {
                        $event = GeneralUtility::makeInstance(Event::class);
                        $event->setStartDate(clone $date);
                        $event->setCalendar($this);
                        $row['entries'][] = $event;
                    }
                }
            }

            $currentEvent = null;
            $eventRepository = GeneralUtility::makeInstance(EventRepository::class);
            //var_dump($firstTimeslot);                die('F='. $firstTimeslot->getStartTime() .'=='. $startTime );
            foreach ($calendarTimeslots as $timeslot) {
                $event = $eventRepository->findOneByDateAndTimeslotAndCalendar(
                    clone $date,
                    $timeslot->getUid(),
                    $this->getUid()
                );

                if ($currentEvent && $event && $currentEvent->getUid() && $event->getUid() && $currentEvent->getUid() == $event->getUid()) {
                    continue;
                }
                if (!$event) {
                    $event = GeneralUtility::makeInstance(Event::class);
                    //$event->addTimeslot($timeslot);
                }
                $event->setStartDate(clone $date);
                $event->setCalendar($this);
                $event->setTimeslot($timeslot);

                $currentEvent = clone $event;
                //$event->setFirstBookableDay($this->settings['firstBookableDay']);
                //$event->setLastBookableDay($this->settings['lastBookableDay']);

                /*
                  $reservations = $this->reservationRepository->findByEvent($event);
                  if($reservations) {
                  $event->setReservations($reservations);
                  }
                */
                /*
                  if( $event->getUid()==16) {
                  die('r=' . count( $event->getReservations() ));
                  }
                */

                $row['entries'][] = clone $event;
            }
            if (!empty($endTime)) {
                if ($blockDuration === null || $blockDuration <= 0) {
                    die('endTime is set! Error: blockDuration must by greater than 0');
                }
                // Zeiten auffüllen
                $lastTimeslot = $timeslot; //last timeslot of forach
                if ($lastTimeslot && $lastTimeslot->getEndTimeInt() < $endTime) {
                    for ($time = $lastTimeslot->getEndTimeInt();$time < $endTime;$time += $blockDuration) {
                        $event = GeneralUtility::makeInstance(Event::class);
                        $event->setStartDate(clone $date);
                        $event->setCalendar($this);
                        $row['entries'][] = $event;
                    }
                }
            }



            $this->calendarRow[] = $row;
        }



        if ($this->defaultView == 'month') {
            // Leer eintraege am Anfang
            for ($weekday = $d->format('N');$weekday > 1 && $weekday <= 7;$weekday++) {
                if (!empty($this->viewDays) && !in_array($weekday, $this->viewDays)) {
                    continue;
                }
                $this->calendarRow[] = [
                    'date' => clone $d,
                    'entries' => [],
                ];
                $d->modify('+1 day');
            }
        }
        //var_dump( $this->calendarRow);
        return $this->calendarRow;
    }


    /**
     * Get the value of timeslotBuffer
     */
    public function getTimeslotBuffer(): string
    {
        return $this->timeslotBuffer;
    }

    /**
     * Set the value of timeslotBuffer
     *
     * @param string $timeslotBuffer
     *
     * @return self
     */
    public function setTimeslotBuffer($timeslotBuffer): void
    {
        $this->timeslotBuffer = $timeslotBuffer;
    }

    public function getEvents(array $search = []): array
    {
        $eventRepository = GeneralUtility::makeInstance(EventRepository::class);

        if ($this->events == null) {
            $this->events = $eventRepository->getByCalendarAndSearch($this->getUid(), $search);
        }

        return $this->events;
    }

    /**
     * Returns the raw events with admin informations on each event
     */
    public function getMyEventsRawWithInfo(array $search = []): array
    {
        return $this->getEventsRaw(true, true, $search);
    }

    /**
     * Returns the raw events with admin informations on each event
     */
    public function getEventsRawWithInfo(array $search = []): array
    {
        return $this->getEventsRaw(true, false, $search);
    }

    /**
     * Returns the raw events without admin information
     */
    public function getEventsRawPublic(array $search = []): array
    {
        return $this->getEventsRaw(false, false, $search);
    }

    /**
     * Returns an array of events that can be used to fill the fullcalendar
     */
    public function getEventsRaw(bool $protected, bool $onlyForLoggedInUser = false, array $search = []): array
    {
        $acl = $this->getAcl();

        $useBuffers = $acl ? $acl->hasPermission('show-buffer-time') : false;

        $events = $this->getEvents($search);
        $eventsClean = [];
        $currentEvent = null;

        $context = GeneralUtility::makeInstance(Context::class);
        $userId = $context->getPropertyFromAspect('frontend.user', 'id');
        // Eintraege mergen
        if ($acl && $acl->hasPermission('merge-events')) {
            foreach ($events as $event) {
                $owner = $event->getOwner();

                if (!$onlyForLoggedInUser || ($userId && $owner && $owner->getUid() == $userId)) {
                    if (!$currentEvent || ($currentEvent && !$currentEvent->merge($event, $useBuffers))) {
                        if ($currentEvent) {
                            $eventsClean[] = $currentEvent->getRaw($protected, $useBuffers, $onlyForLoggedInUser);
                        }
                        $currentEvent = $event;
                    }
                }
            }
            if ($currentEvent) {
                $owner = $currentEvent->getOwner();

                if (!$onlyForLoggedInUser || ($userId && $owner && $owner->getUid() == $userId)) {
                    $eventsClean[] = $currentEvent->getRaw($protected, $useBuffers, $onlyForLoggedInUser);
                }
            }
            return $eventsClean;
        }
        // ?? braucht es getRaw nur fuer buffer?, sonst return $events; + buffer in model
        foreach ($events as $event) {
            $owner = $event->getOwner();
            if (!$onlyForLoggedInUser || ($userId && $owner && $owner->getUid() == $userId)) {
                $eventsClean[] = $event->getRaw($protected, $useBuffers);
            }
        }
        return $eventsClean;
    }

    /**
     * Get the value of isPublic
     *
     * @return boolean
     */
    public function getIsPublic(): bool
    {
        return $this->isPublic;
    }

    /**
     * Set the value of isPublic
     *
     * @param boolean $isPublic
     *
     * @return self
     */
    public function setIsPublic($isPublic): void
    {
        $this->isPublic = $isPublic;
    }


    public function getEventsForWeek(): array
    {
        $date = clone $this->startDate;
        $eventRepository = GeneralUtility::makeInstance(EventRepository::class);

        $events = [];
        for ($d = (int)$date->format('N'); $d >= 1 && $d <= 7; $d++, $date->modify('+1 day')) {
            $events[] = [
                'date' => clone $date,
                'events' => $eventRepository->findByDateAndCalendar(clone $date, $this),
            ];
        }

        return $events;
    }

    /**
     * Get the value of hideInList
     *
     * @return boolean
     */
    public function getHideInList(): bool
    {
        return $this->hideInList;
    }

    /**
     * Set the value of hideInList
     *
     * @param boolean $hideInList
     *
     * @return self
     */
    public function setHideInList($hideInList): void
    {
        $this->hideInList = $hideInList;
    }


    /**
     * Get the value of frontendUser
     */
    public function getFrontendUser(): ?FeUser
    {
        return $this->frontendUser;
    }

    /**
     * Set the value of frontendUser
     *
     * @return self
     */
    public function setFrontendUser(\Ig\IgReservations\Domain\Model\FeUser $frontendUser): void
    {
        $this->frontendUser = $frontendUser;
    }

    public function getPossibleStartAndEndTimes(
        string $startDate,
        string $startTime,
        string $endDate,
        string $endTime
    ): array {
        $eventRepository = GeneralUtility::makeInstance(EventRepository::class);
        $calendar = $this;
        $acl = $this->getAcl();
        $possibleEndTimes = [];
        $startDateTime = new \DateTime($startDate . 'T' . $startTime);
        $minTime = $eventRepository->findMinTimeForDateTime($calendar, $startDate, $startTime);

        $lastEndTime = $minTime['min_time'];

        if ($lastEndTime) {
            $lastDateTime = new \DateTime($endDate . 'T' . $lastEndTime);

            if ($acl && $acl->hasPermission('show-buffer-time') && $calendar->getTimeslotBuffer()) {
                $lastDateTime->modify('-' . $calendar->getTimeslotBuffer());
            }
        }

        if (!$lastEndTime) {
            $lastEndTime = EventUtility::intToTime($calendar->getTimeslotEndTime());
            $lastDateTime = new \DateTime($endDate . 'T' . $lastEndTime);
        }

        $minTime = $startDateTime->getTimestamp();
        $maxTime = $lastDateTime->getTimestamp();

        $intervalTimestamp = 3600;

        if ($calendar->getTimeslotBlockDuration()) {
            $intervalTimestamp = $calendar->getTimeslotBlockDuration();
        }

        $i = 0;
        for ($time = $minTime; $time <= $maxTime; $time += $intervalTimestamp) {
            if ($i > 0) {
                $possibleEndTimes[strftime('%H:%M:%S', $time)] = strftime('%H:%M', $time);
            }

            $i++;
        }
        return $possibleEndTimes;
    }

    public function getTimelotDateTimes(string $startDate, string $endDate)
    {
        $startTimestamp = $this->getTimeslotStartTime();
        $endTimestamp = $this->getTimeslotEndTime();
        $startTime = EventUtility::intToTime($startTimestamp);
        $endTime = EventUtility::intToTime($endTimestamp);
        $startDateTime = new \DateTime($startDate . 'T' . $startTime);
        $endDateTime = new \DateTime($endDate . 'T' . $endTime);
        $intervalTimestamp = $this->getTimeslotBlockDuration();
        $possibleTimes = [];

        $minTime = $startDateTime->getTimestamp();
        $maxTime = $endDateTime->getTimestamp();

        for ($time = $minTime; $time <= $maxTime; $time += $intervalTimestamp) {
            $possibleTimes[strftime('%H:%M:%S', $time)] = strftime('%H:%M', $time);
        }

        return $possibleTimes;
    }

    /**
     * Initializes all ObjectStorage properties
     * Do not modify this method!
     * It will be rewritten on each save in the extension builder
     * You may modify the constructor of this class instead
     */
    protected function initStorageObjects()
    {
        $this->calendarTimeslots = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
        $this->calendarAcl = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
        $this->blockingTimes = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
    }
}
