import { mergeProps as _mergeProps, createVNode as _createVNode, withDirectives as _withDirectives } from "vue";
// Styles
import "./VCalendarCategory.css";
import "./VCalendarDaily.css";
import "./VCalendarWeekly.css";

// Components
import { VCalendarCategory } from "./VCalendarCategory.js";
import { VCalendarDaily } from "./VCalendarDaily.js";
import { VCalendarWeekly } from "./VCalendarWeekly.js"; // Composables
import { makeCalendarBaseProps } from "./composables/calendarBase.js";
import { makeCalendarWithEventsProps, useCalendarWithEvents } from "./composables/calendarWithEvents.js";
import { forwardRefs } from "../../composables/forwardRefs.js"; // Directives
import vResize from "../../directives/resize/index.js"; // Utilities
import { computed, onMounted, onUpdated, ref, watch } from 'vue';
import { getParsedCategories } from "./util/parser.js";
import { copyTimestamp, DAY_MIN, DAYS_IN_MONTH_MAX, DAYS_IN_WEEK, getEndOfMonth, getStartOfMonth, nextDay, prevDay, relativeDays, timestampToDate, updateFormatted, updateRelative, updateWeekday, validateTimestamp } from "./util/timestamp.js";
import { genericComponent, useRender } from "../../util/index.js"; // Types
// Types
export const VCalendar = genericComponent()({
  name: 'VCalendar',
  directives: {
    vResize
  },
  props: {
    modelValue: {
      type: [String, Number, Date],
      validate: validateTimestamp
    },
    categoryDays: {
      type: [Number, String],
      default: 1,
      validate: x => isFinite(parseInt(x)) && parseInt(x) > 0
    },
    categories: {
      type: [Array, String],
      default: ''
    },
    categoryText: {
      type: [String, Function]
    },
    maxDays: {
      type: Number,
      default: 7
    },
    categoryHideDynamic: {
      type: Boolean
    },
    categoryShowAll: {
      type: Boolean
    },
    categoryForInvalid: {
      type: String,
      default: ''
    },
    ...makeCalendarBaseProps(),
    ...makeCalendarWithEventsProps()
  },
  setup(props, {
    slots,
    attrs,
    emit
  }) {
    const root = ref();
    const base = useCalendarWithEvents(props, slots, attrs);
    const lastStart = ref(null);
    const lastEnd = ref(null);
    const parsedCategoryDays = computed(() => {
      return parseInt(String(props.categoryDays)) || 1;
    });
    const parsedCategories = computed(() => {
      return getParsedCategories(props.categories, props.categoryText);
    });
    const renderProps = computed(() => {
      const around = base.parsedValue.value;
      let component = null;
      let maxDays = props.maxDays;
      let categories = parsedCategories.value;
      let start = around;
      let end = around;
      switch (props.type) {
        case 'month':
          component = VCalendarWeekly;
          start = getStartOfMonth(around);
          end = getEndOfMonth(around);
          break;
        case 'week':
          component = VCalendarDaily;
          start = base.getStartOfWeek(around);
          end = base.getEndOfWeek(around);
          maxDays = 7;
          break;
        case 'day':
          component = VCalendarDaily;
          maxDays = 1;
          break;
        case '4day':
          component = VCalendarDaily;
          end = relativeDays(copyTimestamp(end), nextDay, 3);
          updateFormatted(end);
          maxDays = 4;
          break;
        case 'custom-weekly':
          component = VCalendarWeekly;
          start = base.parsedStart.value || around;
          end = base.parsedEnd.value;
          break;
        case 'custom-daily':
          component = VCalendarDaily;
          start = base.parsedStart.value || around;
          end = base.parsedEnd.value;
          break;
        case 'category':
          const days = parsedCategoryDays.value;
          component = VCalendarCategory;
          end = relativeDays(copyTimestamp(end), nextDay, days);
          updateFormatted(end);
          maxDays = days;
          categories = getCategoryList(categories);
          break;
        default:
          const type = props.type;
          throw new Error(`${type} is not a valid Calendar type`);
      }
      return {
        component,
        start,
        end,
        maxDays,
        categories
      };
    });
    const eventWeekdays = computed(() => {
      return base.effectiveWeekdays.value;
    });
    const categoryMode = computed(() => {
      return props.type === 'category';
    });
    const monthLongFormatter = computed(() => {
      return base.getFormatter({
        timeZone: 'UTC',
        month: 'long'
      });
    });
    const monthShortFormatter = computed(() => {
      return base.getFormatter({
        timeZone: 'UTC',
        month: 'short'
      });
    });
    const title = computed(() => {
      const {
        start,
        end
      } = renderProps.value;
      const spanYears = start.year !== end.year;
      const spanMonths = spanYears || start.month !== end.month;
      if (spanYears) {
        return monthShortFormatter.value(start, true) + ' ' + start.year + ' - ' + monthShortFormatter.value(end, true) + ' ' + end.year;
      }
      if (spanMonths) {
        return monthShortFormatter.value(start, true) + ' - ' + monthShortFormatter.value(end, true) + ' ' + end.year;
      } else {
        return monthLongFormatter.value(start, false) + ' ' + start.year;
      }
    });
    function checkChange() {
      const {
        start,
        end
      } = renderProps.value;
      if (!lastStart.value || !lastEnd.value || start.date !== lastStart.value.date || end.date !== lastEnd.value.date) {
        lastStart.value = start;
        lastEnd.value = end;
        emit('change', {
          start,
          end
        });
      }
    }
    function move(amount = 1) {
      const moved = copyTimestamp(base.parsedValue.value);
      const forward = amount > 0;
      const mover = forward ? nextDay : prevDay;
      const limit = forward ? DAYS_IN_MONTH_MAX : DAY_MIN;
      let times = forward ? amount : -amount;
      while (--times >= 0) {
        switch (props.type) {
          case 'month':
            moved.day = limit;
            mover(moved);
            break;
          case 'week':
            relativeDays(moved, mover, DAYS_IN_WEEK);
            break;
          case 'day':
            relativeDays(moved, mover, 1);
            break;
          case '4day':
            relativeDays(moved, mover, 4);
            break;
          case 'category':
            relativeDays(moved, mover, parsedCategoryDays.value);
            break;
        }
      }
      updateWeekday(moved);
      updateFormatted(moved);
      updateRelative(moved, base.times.now);
      if (props.modelValue instanceof Date) {
        emit('update:modelValue', timestampToDate(moved));
      } else if (typeof props.modelValue === 'number') {
        emit('update:modelValue', timestampToDate(moved).getTime());
      } else {
        emit('update:modelValue', moved.date);
      }
      emit('moved', moved);
    }
    function next(amount = 1) {
      move(amount);
    }
    function prev(amount = 1) {
      move(-amount);
    }
    function getCategoryList(categories) {
      if (!base.noEvents.value) {
        const categoryMap = categories.reduce((map, category, index) => {
          if (typeof category === 'object' && category.categoryName) map[category.categoryName] = {
            index,
            count: 0
          };else if (typeof category === 'string') map[category] = {
            index,
            count: 0
          };
          return map;
        }, {});
        if (!props.categoryHideDynamic || !props.categoryShowAll) {
          let categoryLength = categories.length;
          base.parsedEvents.value.forEach(ev => {
            let category = ev.category;
            if (typeof category !== 'string') {
              category = props.categoryForInvalid;
            }
            if (!category) {
              return;
            }
            if (category in categoryMap) {
              categoryMap[category].count++;
            } else if (!props.categoryHideDynamic) {
              categoryMap[category] = {
                index: categoryLength++,
                count: 1
              };
            }
          });
        }
        if (!props.categoryShowAll) {
          for (const category in categoryMap) {
            if (categoryMap[category].count === 0) {
              delete categoryMap[category];
            }
          }
        }
        categories = categories.filter(category => {
          if (typeof category === 'object' && category.categoryName) {
            return categoryMap.hasOwnProperty(category.categoryName);
          } else if (typeof category === 'string') {
            return categoryMap.hasOwnProperty(category);
          }
          return false;
        });
      }
      return categories;
    }
    watch(renderProps, checkChange);
    onMounted(() => {
      base.updateEventVisibility();
      checkChange();
    });
    onUpdated(() => {
      window.requestAnimationFrame(base.updateEventVisibility);
    });
    useRender(() => {
      const {
        start,
        end,
        maxDays,
        component: Component,
        categories
      } = renderProps.value;
      return _withDirectives(_createVNode(Component, _mergeProps({
        "ref": root,
        "class": ['v-calendar', {
          'v-calendar-events': !base.noEvents.value
        }],
        "role": "grid"
      }, Component.filterProps(props), {
        "start": start.date,
        "end": end.date,
        "maxDays": maxDays,
        "weekdays": base.effectiveWeekdays.value,
        "categories": categories,
        "onClick:date": (e, day) => {
          if (attrs['onUpdate:modelValue']) emit('update:modelValue', day.date);
        }
      }), base.getScopedSlots()), [[vResize, base.updateEventVisibility, void 0, {
        quiet: true
      }]]);
    });
    return forwardRefs({
      ...base,
      lastStart,
      lastEnd,
      parsedCategoryDays,
      renderProps,
      eventWeekdays,
      categoryMode,
      title,
      monthLongFormatter,
      monthShortFormatter,
      parsedCategories,
      checkChange,
      move,
      next,
      prev,
      getCategoryList
    }, root);
  }
});
//# sourceMappingURL=VCalendar.js.map