<template>
  <box
    componentkey="Timeline"
    title="节目排期"
  >
    <div
      v-loading="deviceOptions.loading"
      class="l-flex__fill l-flex--col c-timeline"
      :class="{ full: full }"
    >
      <div class="prioritys">
        <div class="priority">
          <div class="priority3 priority__block" />
          <div>高优先级</div>
        </div>
        <div class="priority">
          <div class="priority2 priority__block" />
          <div>中优先级</div>
        </div>
        <div class="priority">
          <div class="priority1 priority__block" />
          <div>低优先级</div>
        </div>
      </div>
      <div class="l-flex__none l-flex c-timeline__row header">
        <div class="l-flex__auto l-flex--row c-timeline__right">
          <i
            class="l-flex__none c-timeline__arrow left el-icon-arrow-left u-pointer"
            :class="{ display: canPrevious }"
            @click="offsetTime(-1)"
          />
          <div class="l-flex__auto l-flex--row c-timeline__time">
            <div
              v-for="time in times"
              :key="time"
              class="l-flex__none"
            >
              {{ time }}
            </div>
          </div>
          <i
            class="l-flex__none c-timeline__arrow right display el-icon-arrow-right u-pointer"
            @click="offsetTime(1)"
          />
        </div>
      </div>
      <div class="l-flex--col c-timeline__main u-relative">
        <div class="l-flex__auto u-overflow-y--auto">
          <div
            v-for="item in deviceOptions.list"
            :key="item.id"
            class="l-flex c-timeline__row"
            :class="{ selected: item.id === deviceId }"
            @click="chooseProgramProxy(item)"
          >
            <div class="l-flex__auto l-flex--col c-timeline__right">
              <div
                v-if="item.options.loading"
                class="l-flex--row c-timeline__programs"
              >
                <i class="el-icon-loading has-padding--h" />加载中...
              </div>
              <div
                v-else-if="item.options.error"
                class="l-flex--row c-timeline__programs has-padding--h"
              >
                <el-link
                  type="warning"
                  @click.stop="getTimeline(item)"
                >
                  获取失败，点击重试
                </el-link>
              </div>
              <template v-else-if="item.options.list.length">
                <div
                  v-for="(programs, index) in item.options.list"
                  :key="index"
                  class="c-timeline__programs l-flex--row u-relative"
                >
                  <div
                    v-for="program in programs"
                    :key="program.key"
                    class="l-flex__none l-flex--row c-event-program u-pointer"
                    :class="[
                      { selected: program.event.selected },
                      `priority${program.event.priority}`,
                    ]"
                    :style="program.style"
                    @click.stop="chooseProgramProxy(item, program)"
                  >
                    <i
                      class="l-flex__none c-event-program__img o-program"
                      :style="program.event.style"
                    />
                    <div class="l-flex__auto">
                      <auto-text
                        class="c-event-program__time"
                        :text="program.time"
                        :tag="program.style.width"
                      />
                      <auto-text
                        class="c-event-program__name"
                        :text="program.event.target.name"
                        :tag="program.style.width"
                      />
                    </div>
                  </div>
                </div>
              </template>
              <div
                v-else
                class="l-flex--row c-timeline__programs has-padding--h"
              >
                当前时段暂无节目
              </div>
            </div>
          </div>
        </div>
        <div
          v-show="style"
          class="c-timeline__line"
        >
          <div
            class="c-timeline__mask"
            :style="style"
          />
        </div>
      </div>
    </div>
  </box>
</template>

<script>
import {
  getProducts, getTimeline
} from '@/api/device'

