<script>
  import { onMount } from 'svelte';
  import { writable } from 'svelte/store';
  import FullCalendar from 'svelte-fullcalendar';
  import timeGridPlugin from '@fullcalendar/timegrid';
  import adaptivePlugin from '@fullcalendar/adaptive';
  import rrulePlugin from '@fullcalendar/rrule';
  import dayGridPlugin from '@fullcalendar/daygrid';
  import listPlugin from '@fullcalendar/list';
  import resourceTimelinePlugin from '@fullcalendar/resource-timeline';
  import frLocale from '@fullcalendar/core/locales/fr';
  import EventModal from './EventModal.svelte';
  import FilterBar from './FilterBar.svelte';
  import { mkSource, calendarTree, initialCalendars, getSubCalendars } from './calendar';
  import { debounce } from 'lodash';
  import Help from './Help.svelte';

  import 'bootstrap/dist/css/bootstrap.css';
  import 'bootstrap-icons/font/bootstrap-icons.css';
  import bootstrap5Plugin from '@fullcalendar/bootstrap5';
  import { Tooltip } from 'bootstrap';

  import { createPopper } from '@popperjs/core';

  import ENSLocations from './static-ens-locations.json';

  const event = writable(null);

  let openModal = false;
  const toggle = () => (openModal = !openModal);
  const mobile = window.innerWidth < 765;
  const now = new Date();
  const scrollTo = (() => {
    const time = new Date();
    time.setHours(Math.max(0, time.getHours() - 2));
    return time.toLocaleTimeString();
  })();

  const allowedViews = [
    'resourceTimelineDay',
    'dayGridMonth',
    'timeGridWeek',
    'timeGridDay',
    'listWeek'
  ];

  const search = new URL(document.location).searchParams;

  const params = search.getAll('c');
  const date = new Date(search.get('d'));
  const view = search.get('v');

  const headers = mobile
    ? {
        left: 'title',
        center: 'prev,today,next',
        right: 'resourceTimelineDay dayGridMonth,timeGridWeek,timeGridDay,listWeek'
      }
    : {
        left: 'prev,next today',
        center: 'title',
        right: 'resourceTimelineDay dayGridMonth,timeGridWeek,timeGridDay,listWeek'
      };

  let calendar;

  let options = writable({
    initialView: allowedViews.includes(view)
      ? view
      : mobile
      ? 'listWeek'
      : 'timeGridWeek',
    initialDate: date.toString() === 'Invalid Date' ? now : date,
    plugins: [
      timeGridPlugin,
      dayGridPlugin,
      rrulePlugin,
      listPlugin,
      resourceTimelinePlugin,
      adaptivePlugin,
      bootstrap5Plugin
    ],
    locale: frLocale,
    allDayContent: '',
    headerToolbar: headers,
    buttonText: { resourceTimelineDay: 'Salles' },
    scrollTime: '08:00:00',
    resourceGroupField: 'building',
    resourceAreaWidth: '27%',
    resources: Object.entries(ENSLocations).flatMap(([building, rooms]) =>
      rooms.map(room => ({
        id: `${building}-${room}`,
        building,
        title: room
      }))
    ),
    height: '100%',
    schedulerLicenseKey: 'CC-Attribution-NonCommercial-NoDerivatives',
    nowIndicator: true,
    now: now,
    scrollTime: scrollTo,
    scrollTimeReset: false,
    eventClick: info => {
      openModal = true;
      event.set(info.event);
    },
    titleFormat: {
      year: mobile ? '2-digit' : 'numeric',
      month: mobile ? 'numeric' : 'long',
      day: 'numeric'
    },
    eventSources: [],
    themeSystem: 'bootstrap5',
    nextDayThreshold: '05:00:00',
    progressiveEventRendering: true,
    expandRows: true,
    eventDidMount: info => {
      new Tooltip(info.el, {
        title: info.event.extendedProps.short_name,
        placement: 'top'
      });
    }
  });

  const flatten = d => {
    let array = [];

    if (!d) {
      return [];
    }

    if (Array.isArray(d)) {
      d.forEach(a => (array = array.concat(flatten(a))));
    } else {
      for (const [n, s] of Object.entries(d)) {
        array = array.concat(n, flatten(s));
      }
    }
    return array;
  };

  let selectedCalendars = [];
  const initial =
    params.length > 0
      ? flatten(params.map(cal => getSubCalendars(cal))).concat(params)
      : initialCalendars;

  const updateEvents = debounce(calendars => {
    options.update(opts => ({
      ...opts,
      eventSources: selectedCalendars.map(mkSource).filter(x => !!x)
    }));
  }, 300);

  $: updateEvents(selectedCalendars);
</script>

<div class="h-100 d-flex flex-column">
  <h1 class="mt-3 title text-center">Calendrier de la vie étudiante à l'ENS</h1>

  <Help />

  <FilterBar {calendarTree} bind:selected={selectedCalendars} {initial} />

  <FullCalendar bind:this={calendar} options={$options} />

  <EventModal event={$event} open={openModal} {toggle} />
</div>

<style>
  :global(.fs-7) {
    font-size: 0.9rem !important;
  }

  :global(.st-tentative) {
    opacity: 40%;
  }
  :global(.st-cancelled) {
    background: repeating-linear-gradient(45deg, #333, #333 10px, #950 10px, #950 20px);
  }

  :global(.modal-open) {
    padding-right: 8px !important;
  }

  :global(.fc-event) {
    cursor: pointer;
  }

  :global(.modal-body p:last-child, ul:last-child) {
    margin-bottom: 0 !important;
  }

  :global(.fc-toolbar-chunk:not(:last-child)) {
    margin-bottom: 0.25em;
  }

  @media (max-width: 765px) {
    :global(.fc-header-toolbar) {
      flex-direction: column;
    }
    :global(.fc-toolbar-title) {
      margin-bottom: 0.25em !important;
    }
    :global(.fc-toolbar-chunk) {
      display: flex;
      justify-content: space-evenly;
      width: 100%;
    }
  }
</style>
