<?php

namespace Ig\IgReservations\Utility;

use Ig\IgReservations\Domain\Repository\CalendarRepository;
use Ig\IgReservations\Domain\Repository\EventRepository;
use DateTime;
use Ig\IgReservations\Domain\Model\Event;
use Ig\IgReservations\Service\UserService;
use TYPO3\CMS\Core\Utility\GeneralUtility;

class CalendarViewUtility
{
    /**
     * calendarRepository
     *
     * @var CalendarRepository
     */
    protected $calendarRepository = null;
    /**
     * eventRepository
     *
     * @var EventRepository
     */
    protected $eventRepository = null;
    
    /**
     * @var \Ig\IgReservations\Domain\Service\UserService
     */
    protected UserService $userService;

    protected $unit = null;
    protected $viewDays = [];
    protected $date = null;
    protected $format = [
        'day' => '%A %d.%m.%Y',
        'week' => 'KW %V %d.%m.%Y',
        'month' => '%B %Y',
        'year' => '%Y',
    ];


    
    public function injectCalendarRepository(
        CalendarRepository $calendarRepository
    ): void {
        $this->calendarRepository = $calendarRepository;
    }

    
    public function injectEventRepository(EventRepository $eventRepository): void
    {
        $this->eventRepository = $eventRepository;
    }

    public function injectUserService(UserService $userService): void
    {
        $this->userService = $userService;
    }

    public function getStartDate(DateTime $date): void
    {
        if ($this->unit == 'week') {
            $date->modify('monday this week');
        } elseif ($this->unit == 'month') {
            $date->modify('first day of this month');
        } elseif ($this->unit == 'year') {
            $date->modify('first day of this year');
        }
    }
    /**
     * init
     *
     * @param DateTime $date selected Date
     * @param string $unit one of 'day', 'week', 'month', 'year'
     * @param null|array $viewDays Weekdays to view or null to show all
     */
    public function init(
        DateTime $date,
        ?string $limitStart = null,
        ?string $limitEnd = null,
        ?string $unit = 'day',
        ?array $viewDays = null
    ): void {
        $this->unit = $unit;
        $this->viewDays = $viewDays;
        $this->setLimits($limitStart, $limitEnd);
        $this->setDate($date);
    }
    /**
     * Returns the calendar data
     * @return array
     */
    public function getCalendarData($calendars)
    {
        //$context = GeneralUtility::makeInstance(Context::class);
        //$feUser = $this->userService->getUser();
        //$dayDaterange = $this->getDayDaterange();
        //$feUserGroups = $context->getPropertyFromAspect('frontend.user','groupIds');
        // Build the array of events
        $calendarData = [];
        foreach ($calendars as $calendar) {
            // was soll dargestellt werden (Von - bis, Blockdauer)
            $startTime = $calendar->getTimeslotStartTime();
            $endTime = $calendar->getTimeslotEndTime();
            $blockDuration = $calendar->getTimeslotBlockDuration();
            //$calendarAcl = $calendar->getAcl( );
            $calendarRow = [
                'calendar' => $calendar,
                //'calendarAcl' => $calendarAcl,
                'data' => [],
            ];

            $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->unit == '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;
                        }
                        $calendarRow['data'][] = [
                            '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 = $calendar->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) {
                        for ($time = $startTime;$time < $firstTimeslot->getStartTimeInt();$time += $blockDuration) {
                            $event = GeneralUtility::makeInstance(Event::class);
                            $event->setStartDate($date);
                            $event->setCalendar($calendar);
                            $row['entries'][] = $event;
                        }
                    }
                }

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

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

                    $currentEvent = $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'][] = $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 foreach

