<template>
  <w-field class="w-date-range">
    <div class="row text-input items-center">
      {{ dateRangeString }}
    </div>
    <template #append>
      <q-icon v-if="dateRange && dateRange.start || dateRange && dateRange.end" class="cursor-pointer" name="fal fa-times-circle" @click="clearDateValue()" />
      <q-icon class="cursor-pointer" name="fal fa-calendar-alt">
        <q-popup-proxy ref="qDateProxy" transition-show="scale" transition-hide="scale">
          <q-card class="w-date-range-popup">
            <q-card-section class="non-selectable">
              <div class="row justify-between items-center">
                <div class="text-subtitle1 text-bold">
                  {{ dateRangeString }}
                </div>
                <w-btn v-close-popup flat round dense icon="fal fa-times" />
              </div>
              <div class="row q-gutter-md">
                <div class="column col calendar-month q-pt-md">
                  <div class="row items-center justify-between">
                    <w-btn flat round dense icon="fal fa-arrow-left" @click="goPrevMonth()" />
                    <div class="text-body2 dynamic">
                      {{ monthsLocale[activeMonthStart] +' '+ activeYearStart }}
                    </div>
                    <w-btn class="lt-md" flat round dense icon="fal fa-arrow-right" @click="goNextMonth()" />
                    <div class="gt-sm" />
                  </div>
                  <div class="row">
                    <div v-for="item in shortDaysLocale" :key="item" class="text-body2 calendar-column">
                      {{ item }}
                    </div>
                  </div>
                  <div class="col">
                    <ul v-for="r in 6" :key="r" class="calendar-days">
                      <li
                        v-for="i in numOfDays"
                        :key="i"
                        :class="{'calendar-days-selected': isDateSelected(r, i, 'first', startMonthDay, endMonthDate), 'calendar-days-in-range': isDateInRange(r, i, 'first', startMonthDay, endMonthDate), 'calendar-days-disabled': isDateDisabled(r, i, startMonthDay, endMonthDate)}"
                        @click="selectFirstItem(r, i)"
                      >
                        {{ getDayCell(r, i, startMonthDay, endMonthDate) }}
                      </li>
                    </ul>
                  </div>
                </div>
                <div class="column col calendar-month gt-sm q-pt-md">
                  <div class="row items-center justify-between">
                    <div />
                    <div class="text-body2 dynamic">
                      {{ monthsLocale[startNextActiveMonth] +' '+ activeYearEnd }}
                    </div>
                    <w-btn flat round dense icon="fal fa-arrow-right" @click="goNextMonth()" />
                  </div>
                  <div class="row">
                    <div v-for="item in shortDaysLocale" :key="item" class="text-body2 calendar-column">
                      {{ item }}
                    </div>
                  </div>
                  <div class="col">
                    <ul v-for="r in 6" :key="r" class="calendar-days">
                      <li
                        v-for="i in numOfDays"
                        :key="i"
                        :class="{'calendar-days-selected': isDateSelected(r, i, 'second', startNextMonthDay, endNextMonthDate), 'calendar-days-in-range': isDateInRange(r, i, 'second', startNextMonthDay, endNextMonthDate), 'calendar-days-disabled': isDateDisabled(r, i, startNextMonthDay, endNextMonthDate)}"
                        @click="selectSecondItem(r, i)"
                      >
                        {{ getDayCell(r, i, startNextMonthDay, endNextMonthDate) }}
                      </li>
                    </ul>
                  </div>
                </div>
                <div class="column col calendar-presets">
                  <w-btn v-for="(item, idx) in finalPresetRanges" :key="idx" flat :label="item.label" :color="presetActive === item.label ? 'primary' : ''" @click="updatePreset(item)" />
                  <w-btn v-close-popup class="q-mt-md" color="primary" label="OK" @click="setDateValue()" />
                </div>
              </div>
            </q-card-section>
          </q-card>
        </q-popup-proxy>
      </q-icon>
    </template>
  </w-field>
</template>

<script>
import { date } from "quasar";
import presetDateRange from "./preset-date-range";

const defaultConfig = {};
const defaultI18n = "en";
const availableMonths = {
  en: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November",
    "December"],
};

const availableShortDays = {
  en: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
};