import {
  EventFreq, ScheduleType
} from '@/constant'
import { createListOptions } from '@/utils'
import {
  toDate,
  toDateStr,
  toTimeStr,
  toZeroPoint,
  getNearestHitDate,
  getStartDate,
  getFinishDate,
  pickMin,
  pickMax
} from '@/utils/event'
import { EventCache } from '@/utils/cache'
import Box from './Box'
export default {
  name: 'GatewayTimeline',
  components: { Box },
  props: {
    device: {
      type: Object,
      required: true
    },
    full: {
      type: Boolean,
      default: false
    }
  },
  data () {
    return {
      productSelectSchema: {
        remote: getProducts,
        pagination: true,
        value: 'id',
        label: 'name'
      },
      deviceOptions: createListOptions({
        productId: '',
        name: '',
        activate: 2,
        pageSize: 5
      }),
      style: null,
      canPrevious: false,
      startHour: 0,
      times: [],
      current: null,
      programProxy: null
    }
  },
  computed: {
    deviceId () {
      return this.device?.id
    },
    deivceName () {
      return this.device?.name
    },
    programName () {
      return this.programProxy?.event.target.name
    },
    programTime () {
      return this.programProxy?.event.time
    },
    programStyle () {
      return this.programProxy?.event.style
    },
    isAbnormal () {
      const deviceOptions = this.deviceOptions
      return (
        deviceOptions.error
        || (!deviceOptions.loading && deviceOptions.totalCount === 0)
      )
    },
    pickerOptions () {
      return {
        disabledDate: this.isDisableDate
      }
    }
  },
  created () {
    this.TIME_COUNT = this.full ? 5 : 3
    this.initTimes(new Date())
    this.getDevices()
    this.$timer = setInterval(this.calcLine, 1000)
  },
  beforeDestroy () {
    clearInterval(this.$timer)
  },
  methods: {
    getDevices () {
      const options = this.deviceOptions
      options.error = false
      const { id, name } = this.device
      options.list = [
        {
          id,
          name,
          options: {
            loading: true,
            error: false,
            events: [],
            list: []
          }
        }
      ]
      options.totalCount = 1
      options.list.forEach(this.getTimeline)
    },
    getTimeline (device) {
      const options = device.options
      options.error = false
      options.loading = true
      getTimeline(device.id, { custom: true })
        .finally(() => {
          options.loading = false
        })
        .then(
          events => {
            const now = Date.now()
            options.events = this.transformEvents(
              events
                .map(this.transformEvent)
                .filter(({ until }) => !until || now < toDate(until))
            )
            this.calcEvents(options)
          },
          () => {
            options.error = true
            options.list = []
          }
        )
    },
    transformEvent (event) {
      return {
        ...event,
        time: this.getTime(event),
        startDateTime: toDate(event.start),
        endDateTime: toDate(event.until),
        style: null,
        selected: false,
        img () {
          EventCache.getImage(this.target.type, this.target.id).then(img => {
            if (img) {
              this.style = { backgroundImage: `url("${img}")` }
            }
          })
        }
      }
    },
    transformEvents (events) {
      const map = {}
      for (let i = 0; i < events.length; i++) {
        const event = events[i]
        if (!map[event.priority]) {
          map[event.priority] = []
        }
        map[event.priority].push(event)
      }
      return Object.keys(map)
        .sort((a, b) => (a > b ? -1 : 1))
        .map(key => map[key].sort((a, b) => toDate(a.start) - toDate(b.start)))
    },
    getTime (event) {
      const { freq, start, until, byDay, startTime, endTime } = event
      switch (freq) {
        case EventFreq.WEEKLY:
          return `自${start.split(' ')[0]}开始${
            until ? `至${until.split(' ')[0]}前` : ''
          } 每周${byDay
            .split(',')
            .map(val => ['日', '一', '二', '三', '四', '五', '六'][val])
            .join('、')} ${startTime} - ${endTime}`
        default:
          return until ? `${start} - ${until}` : `自${start}开始`
      }
    },
    isDisableDate (date) {
      const now = new Date()
      const min = new Date(now.getFullYear(), now.getMonth(), now.getDate())
      const max = new Date(
        now.getFullYear(),
        now.getMonth(),
        now.getDate() + 30
      )
      return date < min || date > max
    },
    initTimes (date) {
      const maxDate = pickMax(toDate(date), new Date())
      this.current = toZeroPoint(maxDate)
      this.startHour = Math.min(20, maxDate.getHours())
      this.refreshTimes(this.startHour)
    },
    refreshTimes (start) {
      const times = []
      for (let i = 0; i < this.TIME_COUNT; i++) {
        times.push(`${(start + i).toString().padStart(2, '0')}:00`)
      }
      this.times = times

      this.$startDateTime = toDate(this.current.getTime() + start * 3600000)
      this.$endDateTime = toDate(
        this.current.getTime() + (start + 4) * 3600000
      )
      this.calcLine()

      this.refreshTimeline()
    },
    calcLine () {
      const now = Date.now()
      this.canPrevious = now < this.$startDateTime
      if (now < this.$startDateTime || now >= this.$endDateTime) {
        this.style = null
      } else {
        this.style = {
          left: `${Math.min(100, (now - this.$startDateTime) / 144000)}%`
        }
      }
    },
    refreshTimeline () {
      this.deviceOptions.list.forEach(device => {
        this.calcEvents(device.options)
      })
    },
    offsetTime (offset) {
      const next = this.startHour + offset
      if (offset < 0) {
        const timestamp = this.current.getTime() + next * 3600000
        const now = new Date()
        now.setMinutes(0)
        now.setSeconds(0)
        now.setMilliseconds(0)
        if (timestamp >= now) {
          if (next < 0) {
            this.initTimes(timestamp)
          } else {
            this.refreshTimes((this.startHour = next))
          }
        }
      } else if (next > 20) {
        this.initTimes(this.$endDateTime)
      } else {
        this.refreshTimes((this.startHour = next))
      }
    },
    onTimeChange (val) {
      this.initTimes(val)
    },
    calcSamePriorityEvents (events) {
      const total = 144000
      const arr = []
      for (let i = 0; i < events.length; i++) {
        const event = events[i]
        const { startDateTime, endDateTime } = event
        if (
          (endDateTime && endDateTime <= this.$startDateTime)
          || startDateTime >= this.$endDateTime
        ) {
          continue
        }
        const hit = getNearestHitDate(
          event,
          this.$startDateTime,
          this.$endDateTime
        )
        if (hit) {
          const startDate = getStartDate(event, hit)
          const endDate = getFinishDate(event, hit)
          arr.push({
            key: event.target.id,
            event,
            time: `${toDateStr(startDate)} ${toTimeStr(
              startDate
            )} - ${toDateStr(endDate)} ${toTimeStr(endDate)}`,
            style: {
              left: `${(hit - this.$startDateTime) / total}%`,
              width: `${
                (pickMin(this.$endDateTime, getFinishDate(event, hit)) - hit) /
                total
              }%`,
              zIndex: event.priority
            }
          })
          event.img?.()
          if (endDate >= this.$endDateTime) {
            break
          }
        }
      }
      return arr
    },
    calcEvents (options) {
      if (options.loading || options.error) {
        return
      }
      options.list = options.events
        .map(this.calcSamePriorityEvents)
        .filter(events => events.length)
    },
    chooseProgramProxy (device, programProxy) {
      this.device = device
      if (this.programProxy) {
        this.programProxy.event.selected = false
      }
      if (programProxy) {
        programProxy.event.selected = true
      }
      this.programProxy = programProxy
    },
    onView () {
      if (this.programProxy) {
        switch (this.programProxy.event.target.type) {
          case ScheduleType.CALENDAR:
            window.open(
              this.$router.resolve({
                name: 'program',
                params: { id: this.programProxy.event.target.id }
              }).href,
              '_blank'
            )
            break
          case ScheduleType.RECUR:
            this.$refs.scheduleDialog.show(this.programProxy.event.target.id)
            break
          default:
            break
        }
      }
    }
  }
}
</script>

