<template>
  <div
    class="o-device u-pointer"
    @click="onClick"
  >
    <div class="l-flex__none l-flex--row o-device__header">
      <auto-text
        class="l-flex__fill"
        :text="name"
      />
      <template v-if="isOnline">
        <i
          v-if="isShotting"
          class="l-flex__none el-icon-loading"
          @click.stop
        />
        <i
          v-else
          class="l-flex__none o-device__shot"
          @click.stop="screenshot"
        />
      </template>
      <div
        class="l-flex__none o-device__tip"
        :class="statusClass"
      >
        <span class="u-color--white">{{ statusTip }}</span>
      </div>
    </div>
    <div
      class="l-flex__fill o-device__preview u-size--contain"
      :class="{ offline: !isOnline, empty: !shot }"
      :style="styles"
    />
    <div class="l-flex__auto l-flex--col center o-device__info">
      <template v-if="isOnline">
        <i
          v-if="loadingTimeline"
          class="l-flex__none el-icon-loading"
        />
        <template v-else-if="current">
          <auto-text
            class="l-flex__none o-device__current"
            :text="current.target.name"
          />
          <div class="l-flex__none l-flex--row o-device__time">
            <span class="o-device__hms">
              {{ current.startTime }}
              <span class="o-device__ymd">{{ current.startDate }}</span>
            </span>
            <template v-if="current.endDate">
              <span class="o-device__line" />
              <span class="o-device__hms">
                {{ current.endTime }}
                <span class="o-device__ymd">{{ current.endDate }}</span>
              </span>
            </template>
          </div>
        </template>
        <span
          v-else
          class="l-flex__auto l-flex--row center o-device__current"
        >
          当前暂无节目
        </span>
      </template>
      <template v-else>
        <span class="l-flex__auto l-flex--row center o-device__current">
          当前设备离线了
        </span>
      </template>
    </div>
    <div class="l-flex__none o-device__next">
      <auto-text
        v-if="next"
        :text="nextInfo"
      />
    </div>
    <div class="l-flex__none l-flex--row o-device__footer">
      <i class="l-flex__none o-device__icon el-icon-location-outline" />
      <auto-text
        class="l-flex__auto"
        :text="address"
      />
    </div>
  </div>
</template>

<script>
import { getTimeline } from '@/api/device'
import {
  listen,
  unlisten
} from '@/utils/mqtt'
import { ScreenshotCache } from '@/utils/cache'
import {
  isHit,
  getNearestHitDate,
  getFinishDate,
  getStartDate,
  toDate
} from '@/utils/event'
import { parseTime } from '@/utils'

