<template>
  <div class="l-flex--col c-week-calendar has-border">
    <div class="l-flex__none l-flex c-week-calendar__header">
      <div class="l-flex__none o-time header" />
      <div class="l-flex__fill l-flex">
        <div
          v-for="item in headers"
          :key="item.info"
          class="l-flex__auto u-readonly"
        >
          <div class="c-week-calendar__week">{{ item.day }}</div>
          <div class="c-week-calendar__info">{{ item.info }}</div>
        </div>
      </div>
    </div>
    <div class="l-flex__auto u-overflow-y--auto">
      <div class="l-flex">
        <div
          v-once
          class="l-flex__none l-flex--col c-week-calendar__time"
        >
          <div
            v-for="(val, hour) in 24"
            :key="hour"
            class="l-flex__none o-time"
          >
            {{ hour.toString().padStart(2, '0') }}:00
          </div>
        </div>
        <div class="l-flex__fill l-flex c-week-calendar__right">
          <div
            v-for="day in week"
            :key="day.value"
            class="l-flex__fill l-flex--col c-week-calendar__day"
            :class="day.status"
          >
            <template v-for="item in day.items">
              <div
                v-if="item.event"
                :key="item.key"
                class="l-flex__none c-week-calendar__item"
                :class="item.status"
                :style="item.style"
                @click="onClick(item.event)"
              >
                <event-item
                  :item="item"
                  :editable="editable"
                  @remove="$listeners.remove"
                />
              </div>
              <div
                v-if="!item.event"
                :key="item.key"
                class="l-flex__none c-week-calendar__item"
                :class="item.status"
                :style="item.style"
                @dblclick="onAdd(item)"
              />
            </template>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import { EventFreq } from '@/constant'
import {
  ONE_DAY,
  toDate,
  toDateStr,
  toTimeStr,
  pickMin,
  pickMax,
  isOverDay,
  isHitOverDayTime,
  correctEndTime
} from '@/utils/event'
import EventItem from './EventItemWeek'

const Week = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']

