<!--
Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
Copyright (C) 2024 HTWKalender support@htwkalender.de

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
-->

<template>
  <DynamicPage
    :hide-content="availableRooms.length === 0"
    :headline="$t('freeRooms.freeRooms')"
    :sub-title="$t('freeRooms.detail')"
    icon="pi pi-search"
    :button="{
      label: $t('freeRooms.search'),
      icon: 'pi pi-search',
      disabled: isLater,
      onClick: loadFreeRooms,
    }"
  >
    <template #selection="{ flexSpecs }">
      <Calendar
        v-model="date"
        :class="flexSpecs"
        :placeholder="$t('freeRooms.pleaseSelectDate')"
        :empty-message="$t('roomFinderPage.noRoomsAvailable')"
        date-format="dd.mm.yy"
        panel-class="min-w-min"
        touch-u-i
      />
      <div class="break" />
      <Calendar
        v-if="mobilePage"
        v-model="start"
        type="time"
        placeholder="start"
        time-only
        hour-format="24"
        date-format="HH:mm"
        :class="[{ 'p-invalid': isLater }, flexSpecs]"
        panel-class="min-w-min"
        touch-u-i
        @update:model-value="
          () => {
            timeRange[0] = start.getHours() * 60 + start.getMinutes();
          }
        "
      />
      <Calendar
        v-if="mobilePage"
        v-model="end"
        type="time"
        time-only
        hour-format="24"
        placeholder="end"
        date-format="HH:mm"
        :class="[{ 'p-invalid': isLater }, flexSpecs]"
        panel-class="min-w-min"
        touch-u-i
        @update:model-value="
          () => {
            timeRange[1] = end.getHours() * 60 + end.getMinutes();
          }
        "
      />
      <div
        v-if="!mobilePage"
        class="flex-grow-1 relative mb-2"
        :class="flexSpecs"
      >
        <Tag
          :value="formatTime(start)"
          class="opacity-0 pointer-events-none text-xl lg:text-base"
        />
        <Tag
          :value="formatTime(start)"
          class="absolute time-tag text-xl lg:text-base"
          :style="{ left: timeToPercentString(timeRange[0]) }"
          :class="startMoved.value ? 'moved' : ''"
        />
        <Tag
          :value="formatTime(end)"
          class="absolute time-tag text-xl lg:text-base"
          :style="{ left: timeToPercentString(timeRange[1]) }"
          :class="endMoved.value ? 'moved' : ''"
        />
      </div>
      <div class="break" />
      <Slider
        v-if="!mobilePage"
        v-model="timeRange"
        :class="flexSpecs"
        range
        :min="0"
        :max="24 * 60"
        :step="5"
        @change="updateTimeRange"
      />
    </template>
    <template #content>
      <DataTable
        v-model:filters="filters"
        :value="availableRooms"
        data-key="id"
        filter-display="row"
        paginator
        :rows="10"
        :global-filter-fields="['room']"
      >
        <Column field="room" sortable :header="$t('freeRooms.room')">
          <template #filter="{ filterModel, filterCallback }">
            <InputText
              v-model="filterModel.value"
              type="text"
              class="p-column-filter"
              :placeholder="$t('freeRooms.searchByRoom')"
              @input="filterCallback()"
            />
          </template>
          <template #body="slotProps">
            <div class="flex flex-column sm:flex-row justify-content-between flex-1 column-gap-4 mx-2 md:mx-4">
              <p class="flex-1 align-self-stretch sm:align-self-center my-2">{{ slotProps.data.room }}</p>
              <Button
                :label="$t('freeRooms.viewOccupancy')"
                icon="pi pi-hourglass"
                class="p-button-rounded p-button-outlined sm:align-self-center align-self-end"
                @click="occupationRoute(slotProps.data.room)"
              />
            </div>
          </template>
        </Column>
      </DataTable>
    </template>
  </DynamicPage>
</template>

<script setup lang="ts">
import { computed, inject, Ref, ref } from "vue";
import DynamicPage from "@/view/DynamicPage.vue";
import { requestFreeRooms } from "@/api/requestFreeRooms.ts";
import { FilterMatchMode } from "primevue/api";
import { padStart } from "@fullcalendar/core/internal";
import router from "@/router";
import { formatYearMonthDay } from "@/helpers/dates";

