<template>
  <div
    class="o-device has-padding u-pointer"
    @click="onClick"
  >
    <div class="l-flex__none l-flex--row o-device__block o-device__header">
      <i
        class="l-flex__none o-device__status"
        :class="statusClass"
      />
      <auto-text
        class="l-flex__fill"
        :text="name"
      />
      <template v-if="isActivated && 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
      v-if="shot"
      class="l-flex__fill o-device__block o-device__preview"
      :style="styles"
    />
    <div
      v-if="isActivated"
      class="l-flex__auto l-flex--col center o-device__block o-device__info"
    >
      <i
        v-if="loadingTimeline"
        class="l-flex__none el-icon-loading"
      />
      <template v-else>
        <template v-if="current">
          <auto-text
            class="l-flex__none o-device__current"
            :text="current.name"
          />
          <div class="l-flex__auto l-flex--row o-device__time">
            <span class="o-device__hms">
              {{ current.startTime }}
              <span class="o-device__ymd">{{ current.startDate }}</span>
            </span>
            <span class="o-device__line" />
            <span class="o-device__hms">
              {{ current.endTime }}
              <span class="o-device__ymd">{{ current.endDate }}</span>
            </span>
          </div>
        </template>
        <span
          v-else
          class="o-device__current"
        >
          当前暂无节目
        </span>
        <auto-text
          v-if="next"
          class="l-flex__none o-device__next"
          :text="nextInfo"
        />
      </template>
    </div>
    <auto-text
      class="l-flex__none o-device__block o-device__footer"
      :text="address"
    />
  </div>
</template>

<script>
import { getTimeline } from '@/api/calendar'
import { parseTime } from '@/utils'
import {
  listen,
  unlisten
} from '@/utils/mqtt'
import { getName } from '@/utils/cache'
import {
  getAndCheck,
  screenshot,
  reset,
  stop
} from '@/utils/screenshot'

export default {
  name: 'DeviceCard',
  props: {
    device: {
      type: Object,
      default: null
    }
  },
  data () {
    return {
      isShotting: false,
      shot: null,
      timeline: [],
      loadingTimeline: false,
      current: null,
      next: null
    }
  },
  computed: {
    name () {
      return this.device.name
    },
    isActivated () {
      return this.device.activate === 2
    },
    isOnline () {
      return this.device.onlineStatus === 1
    },
    statusClass () {
      return this.isActivated
        ? this.isOnline
          ? 'u-color--success dark'
          : 'u-color--error light'
        : this.device.activate
          ? 'u-color--primary'
          : 'u-color--warning'
    },
    statusTip () {
      return this.isActivated
        ? this.isOnline
          ? '在线'
          : '离线'
        : this.device.activate
          ? '已激活'
          : '未激活'
    },
    address () {
      return `地址：${this.device.remark}`
    },
    styles () {
      return this.isActivated && this.isOnline && this.shot ? {
        backgroundImage: `url("${this.shot}")`
      } : null
    },
    nextInfo () {
      return this.next ? `下一场：${this.next.startDate} ${this.next.startTime} ${this.next.name}` : ''
    }
  },
  created () {
    if (this.isActivated) {
      listen(this.onMessage)
      this.getTimeline()
      if (this.isOnline) {
        getAndCheck(this.device, this.onScreenshotUpdate)
      } else {
        reset(this.device.id)
      }
    }
    this.$timer = -1
  },
  beforeDestroy () {
    if (this.isActivated) {
      unlisten(this.onMessage)
      if (this.isOnline) {
        stop(this.device.id)
      }
    }
    clearTimeout(this.$timer)
  },
  methods: {
    screenshot () {
      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 {
        message = JSON.parse(message)
        this.timeline = (message.eventDetail || []).map(this.createItem)
        this.checkTimeline()
      } catch {
        this.getTimeline()
      }
    },
    createItem ({ programCalendarId, type, startTimestamp, endTimestamp }) {
      const startDateTime = new Date(Number(startTimestamp))
      const endDateTime = endTimestamp ? new Date(Number(endTimestamp)) : null
      return {
        type, startDateTime, endDateTime,
        id: programCalendarId,
        name: null,
        startDate: parseTime(startDateTime, '{y}.{m}.{d}'),
        startTime: parseTime(startDateTime, '{h}:{i}:{s}'),
        endDate: endDateTime ? parseTime(endDateTime - 1000, '{y}.{m}.{d}') : '',
        endTime: endDateTime ? parseTime(endDateTime - 1000, '{h}:{i}:{s}') : ''
      }
    },
    getTimeline () {
      this.loadingTimeline = true
      getTimeline(this.device.id, { custom: true }).then(({ data }) => {
        this.timeline = (JSON.parse(data.eventDetail) || []).map(this.createItem)
        this.checkTimeline()
      }).catch(({ isCancel }) => {
        if (!isCancel) {
          this.$timer = setTimeout(this.getTimeline, 2000)
        }
      })
    },
    checkTimeline () {
      this.loadingTimeline = true
      const now = Date.now()
      const current = this.timeline.findIndex(({ startDateTime, endDateTime }) => {
        return now >= startDateTime && (!endDateTime || now <= endDateTime)
      })
      this.current = this.timeline[current]
      this.next = this.current && this.timeline[current + 1]
      this.next && this.getName(this.next).then(name => {
        this.next.name = name
      })
      this.getDetail()
    },
    finishTimeline () {
      this.loadingTimeline = false
      const time = this.current ? this.current.endDateTime : this.next ? this.next.startDateTime : null
      clearTimeout(this.$timer)
      if (time) {
        this.$timer = setTimeout(this.checkTimeline, time - Date.now())
      }
    },
    getName (item) {
      if (item.id) {
        if (item.name) {
          return Promise.resolve(item.name)
        }
        return getName(item.type, item.id)
      }
      return Promise.resolve('未知')
    },
    getDetail () {
      if (this.current) {
        this.getName(this.current).then(name => {
          this.current.name = name
          this.finishTimeline()
        }, ({ isCancel }) => {
          if (!isCancel) {
            if (this.current.count == null) {
              this.current.count = 1
            } else if (this.current.count < 3) {
              this.current.count += 1
            } else {
              this.current.name = '未知'
              this.finishTimeline()
              return
            }
            this.$timer = setTimeout(this.getDetail, 2000)
          }
        })
      } else {
        this.finishTimeline()
      }
    }
  }
}
</script>

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

  &__block + &__block {
    margin-top: $spacing;
  }

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

  &__status {
    display: inline-block;
    width: 12px;
    height: 12px;
    margin-right: 6px;
    border-radius: 50%;
    background-color: currentColor;
  }

  &__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: 2px 8px 2px 10px;
    margin-left: -10px;
    font-size: 12px;
    line-height: 1;
    border-radius: 9px 0 0 9px;
    background-color: currentColor;
  }

  &__preview {
    padding-top: 50%;
    background-position: center center;
    background-size: contain;
    background-repeat: no-repeat;
  }

  &__info {
    justify-content: center;
    height: 100px;
  }

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

  &__time {
    margin: $spacing 0 24px;
    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: $gray;
    font-size: 12px;
    font-weight: normal;
    transform: translate(-50%, 4px);
  }

  &__next {
    align-self: stretch;
    color: $gray;
    font-size: 12px;
    text-align: center;
  }

  &__current + &__next {
    margin-top: 24px;
  }

  &__footer {
    font-size: 12px;
    font-weight: bold;
  }
}

.o-shot {
  display: inline-block;
  width: 480px;
  height: 270px;
  object-fit: contain;
}
</style>