<style lang="scss" scoped>
.c-device-detail {
  height: 200px;

  &__screen {
    width: 354px;
    margin-right: 24px;
    border-radius: 4px;
  }

  &__name {
    color: $black;
    font-size: 20px;
    font-weight: bold;
    line-height: 1;
  }

  &__program {
    margin-top: 20px;
    color: $blue;
    font-size: 20px;
    font-weight: bold;
  }

  &__time {
    margin-top: $spacing;
    color: $info--dark;
    font-size: 20px;
  }
}

.c-timeline {
  min-height: 400px;

  &__main {
    flex: 0 1 auto;
    min-height: 0;
    border: 1px solid $border;
  }

  &__row {
    & + & {
      border-top: 1px solid $border;
    }
  }

  &__row.header {
    .c-timeline__left {
      border-right: none;
    }
  }

  &__row.header &__right {
    color: $black;
    user-select: none;
    background-color: transparent;
  }

  &__row.selected &__left {
    color: #fff;
    background-color: #9fbfe8;
  }

  &__row.selected &__left::after {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    bottom: 0;
    width: 4px;
    background-color: $blue;
  }

  &__left {
    display: inline-flex;
    align-items: center;
    width: 210px;
    padding: 0 10px 0 20px;
    margin-right: 1px;
    color: $black;
    font-size: 16px;
    background-color: #fafbfc;
    border-right: 1px solid $border;
  }

  &__right {
    position: relative;
    color: $info--dark;
    font-size: 14px;
    line-height: 1;
    overflow: hidden;
  }

  &__programs {
    box-sizing: content-box;
    height: 48px;

    & + & {
      border-top: 1px dashed $border;
    }
  }

  &__arrow {
    visibility: hidden;
    position: absolute;
    top: 50%;
    // padding: 6px;
    color: #fff;
    font-size: 12px;
    border-radius: 4px;
    background-color: $blue;
    transform: translateY(-50%);

    &.left {
      left: 0;
    }

    &.right {
      right: 0;
    }

    &.display {
      visibility: visible;
    }
  }

  &__time {
    justify-content: space-around;
    padding: 10px 0;
    color: $black;
    font-size: 14px;
  }

  &__line {
    position: absolute;
    top: -12px;
    left: 0px;
    right: 0;
    bottom: 0;
    pointer-events: none;
  }

  &__mask {
    position: absolute;
    top: 0;
    left: 0;
    bottom: 0;
    color: #ff0000;
    border-right: 1px solid currentColor;
    z-index: 999;

    &::after {
      content: "";
      position: absolute;
      top: 0;
      right: -5px;
      width: 9px;
      height: 9px;
      border-radius: 50%;
      background-color: currentColor;
    }
  }
}