export default {
  name: "WDateRange",
  props: {
    modelValue: {
      type: Object,
      default: () => defaultConfig
    },
    configs: {
      type: Object,
      default: () => defaultConfig
    },
    i18n: {
      type: String,
      default: defaultI18n
    },
    startActiveMonth: {
      type: Number,
      default: new Date().getMonth()
    },
    startActiveYear: {
      type: Number,
      default: new Date().getFullYear()
    },
    presetRanges: {
      type: Array,
      default: null,
    },
    pastDateRange: {
      type: Boolean,
      default: false,
    },
  },
  emits: ["update:modelValue"],
  data() {
    return {
      dateRange: {},
      numOfDays: 7,
      isFirstChoice: true,
      presetActive: "",
      showMonth: true,
      activeMonthStart: this.startActiveMonth,
      activeYearStart: this.startActiveYear,
      activeYearEnd: this.startActiveYear
    };
  },
  computed: {
    monthsLocale() {
      return this.months || availableMonths[this.i18n];
    },
    shortDaysLocale() {
      return this.shortDays || availableShortDays[this.i18n];
    },
    startMonthDay() {
      return new Date(Date.UTC(this.activeYearStart, this.activeMonthStart, 1)).getDay();
    },
    startNextMonthDay() {
      return new Date(Date.UTC(this.activeYearStart, this.startNextActiveMonth, 1)).getDay();
    },
    endMonthDate() {
      return new Date(Date.UTC(this.activeYearEnd, this.startNextActiveMonth, 0)).getDate();
    },
    endNextMonthDate() {
      return new Date(Date.UTC(this.activeYearEnd, this.activeMonthStart + 2, 0)).getDate();
    },
    startNextActiveMonth() {
      return this.activeMonthStart >= 11 ? 0 : this.activeMonthStart + 1;
    },
    finalPresetRanges() {
      if (this.presetRanges && this.presetRanges.length) {
        return this.presetRanges;
      }
      const presets = presetDateRange(this.i18n);
      return this.pastDateRange ? presets.past : presets.future;
    },
    dateRangeString() {
      let result;

      if (this.dateRange.start || this.dateRange.end) {
        result = `${this.getDateString(this.dateRange.start)} - ${this.getDateString(this.dateRange.end)}`;
      } else {
        result = "Choose dates";
      }

      return result;
    },
  },
  watch: {
    startNextActiveMonth(value) {
      if (value === 0) this.activeYearEnd = this.activeYearStart + 1;
    }
  },
  mounted() {
    this.dateRange = this.modelValue || {};
  },
  created() {
    if (this.activeMonthStart === 11) this.activeYearEnd = this.activeYearStart + 1;
  },
  methods: {
    getDateString(d) {
      let result = "";

      if (d) {
        result = date.formatDate(d, "D MMM YYYY");
      }

      return result;
    },
    getDayIndexInMonth(r, i, startMonthDay) {
      const date = (this.numOfDays * (r - 1)) + i;
      return date - startMonthDay;
    },
    getDayCell(r, i, startMonthDay, endMonthDate) {
      const result = this.getDayIndexInMonth(r, i, startMonthDay);
      // bound by > 0 and < last day of month
      return result > 0 && result <= endMonthDate ? result : "";
    },
    getNewDateRange(result, activeMonth, activeYear) {
      const newData = {};
      let key = "start";
      if (!this.isFirstChoice) {
        key = "end";
      } else {
        newData.end = null;
      }
      const resultDate = new Date(Date.UTC(activeYear, activeMonth, result));
      if (!this.isFirstChoice && resultDate < this.dateRange.start) {
        this.isFirstChoice = false;
        return { start: resultDate };
      }

      // toggle first choice
      this.isFirstChoice = !this.isFirstChoice;
      newData[key] = resultDate;
      return newData;
    },
    selectFirstItem(r, i) {
      const result = this.getDayIndexInMonth(r, i, this.startMonthDay);
      this.dateRange = Object.assign({}, this.dateRange, this.getNewDateRange(result, this.activeMonthStart,
        this.activeYearStart));
      if (this.dateRange.start && this.dateRange.end) {
        this.presetActive = "";
        if (this.isCompact) {
          this.showMonth = false;
        }
      }
    },
    selectSecondItem(r, i) {
      const result = this.getDayIndexInMonth(r, i, this.startNextMonthDay);
      this.dateRange = Object.assign({}, this.dateRange, this.getNewDateRange(result, this.startNextActiveMonth,
        this.activeYearEnd));
      if (this.dateRange.start && this.dateRange.end) {
        this.presetActive = "";
      }
    },
    isDateSelected(r, i, key, startMonthDay, endMonthDate) {
      const result = this.getDayIndexInMonth(r, i, startMonthDay);
      if (result < 1 || result > endMonthDate) return false;

      let currDate = null;
      if (key === "first") {
        currDate = new Date(Date.UTC(this.activeYearStart, this.activeMonthStart, result));
      } else {
        currDate = new Date(Date.UTC(this.activeYearEnd, this.startNextActiveMonth, result));
      }
      return (this.dateRange.start && this.dateRange.start.getTime() === currDate.getTime()) ||
        (this.dateRange.end && this.dateRange.end.getTime() === currDate.getTime());
    },
    isDateInRange(r, i, key, startMonthDay, endMonthDate) {
      const result = this.getDayIndexInMonth(r, i, startMonthDay);
      if (result < 1 || result > endMonthDate) return false;

      let currDate = null;
      if (key === "first") {
        currDate = new Date(Date.UTC(this.activeYearStart, this.activeMonthStart, result));
      } else {
        currDate = new Date(Date.UTC(this.activeYearEnd, this.startNextActiveMonth, result));
      }
      return (this.dateRange.start && this.dateRange.start.getTime() < currDate.getTime()) &&
        (this.dateRange.end && this.dateRange.end.getTime() > currDate.getTime());
    },
    isDateDisabled(r, i, startMonthDay, endMonthDate) {
      const result = this.getDayIndexInMonth(r, i, startMonthDay);
      // bound by > 0 and < last day of month
      return !(result > 0 && result <= endMonthDate);
    },
    goPrevMonth() {
      const prevMonth = new Date(Date.UTC(this.activeYearStart, this.activeMonthStart, 0));
      this.activeMonthStart = prevMonth.getMonth();
      this.activeYearStart = prevMonth.getFullYear();
      this.activeYearEnd = prevMonth.getFullYear();
    },
    goNextMonth() {
      const nextMonth = new Date(Date.UTC(this.activeYearEnd, this.startNextActiveMonth, 1));
      this.activeMonthStart = nextMonth.getMonth();
      this.activeYearStart = nextMonth.getFullYear();
      this.activeYearEnd = nextMonth.getFullYear();
    },
    updatePreset(item) {
      this.presetActive = item.label;
      this.dateRange = item.dateRange;
      // update start active month
      this.activeMonthStart = this.dateRange.start.getMonth();
      this.activeYearStart = this.dateRange.start.getFullYear();
      this.activeYearEnd = this.dateRange.end.getFullYear();
    },
    setDateValue() {
      this.$emit("update:modelValue", this.dateRange);
    },
    clearDateValue() {
      this.dateRange = {};
      this.$emit("update:modelValue", null);
    },
  }
};
</script>
