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

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

// Directives
import Resize from '../../directives/resize'

// Mixins
import CalendarWithIntervals from './mixins/calendar-with-intervals'

// Util
import { convertToUnit } from '../../util/helpers'
import { VTimestamp } from './util/timestamp'

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

  directives: { Resize },

  data: () => ({
    scrollPush: 0
  }),

  computed: {
    classes (): object {
      return {
        'v-calendar-daily': true,
        ...this.themeClasses
      }
    }
  },

  mounted () {
    this.init()
  },

  methods: {
    init () {
      this.$nextTick(this.onResize)
    },
    onResize () {
      this.scrollPush = this.getScrollPush()
    },
    getScrollPush (): number {
      const area = this.$refs.scrollArea as HTMLElement
      const pane = this.$refs.pane as HTMLElement

      return area && pane ? (area.offsetWidth - pane.offsetWidth) : 0
    },
    genHead (): VNode {
      return this.$createElement('div', {
        staticClass: 'v-calendar-daily__head',
        style: {
          marginRight: this.scrollPush + 'px'
        }
      }, [
        this.genHeadIntervals(),
        ...this.genHeadDays()
      ])
    },
    genHeadIntervals (): VNode {
      return this.$createElement('div', {
        staticClass: 'v-calendar-daily__intervals-head'
      })
    },
    genHeadDays (): VNode[] {
      return this.days.map(this.genHeadDay)
    },
    genHeadDay (day: VTimestamp): VNode {
      const slot = this.$scopedSlots.dayHeader

      return this.$createElement('div', {
        key: day.date,
        staticClass: 'v-calendar-daily_head-day',
        class: this.getRelativeClasses(day),
        on: this.getDefaultMouseEventHandlers(':day', _e => {
          return this.getSlotScope(day)
        })
      }, [
        this.genHeadWeekday(day),
        this.genHeadDayLabel(day),
        slot ? slot(day) : ''
      ])
    },
    genHeadWeekday (day: VTimestamp): VNode {
      const color = day.present ? this.color : undefined

      return this.$createElement('div', this.setTextColor(color, {
        staticClass: 'v-calendar-daily_head-weekday'
      }), this.weekdayFormatter(day, this.shortWeekdays))
    },
    genHeadDayLabel (day: VTimestamp): VNode {
      const color = day.present ? this.color : undefined

      return this.$createElement('div', this.setTextColor(color, {
        staticClass: 'v-calendar-daily_head-day-label',
        on: this.getMouseEventHandlers({
          'click:date': { event: 'click', stop: true },
          'contextmenu:date': { event: 'contextmenu', stop: true, prevent: true, result: false }
        }, _e => {
          return day
        })
      }), this.dayFormatter(day, false))
    },
    genBody (): VNode {
      return this.$createElement('div', {
        staticClass: 'v-calendar-daily__body'
      }, [
        this.genScrollArea()
      ])
    },
    genScrollArea (): VNode {
      return this.$createElement('div', {
        ref: 'scrollArea',
        staticClass: 'v-calendar-daily__scroll-area'
      }, [
        this.genPane()
      ])
    },
    genPane (): VNode {
      return this.$createElement('div', {
        ref: 'pane',
        staticClass: 'v-calendar-daily__pane',
        style: {
          height: convertToUnit(this.bodyHeight)
        }
      }, [
        this.genDayContainer()
      ])
    },
    genDayContainer (): VNode {
      return this.$createElement('div', {
        staticClass: 'v-calendar-daily__day-container'
      }, [
        this.genBodyIntervals(),
        ...this.genDays()
      ])
    },
    genDays (): VNode[] {
      return this.days.map(this.genDay)
    },
    genDay (day: VTimestamp, index: number): VNode {
      const slot = this.$scopedSlots.dayBody
      const scope = this.getSlotScope(day)

      return this.$createElement('div', {
        key: day.date,
        staticClass: 'v-calendar-daily__day',
        class: this.getRelativeClasses(day),
        on: this.getDefaultMouseEventHandlers(':time', e => {
          return this.getSlotScope(this.getTimestampAtEvent(e, day))
        })
      }, [
        ...this.genDayIntervals(index),
        slot ? slot(scope) : ''
      ])
    },
    genDayIntervals (index: number): VNode[] {
      return this.intervals[index].map(this.genDayInterval)
    },
    genDayInterval (interval: VTimestamp): VNode {
      const height: string | undefined = convertToUnit(this.intervalHeight)
      const styler = this.intervalStyle || this.intervalStyleDefault
      const slot = this.$scopedSlots.interval
      const scope = this.getSlotScope(interval)

      const data = {
        key: interval.time,
        staticClass: 'v-calendar-daily__day-interval',
        style: {
          height,
          ...styler(interval)
        }
      }

      const children = slot ? slot(scope) as VNodeChildren : undefined

      return this.$createElement('div', data, children)
    },
    genBodyIntervals (): VNode {
      const data = {
        staticClass: 'v-calendar-daily__intervals-body',
        on: this.getDefaultMouseEventHandlers(':interval', e => {
          return this.getTimestampAtEvent(e, this.parsedStart)
        })
      }

      return this.$createElement('div', data, this.genIntervalLabels())
    },
    genIntervalLabels (): VNode[] {
      return this.intervals[0].map(this.genIntervalLabel)
    },
    genIntervalLabel (interval: VTimestamp): VNode {
      const height: string | undefined = convertToUnit(this.intervalHeight)
      const short: boolean = this.shortIntervals
      const shower = this.showIntervalLabel || this.showIntervalLabelDefault
      const show = shower(interval)
      const label = show ? this.intervalFormatter(interval, short) : undefined

      return this.$createElement('div', {
        key: interval.time,
        staticClass: 'v-calendar-daily__interval',
        style: {
          height
        }
      }, [
        this.$createElement('div', {
          staticClass: 'v-calendar-daily__interval-text'
        }, label)
      ])
    }
  },

  render (h): VNode {
    return h('div', {
      class: this.classes,
      nativeOn: {
        dragstart: (e: MouseEvent) => {
          e.preventDefault()
        }
      },
      directives: [{
        modifiers: { quiet: true },
        name: 'resize',
        value: this.onResize
      }]
    }, [
      !this.hideHeader ? this.genHead() : '',
      this.genBody()
    ])
  }
})