.c-event-program {
  position: absolute;
  top: 0;
  bottom: 0;
  font-size: 12px;
  overflow: hidden;

  &:hover {
    // color: #fff;
    // background-color: rgba($blue, 0.4);
  }

  &.selected {
    color: #fff;
    background-color: $blue;
    z-index: 99;
  }

  &__img {
    width: 82px;
    height: 46px;
    margin-right: 8px;
    border-radius: 2px;
    margin-left: 1px;
  }

  &__name {
    margin-top: 10px;
  }
}

.o-priority {
  display: inline-block;
  padding: 4px;
  font-size: 12px;
  border-radius: 4px;

  & + & {
    margin-left: 4px;
  }
}

.priority1 {
  color: #fff;
  background-color: #79829D;
}

.priority2 {
  color: #fff;
  background-color: #7E4DE2;
}

.priority3 {
  color: #fff;
  background-color: #FF2222;
}

.o-program {
  background: url("~@/assets/program_bg.png") 0 0 / 100% 100% no-repeat;
}
.c-timeline__main {
  border: none;
  flex:1
}
.c-timeline__time {
  color: #fff;
}
.full {
  .c-event-program,
  .c-timeline__time,
  .c-timeline__arrow {
    font-size: 20px;
    font-weight: 600;
  }

  .c-timeline__programs {
    height: 96px;
  }
  .c-event-program__img {
    width: 168px;
    height: 94px;
  }
}
.prioritys {
  display: flex;
  justify-content: flex-end;
  transform: scale(0.8);
  transform-origin: top right;
  .priority {
    display: flex;
    align-items: center;
    margin-right: 1em;
    font-size: 12px;
    &__block {
      width: 12px;
      height: 12px;
      margin-right: 4px;
    }
  }
  margin: 10px 0;
}
.full {
  .prioritys {
    display: flex;
    justify-content: flex-end;
    transform: scale(1);
    .priority {
      display: flex;
      align-items: center;
      margin-right: 1em;
      font-size: 20px;
      &__block {
        width: 20px;
        height: 20px;
        margin-right: 4px;
      }
    }
    margin: 10px 0;
  }
}
</style>