export default {
  name: 'ScheduleCalendarWeek',
  components: {
    EventItem
  },
  props: {
    editable: {
      type: [Boolean, String],
      default: false
    },
    weeks: {
      type: Array,
      default: null
    },
    cursor: {
      type: Number,
      default: 0
    }
  },
  computed: {
    currWeek () {
      return this.weeks[this.cursor]
    },
    headers () {
      return this.currWeek.map(({ value }, i) => {
        return { day: value, info: Week[i] }
      })
    },
    week () {
      return this.currWeek.map(this.transform)
    }
  },
  methods: {
    transform (day) {
      return { ...day, items: this.createItems(day) }
    },
    createItems ({ date, events }) {
      const maxDate = new Date(date.getTime() + ONE_DAY)
      const week = date.getDay()
      const arr = []
      for (let i = 0; i < events.length; i++) {
        const eventProxy = events[i]
        switch (eventProxy.origin.freq) {
          case EventFreq.ONCE:
            arr.push(this.createOnceItem(eventProxy, date, maxDate))
            break
          case EventFreq.WEEKLY:
            if (isHitOverDayTime(eventProxy.origin, date)) {
              arr.push(this.createRemainderWeeklyItem(eventProxy, date, maxDate))
            }
            if (eventProxy.origin.byDay.includes(week)) {
              arr.push(this.createWeeklyItem(eventProxy, date, maxDate))
            }
            break
          default:
            break
        }
      }
      const items = []
      let minDate = toDate(date)
      if (arr.length) {
        arr.sort((a, b) => a.minDate - b.minDate)
        for (let i = 0; i < arr.length; i++) {
          if (arr[i].minDate > minDate) {
            this.createPlaceholdeItem(items, minDate, arr[i].minDate)
          }
          items.push(arr[i])
          minDate = arr[i].maxDate
        }
      }
      if (this.editable && minDate < maxDate) {
        this.createPlaceholdeItem(items, minDate, maxDate)
      }
      return items
    },
    createPlaceholdeItem (items, minDate, maxDate) {
      const now = new Date()
      let startDate = minDate
      if (maxDate > now && minDate < now) {
        items.push(this.createItem(null, minDate, now))
        startDate = now
      }
      items.push(this.createItem(null, startDate, maxDate))
    },
    createItem (eventProxy, minDate, maxDate) {
      const duration = (maxDate - minDate) / 1000
      return {
        key: Math.random().toString(16).slice(2),
        event: eventProxy,
        status: eventProxy
          ? duration <= 7200 ? 'hold hidden' : 'hold'
          : maxDate > Date.now()
            ? this.editable ? 'free' : 'empty'
            : 'disabled',
        placement: duration <= 7200 ? 'right-start' : 'right-end',
        minDate,
        maxDate,
        startTime: toTimeStr(minDate),
        endTime: toTimeStr(maxDate),
        duration,
        style: { height: `${duration * 0.02}px` }
      }
    },
    createOnceItem (eventProxy, minDate, maxDate) {
      const { start, until } = eventProxy.origin
      return this.createItem(eventProxy, pickMax(toDate(start), minDate), pickMin(toDate(until), maxDate))
    },
    createRemainderWeeklyItem (eventProxy, minDate, maxDate) {
      const { until, endTime } = eventProxy.origin
      const endDate = pickMin(toDate(`${toDateStr(minDate)} ${correctEndTime(endTime)}`), pickMin(toDate(until), maxDate))
      return this.createItem(eventProxy, minDate, endDate)
    },
    createWeeklyItem (eventProxy, minDate, maxDate) {
      const { start, until, startTime, endTime } = eventProxy.origin
      const startDate = pickMax(toDate(`${toDateStr(minDate)} ${startTime}`), pickMax(toDate(start), minDate))
      const endDate = pickMin(toDate(`${toDateStr(minDate, isOverDay(eventProxy.origin) ? 1 : 0)} ${correctEndTime(endTime)}`), pickMin(toDate(until), maxDate))
      return this.createItem(eventProxy, startDate, endDate)
    },
    onClick (event) {
      this.$emit('edit', event)
    },
    onAdd ({ minDate, maxDate }) {
      this.$emit('add', {
        freq: EventFreq.ONCE,
        start: `${toDateStr(minDate)} ${toTimeStr(minDate)}`,
        until: `${toDateStr(maxDate)} 00:00:00`
      })
    }
  }
}
</script>

<style lang="scss" scoped>
.c-week-calendar {
  position: relative;
  color: $black;
  font-size: 16px;

  &__header {
    padding: 10px 0;
    text-align: center;
    border-bottom: 1px solid $info;
  }

  &__week {
    font-size: 20px;
    font-weight: bold;
  }

  &__info {
    color: $info;
  }

  &__time {
    align-self: flex-start;
    border-right: 1px solid $info;
  }

  &__right {
    background-size: 100% 72px;
    background-image: linear-gradient(
      to bottom,
      transparent 0,
      transparent 71px,
      $info 71px
    );
  }

  &__day {
    user-select: none;

    &.enabled {
      .c-week-calendar__item.disabled {
        background-color: rgba($gray, 0.5);
        cursor: not-allowed;
      }
    }

    &.invalid {
      background-color: lighten($gray--light, 4%);
      cursor: not-allowed;
    }

    & + & {
      border-left: 1px solid $info;
    }
  }

  .c-week-calendar__item {
    &.hidden {
      overflow: hidden;
    }

    &.hold {
      margin: 0 6px;
      color: #fff;
      text-align: center;
      border-radius: $radius--mini;
      background-color: $blue;
      transition: background-color 0.4s;
    }

    &.hold,
    &.free {
      cursor: pointer;
    }

    &.free:hover {
      margin: 0 6px;
      border-radius: $radius--mini;
      background-color: rgba($blue, 0.5);
    }
  }
}

.o-time {
  display: inline-block;
  width: 70px;
  height: 72px;
  text-align: center;

  &.header {
    height: auto;
  }
}
</style>
