// Styles
// import '../../stylus/components/_calendar-daily.styl'

// Types
import { VNode, Component } from 'vue'

// Mixins
import CalendarBase from './mixins/calendar-base'

// Util
import props from './util/props'
import {
  DAYS_IN_MONTH_MAX,
  DAY_MIN,
  DAYS_IN_WEEK,
  VTimestamp,
  VTime,
  parseTimestamp,
  relativeDays,
  nextDay,
  prevDay,
  copyTimestamp,
  updateFormatted,
  updateWeekday,
  updateRelative,
  getStartOfMonth,
  getEndOfMonth
} from './util/timestamp'

// Calendars
import VCalendarMonthly from './VCalendarMonthly'
import VCalendarDaily from './VCalendarDaily'
import VCalendarWeekly from './VCalendarWeekly'

// Types
interface VCalendarRenderProps {
  start: VTimestamp
  end: VTimestamp
  component: string | Component
  maxDays: number
}

/* @vue/component */
export default CalendarBase.extend({
  name: 'v-calendar',

  props: {
    ...props.calendar,
    ...props.weeks,
    ...props.intervals
  },

  data: () => ({
    lastStart: null as VTimestamp | null,
    lastEnd: null as VTimestamp | null
  }),

  computed: {
    parsedValue (): VTimestamp {
      return parseTimestamp(this.value) ||
        this.parsedStart ||
        this.times.today
    },
    renderProps (): VCalendarRenderProps {
      const around = this.parsedValue
      let component: any = 'div'
      let maxDays = this.maxDays
      let start = around
      let end = around
      switch (this.type) {
        case 'month':
          component = VCalendarMonthly
          start = getStartOfMonth(around)
          end = getEndOfMonth(around)
          break
        case 'week':
          component = VCalendarDaily
          start = this.getStartOfWeek(around)
          end = this.getEndOfWeek(around)
          maxDays = 7
          break
        case 'day':
          component = VCalendarDaily
          maxDays = 1
          break
        case '4day':
          component = VCalendarDaily
          end = relativeDays(copyTimestamp(end), nextDay, 4)
          updateFormatted(end)
          maxDays = 4
          break
        case 'custom-weekly':
          component = VCalendarWeekly
          start = this.parsedStart || around
          end = this.parsedEnd
          break
        case 'custom-daily':
          component = VCalendarDaily
          start = this.parsedStart || around
          end = this.parsedEnd
          break
      }

      return { component, start, end, maxDays }
    }
  },

  watch: {
    renderProps: 'checkChange'
  },

  methods: {
    checkChange (): void {
      const { start, end } = this.renderProps
      if (start !== this.lastStart || end !== this.lastEnd) {
        this.lastStart = start
        this.lastEnd = end
        this.$emit('change', { start, end })
      }
    },
    move (amount = 1): void {
      const moved = copyTimestamp(this.parsedValue)
      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 (this.type) {
          case 'month':
            moved.day = limit
            mover(moved)
            break
          case 'week':
            relativeDays(moved, mover, DAYS_IN_WEEK)
            break
          case 'day':
            mover(moved)
            break
          case '4day':
            relativeDays(moved, mover, 4)
            break
        }
      }

      updateWeekday(moved)
      updateFormatted(moved)
      updateRelative(moved, this.times.now)

      this.$emit('input', moved.date)
      this.$emit('moved', moved)
    },
    next (amount = 1): void {
      this.move(amount)
    },
    prev (amount = 1): void {
      this.move(-amount)
    },
    timeToY (time: VTime, clamp = true): number | false {
      const c = this.$children[0] as any
      if (c && c.timeToY) {
        return c.timeToY(time, clamp)
      } else {
        return false
      }
    },
    minutesToPixels (minutes: number): number {
      const c = this.$children[0] as any
      if (c && c.minutesToPixels) {
        return c.minutesToPixels(minutes)
      } else {
        return -1
      }
    },
    scrollToTime (time: VTime): boolean {
      const c = this.$children[0] as any
      if (c && c.scrollToTime) {
        return c.scrollToTime(time)
      } else {
        return false
      }
    }
  },

  render (h): VNode {
    const { start, end, maxDays, component } = this.renderProps

    return h(component, {
      staticClass: 'v-calendar',
      props: {
        ...this.$props,
        start: start.date,
        end: end.date,
        maxDays
      },
      on: {
        ...this.$listeners,
        'click:date': (day: VTimestamp) => {
          if (this.$listeners['input']) {
            this.$emit('input', day.date)
          }
          if (this.$listeners['click:date']) {
            this.$emit('click:date', day)
          }
        }
      },
      scopedSlots: this.$scopedSlots
    })
  }
})
