<?php

namespace onespace\tools\widgets\calendar;

use yii\base\Widget;

/**
 * This widget is used to generate FullCalendar calendar on the fly in PHP.
 * 
 * Note: This value is exclusive. For example, if you have an all-day event that has an end of 2018-09-03, 
 * then it will span through 2018-09-02 and end before the start of 2018-09-03. This is intended behaviour by 
 * FullCalendar.
 * 
 * @see https://fullcalendar.io/docs/event-parsing#end
 * 
 * @link https://fullcalendar.io/
 * @link https://fullcalendar.io/docs
 * 
 * @author  Gareth Palmer <gareth@one-space.co.za>
 */

class FullCalendar extends Widget {

    /**
     * HTML properties to apply to the div into which the calendar is generated.
     * 
     * @var array   $options
     * 
     * @access  public
     */

    public array $options;

    /**
     * Static list of events to apply to the calendar.
     * 
     * @var array   $events Default: []
     * 
     * @link https://fullcalendar.io/docs/event-parsing
     * 
     * @access  public
     */

    public array $events = [];

    /**
     * Rather than a static list, pull the data from an api, i.e a JSON feed.
     * 
     * @var array   $eventSources   Default: []
     * 
     * @link https://fullcalendar.io/docs/events-json-feed
     * 
     * @access  public
     */

    public array $eventSources = [];

    /**
     * The initial view of the calendar to load.
     * 
     * @var string  $initialView    Default: 'dayGridMonth'
     * 
     * @link    https://fullcalendar.io/docs#views
     * 
     * @access  public
     */

    public string $initialView = 'dayGridMonth';

    /**
     * Whether or not to use navigation links on the date numbers on the calendar.
     * 
     * @var bool    $navLinks   Default: true
     * 
     * @access  public
     */

    public bool $navLinks = true;

    /**
     * How the calendar header is layed out.
     * 
     * @var array   $header_layout
     * 
     * @access  public
     */

    public array $header_layout = [
        'start' => 'prev,next today',
        'center' => 'title',
        'end' => 'dayGridMonth timeGridWeek timeGridOneDay listWeek'
    ];

    /**
     * Rather than use all the params indiviually (see above),
     * set them all as a single string.
     * 
     * @var string|array    $params_overwrite
     * 
     * @access  public
     */

    public string|array $params_overwrite;

    /**
     * A modal widget, should be generated from onespace\tools\widgets\ui\Modal or similar modal generating widget.
     * 
     * @var string  $modal
     * 
     * @access  public
     */

    public string $modal;

    /**
     * The custom onEventClick function.
     * 
     * Should be something like this:
     * 
     * ```php
     * $this->custom_click_event = <<<JS
     * function(event) {
     *   $('#your_id').modal('show');
     * }
     * JS;
     * ```
     * 
     * @var string  $custom_click_event
     * 
     * @access  public
     */

    public string $custom_click_event;

    /**
     * OPTIONAL: set a custom name for whichever service this is required for.
     * This allows for custom "no events" message.
     * 
     * @var string  $eventName  Default: 'event'
     * 
     * @access  public
     */

    public string $eventName = 'event';


    /**
     * {@inheritdoc}
     */

    public function init(): void {
        parent::init();

        $this->modal ??= '';

        $view = $this->getView();
        $view->registerJsFile('https://cdn.jsdelivr.net/npm/fullcalendar@latest/index.global.min.js');

        $this->options['id'] ??= 'calendar';
        $cal_class = 'ui-calendar-background ui-calendar-br';
        if (isset($this->options['class'])) {
            $this->options['class'] = "{$cal_class} " . $this->options['class'];
        }
        $this->options['class'] ??= $cal_class;

        $params = isset($this->params_overwrite) ? $this->set_calendar_overwrite() : $this->set_calander_params();

        $view->registerJs(<<<JS
        const calendar = new FullCalendar.Calendar(document.getElementById("{$this->options['id']}"), {
            $params,
        });
        calendar.render();
        JS);
    }


    /**
     * Set the params from the overwrite property.
     * 
     * @return  string
     * 
     * @access  private
     */

    private function set_calendar_overwrite(): string {
        if (is_string($this->params_overwrite)) {
            return $this->params_overwrite;
        }
        return json_encode($this->params_overwrite);
    }


    /**
     * Set the params from the general widget properties.
     * 
     * @return  string
     * 
     * @access  private
     */

    private function set_calander_params(): string {
        $events = '';
        if (count($this->events) > 0) {
            foreach ($this->events as &$event) {
                $event = json_encode($event);
            }
            $events = "events: [" . implode(',', $this->events) . "],";
        }

        $eventSources = "";
        if (count($this->eventSources) > 0) {
            foreach ($this->eventSources as &$option) {
                $option = json_encode($option);
            }
            $eventSources = "eventSources: [" . implode(',', $this->eventSources) . "],";
        }

        $eventClick = '';
        if (isset($this->custom_click_event)) {
            $view = $this->getView();
            $view->registerCss(<<<CSS
            .fc-event {
                cursor: pointer;
            }
            CSS);
            $eventClick = "eventClick: {$this->custom_click_event},";
        }


        $header_layout = json_encode($this->header_layout);

        $params = <<<JS
        initialView: '{$this->initialView}',
        headerToolbar: {$header_layout},
        navLinks: {$this->navLinks},
        {$events}
        {$eventSources}
        eventTimeFormat: {
            // https://fullcalendar.io/docs/eventTimeFormat
            hour: '2-digit',
            minute: '2-digit',
            meridiem: false,
            hour12: false
        },
        {$eventClick}
        buttonText: {
            today: 'Today',
        },
        views: {
            timeGridOneDay: {
                type: 'timeGrid',
                duration: { days: 1 },
                buttonText: 'Day',
            },
            dayGridMonth: {
                buttonText: 'Month',
            },
            timeGridWeek: {
                buttonText: 'Week',
            },
            listDay: {
                buttonText: 'List',
                noEventsContent: (arg) => {
                    return "No {$this->eventName}s to display this day.";
                },
            },
            listWeek: {
                buttonText: 'List',
                noEventsContent: (arg) => {
                    return "No {$this->eventName}s to display this week";
                },
            },
            listMonth: {
                buttonText: 'List',
                noEventsContent: (arg) => {
                    return "No {$this->eventName}s to display this month";
                },
            },
            listYear: {
                buttonText: 'List',
                noEventsContent: (arg) => {
                    return "No {$this->eventName}s to display this year";
                },
            },
        }
        JS;

        return $params;
    }


    /**
     * {@inheritdoc}
     * 
     * @return  string
     */

    public function run(): string {
        $cal = "<div";
        foreach ($this->options as $key => $value) {
            $cal .= " {$key}='{$value}'";
        }
        $cal .=  "></div>";
        $cal .= $this->modal;
        return $cal;
    }
}