                    if ($lastTimeslot && $lastTimeslot->getEndTimeInt() < $endTime) {
                        for ($time = $lastTimeslot->getEndTimeInt();$time < $endTime;$time += $blockDuration) {
                            $event = GeneralUtility::makeInstance(Event::class);
                            $event->setStartDate($date);
                            $event->setCalendar($calendar);
                            $row['entries'][] = $event;
                        }
                    }
                }



                $calendarRow['data'][] = $row;
            }

            if ($this->unit == '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;
                    }
                    $calendarRow['data'][] = [
                        'date' => clone $d,
                        'entries' => [],
                    ];
                    $d->modify('+1 day');
                }
            }
            $calendarData[] = $calendarRow;
        }
        return $calendarData;
    }

    /**
     * Returns the calendar nav
     * @return array
     */
    public function getCalendarNav($calendars)
    {
        $showBeginDate = null;
        $showEndDate = null;
        $viewDays = [];
        foreach ($calendars as $calendar) {
            $acl = $calendar->getAcl();
            if ($acl) {
                if ($showBeginDate === null || $showBeginDate > $acl->getShowBeginDateTime()) {
                    $showBeginDate = $acl->getShowBeginDateTime();
                }
                if ($showEndDate === null || $showEndDate < $acl->getShowBeginDateTime()) {
                    $showEndDate = $acl->getShowEndDateTime();
                }
            }
            $viewDays = array_unique(array_merge($viewDays, $calendar->getViewDays()));
        }
        $this->limitStartDate = $showBeginDate;
        $this->limitEndDate = $showEndDate;
        // @todo calendarNavi muss Days respektieren bei Tagesansicht
        // @todo calendarNavi startDate und enDate von Calendar?? Was bei mehreren Kalendern
        $prevDate = clone $this->startDate;
        //$prevDate->modify('-1 '.$this->unit);
        $prevDate->modify('-1 day');
        $prevShow = $prevDate >= $this->limitStartDate;
        /*
        echo('limit='.$this->limitStartDate->format('d.m.Y H:i').'<br />');
        echo('P='.$prevDate->format('d.m.Y H:i').'<br />');
        echo('res='. ($prevShow ? 'ja' : 'n'));
        exit(0);
        */
        $nextDate = clone $this->startDate;
        $nextDate->modify('+1 ' . $this->unit);
        $nextShow = $nextDate < $this->limitEndDate;

        /*
        var_dump($prevShow, $nextShow, $this->unit);
        echo('limit');
        var_dump($this->limitStartDate,$this->limitEndDate);
        echo('start/end');
        var_dump($this->startDate,$this->endDate);exit(0);
        */

        $options = [];
        if (count($viewDays) < 7) {
            $disable = [];
            for ($d = 1;$d <= 7;$d++) {
                if (!in_array($d, $viewDays)) {
                    $disable[] = $d;
                }
            }
            $options['disable'] = $disable;
        }

        /*        $max = 100;
        $d = clone $this->limitStartDate;
        while( $max>0 && $d<$this->limitEndDate) {
            $max--;
            $options[$d->format('Y-m-d')] = strftime($this->format[$this->unit], $d->format('U'));
            $d->modify('+1 '.$this->unit);
        }*/
        /*
        for($max = 100,$d = clone $this->limitStartDate;$max>0 && $d<$this->limitEndDate; $max--,$d->modify('+1 '. $this->unit)) {
            $options[$d->format('Y-m-d')] = strftime($this->format[$this->unit], $d->format('U'));
        }
        */
        $now = new DateTime();
        return [
            'prev' => [
                'show' => $prevShow,
                'date' => $prevDate,
            ],
            'next' => [
                'show' => $nextShow,
                'date' => $nextDate,
            ],
            'options' => $options,
            'optionsJson' => json_encode($options, JSON_THROW_ON_ERROR),
            'selectedValue' => $this->startDate->format('Y-m-d'),
            'showBeginDate' => $showBeginDate,
            'showEndDate' => $showEndDate,
            'showToday' => $showBeginDate <= $now && $showEndDate >= $now,
        ];
    }

    /**
     * setDate
     *
     * @param DateTime $date selected Date
     */
    protected function setDate(DateTime $date)
    {
        $this->date = $date;
        $this->startDate = clone $this->date;
        $this->getStartDate($this->startDate);
        $this->endDate = clone $this->startDate;
        $this->endDate->modify('+1 ' . $this->unit);
    }


    /**
     * setLimits
     */
    protected function setLimits(string $limitStart = null, string $limitEnd = null)
    {
        $baseDate = new DateTime();
        $baseDate->setTime(12, 0, 0);
        $this->getStartDate($baseDate);
        $this->limitStartDate = clone $baseDate;
        $this->limitEndDate = clone $baseDate;
        if ($limitStart) {
            $this->limitStartDate->modify($limitStart);
        }
        if ($limitEnd) {
            $this->limitEndDate->modify($limitEnd);
        }
    }
}