const mobilePage = inject("mobilePage") as Ref<boolean>;
const filters = ref({
  room: { value: null, matchMode: FilterMatchMode.CONTAINS, label: "Room" },
});

function occupationRoute(room: string): void {
  // date is in format like YYYYMMDD
  router.push({
    name: "room-schedule",
    query: {
      room: room,
      date: formatYearMonthDay(date.value),
    },
  });
}

const date: Ref<Date> = ref(new Date(Date.now()));
const start: Ref<Date> = ref(new Date(0));
const end: Ref<Date> = ref(new Date(0));

class Moved {
  value: boolean = false;
  timeout: NodeJS.Timeout | null = null;
}

const startMoved: Ref<Moved> = ref(new Moved());
const endMoved: Ref<Moved> = ref(new Moved());

start.value.setHours(new Date().getHours());
end.value.setHours(Math.min(new Date().getHours() + 3, 23));
if (end.value.getHours() === 23) {
  start.value.setHours(22);
  end.value.setMinutes(55);
}

const timeRange: Ref<number[]> = ref([
  start.value.getHours() * 60 + start.value.getMinutes(),
  end.value.getHours() * 60 + end.value.getMinutes(),
]);

function buttonMovedTimeout(
  time: number,
  timeDate: Date,
  moved: Ref<Moved>,
): void {
  if (time !== timeDate.getHours() * 60 + timeDate.getMinutes()) {
    if (moved.value.timeout !== null) {
      clearTimeout(moved.value.timeout);
    }
    moved.value.value = true;
    moved.value.timeout = setTimeout(() => {
      moved.value.value = false;
    }, 1000);
  }
}

function updateTimeRange(): void {
  buttonMovedTimeout(timeRange.value[0], start.value, startMoved);
  buttonMovedTimeout(timeRange.value[1], end.value, endMoved);

  if (timeRange.value[0] > timeRange.value[1]) {
    timeRange.value[1] = timeRange.value[0];
  }
  start.value.setHours(Math.floor(timeRange.value[0] / 60));
  start.value.setMinutes(timeRange.value[0] % 60);
  end.value.setHours(Math.floor(timeRange.value[1] / 60));
  end.value.setMinutes(timeRange.value[1] % 60);
}

function formatTime(time: Date): string {
  return (
    padStart(time.getHours().toString(), 2) +
    ":" +
    padStart(time.getMinutes().toString(), 2)
  );
}

function timeToPercentString(time: number): string {
  return `${(Number(time) * 100) / (24 * 60)}%`;
}

async function loadFreeRooms(): Promise<void> {
  availableRooms.value = [];
  const startDate = new Date(date.value.getTime());
  startDate.setHours(start.value.getHours());
  startDate.setMinutes(start.value.getMinutes());
  const endDate = new Date(date.value.getTime());
  endDate.setHours(end.value.getHours());
  endDate.setMinutes(end.value.getMinutes());
  await requestFreeRooms(startDate.toISOString(), endDate.toISOString()).then(
    (data) => {
      availableRooms.value = data.map((room, index) => {
        return { id: index, room: room };
      });
    },
  );
}

const isLater = computed(() => {
  return start.value > end.value;
});

const availableRooms: Ref<{ id: number; room: string }[]> = ref([]);
</script>

<style scoped>
.break {
  flex-basis: 100%;
  height: 0;
}

.time-tag {
  transform: translateX(-50%);
  border: 2px solid var(--primary-color);
  background-color: var(--primary-color);
  opacity: 0.6;
  transition: opacity 0.2s ease-in-out;
}

.time-tag.moved {
  opacity: 1;
  z-index: 2;
}

.time-tag::before {
  content: "";
  position: absolute;
  bottom: -0.8rem;
  left: 50%;
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 0.8rem 0.5rem 0 0.5rem;
  border-color: var(--primary-color) transparent transparent transparent;
  transform: translateX(-50%);
}
</style>