export default {
  name: 'DeviceCard',
  props: {
    device: {
      type: Object,
      required: true
    }
  },
  data () {
    return {
      isShotting: false,
      shot: null,
      loadingTimeline: false,
      current: null,
      next: null
    }
  },
  computed: {
    name () {
      return this.device.name
    },
    address () {
      return `${this.device.remark}`
    },
    isOnline () {
      return this.device.onlineStatus === 1
    },
    statusClass () {
      return this.isOnline ? 'u-color--success dark' : 'u-color--error dark'
    },
    statusTip () {
      return this.isOnline ? '在线' : '离线'
    },
    styles () {
      return this.isOnline && this.shot ? { backgroundImage: `url("${this.shot}")` } : null
    },
    nextInfo () {
      return this.next ? `下一场：${this.next.startDate} ${this.next.startTime} ${this.next.target.name}` : ''
    }
  },
  created () {
    if (this.isOnline) {
      this.getTimeline()
      listen(this.onMessage)
      ScreenshotCache.watch(this.device, this.onScreenshotUpdate)
    } else {
      ScreenshotCache.remove(this.device.id)
    }
  },
  beforeDestroy () {
    if (this.isOnline) {
      unlisten(this.onMessage)
      ScreenshotCache.unwatch(this.device.id)
      clearTimeout(this.$timer)
      this.$timer = -1
    }
  },
  methods: {
    screenshot () {
      ScreenshotCache.screenshot(this.device.id)
    },
    onScreenshotUpdate ({ waiting, base64 }) {
      this.isShotting = waiting
      this.shot = waiting ? null : base64
    },
    onClick () {
      this.$router.push({
        name: 'device-detail',
        params: { id: this.device.id }
      })
    },
    onMessage (topic, message) {
      if (message) {
        const result = new RegExp(`${this.device.productId}/${this.device.id}/(.+)`).exec(topic)
        if (result) {
          switch (result[1]) {
            case 'calendar/update':
              this.onCalendarUpdate(message)
              break
            default:
              break
          }
        }
      }
    },
    onCalendarUpdate (message) {
      clearTimeout(this.$timer)
      try {
        this.onUpdate(JSON.parse(message).eventDetail || [])
      } catch {
        this.getTimeline()
      }
    },
    onUpdate (events) {
      if (this.$timer === -1) {
        return
      }
      if (events.length) {
        const now = Date.now()
        this.timeline = events.filter(({ until }) => !until || now < new Date(until)).sort((a, b) => {
          if (b.priority === a.priority) {
            return toDate(a.start) - toDate(b.start)
          }
          return b.priority - a.priority
        })
        this.checkTimeline()
      } else {
        clearTimeout(this.$timer)
        this.timeline = null
        this.current = null
        this.next = null
        this.loadingTimeline = false
      }
    },
    getTimeline () {
      this.loadingTimeline = true
      getTimeline(this.device.id, { custom: true }).then(this.onUpdate).catch(({ isCancel }) => {
        if (!isCancel && this.$timer !== -1) {
          this.$timer = setTimeout(this.getTimeline, 2000)
        }
      })
    },
    checkTimeline (target) {
      this.loadingTimeline = false
      const now = new Date()
      let current = target
      let currentEndDate = null
      let next = null
      if (!current) {
        for (let i = 0; i < this.timeline.length; i++) {
          const event = this.timeline[i]
          if (!current && isHit(event, now)) {
            current = event
            break
          }
        }
      }
      currentEndDate = current && getFinishDate(current, now)
      let nextStartDate = null
      for (let i = 0; i < this.timeline.length; i++) {
        const event = this.timeline[i]
        if (!current || currentEndDate || event.priority > current.priority) {
          const rangeStart = !current || event.priority > current.priority ? now : currentEndDate || now
          if (!nextStartDate || rangeStart < nextStartDate) {
            const hit = getNearestHitDate(event, rangeStart, nextStartDate)
            if (hit && (!next || hit < nextStartDate)) {
              next = event
              nextStartDate = hit
            }
          }
        }
      }
      console.log('current', current, currentEndDate)
      console.log('next', next, nextStartDate)
      if (nextStartDate && (!currentEndDate || currentEndDate > nextStartDate)) {
        currentEndDate = nextStartDate
      }
      this.current = current && this.getEventInfo(current, getStartDate(current, now), currentEndDate)
      this.next = next && this.getEventInfo(next, nextStartDate)

      clearTimeout(this.$timer)
      if (currentEndDate) {
        const delay = currentEndDate - now
        // delay有最大限制2^31-1，超过后会直接执行
        // 当前限制最多1天
        if (delay > 86400000) {
          this.$timer = setTimeout(this.checkTimeline, 86400000, current)
        } else {
          this.$timer = setTimeout(this.checkTimeline, delay, next)
        }
      }
    },
    getEventInfo (event, startDate, endDate) {
      return {
        target: event.target,
        startDate: parseTime(startDate, '{y}.{m}.{d}'),
        startTime: parseTime(startDate, '{h}:{i}:{s}'),
        endDate: endDate ? parseTime(endDate, '{y}.{m}.{d}') : '',
        endTime: endDate ? parseTime(endDate, '{h}:{i}:{s}') : ''
      }
    }
  }
}
</script>

<style lang="scss" scoped>
.o-device {
  display: inline-flex;
  flex-direction: column;
  padding: 12px $spacing;
  color: $black;
  line-height: 1;
  border-radius: $radius;
  background-color: #fff;

  &__header {
    justify-self: flex-start;
    height: 24px;
    font-size: 16px;
    font-weight: bold;
  }

  &__shot {
    display: inline-block;
    width: 24px;
    height: 24px;
    background: url("~@/assets/icon_screenshot.png") 0 0 / 100% 100% no-repeat;
  }

  &__tip {
    display: inline-block;
    position: relative;
    left: 16px;
    padding: 4px 8px 4px 10px;
    margin-left: -4px;
    font-size: 12px;
    line-height: 1;
    border-radius: 4px 0 0 4px;
    background-color: currentColor;
  }

  &__preview {
    margin-top: 12px;
    padding-top: 56.25%;
    border-radius: 4px;

    &.empty {
      background: url("~@/assets/image_no_program.svg") 0 0 / 100% 100%
        no-repeat;
    }

    &.offline {
      background: url("~@/assets/image_offline.svg") 0 0 / 100% 100% no-repeat;
    }
  }

  &__info {
    margin-top: 12px;
    height: 88px;
  }

  &__current {
    align-self: stretch;
    font-size: 20px;
    font-weight: bold;
    text-align: center;
  }

  &__time {
    margin: $spacing 0 32px;
    font-size: 20px;
  }

  &__line {
    display: inline-block;
    width: 20px;
    margin: 0 10px;
    border-bottom: 1px solid currentColor;
  }

  &__hms {
    position: relative;
    font-weight: bold;
  }

  &__ymd {
    position: absolute;
    top: 100%;
    left: 50%;
    color: $info;
    font-size: 12px;
    font-weight: normal;
    transform: translate(-50%, 4px);
  }

  &__next {
    align-self: stretch;
    height: 16px;
    color: $info;
    font-size: 12px;
    line-height: 16px;
    text-align: center;
  }

  &__footer {
    margin-top: 10px;
    color: $blue;
    font-size: 12px;
    font-weight: bold;

    .o-device__icon {
      margin-right: 6px;
      font-size: 16px;
    }
  }
}
</style>
