<template>
  <div
    ref="wrapper"
    v-loading="!loaded"
    element-loading-background="transparent"
  >
    <template v-if="loaded">
      <div
        v-if="linkDeviceMap"
        class="c-link-state l-flex--row"
        :class="className"
      >
        <div
          class="c-link-state__main"
          :style="styles"
        >
          <div
            v-for="item in items"
            :key="item.key"
            class="o-link-item"
            :class="item.className"
            @click="onClick(item)"
          >
            <div
              v-if="item.status !== -2"
              class="o-link-item__name"
            >
              {{ item.label }}
            </div>
            <template v-if="item.key === 'led'">
              <device-player
                :device="led"
                autoplay
                retry
                keep
              />
              <i
                v-if="led.powerStatus"
                class="o-link-item__status"
                :class="led.powerStatus"
              />
            </template>
          </div>
          <div
            v-for="line in lines"
            :key="line.key"
            class="o-line"
            :class="line.className"
          />
        </div>
      </div>
      <div
        v-else
        class="l-flex--row center"
      >
        <el-empty description="暂无相关配置" />
      </div>
    </template>
    <slot />
  </div>
</template>

<script>
import { ThirdPartyDevice } from '@/constant.js'
import {
  Status,
  Power,
  getReceivingCardStatusFromServer
} from '@/utils/adapter'
import { getThirdPartyDevicesByThirdPartyDevice } from '@/api/mesh.js'
import { getDevice } from '@/api/device.js'

const LinkItems = Object.freeze([
  {
    key: 'msr',
    label: '浪潮屏媒安播云平台'
  },
  {
    key: ThirdPartyDevice.BOX,
    alias: 'device',
    label: '浪潮超高清媒体播控器',
    info: 'boxInfo'
  },
  {
    key: 'led',
    label: 'LED大屏'
  },
  {
    key: ThirdPartyDevice.GATEWAY,
    alias: 'gateway',
    label: '浪潮物联网关',
    canClick: true
  },
  {
    key: ThirdPartyDevice.LED_CAMERA,
    alias: 'led_camera',
    label: 'LED屏监测摄像头',
    canClick: true
  },
  {
    key: ThirdPartyDevice.TRAFFIC_CAMERA,
    alias: 'traffic_camera',
    label: '人流量监测摄像头',
    canClick: true
  },
  {
    key: ThirdPartyDevice.SENDING_CARD,
    alias: 'sending_card',
    label: '发送控制设备',
    canClick: true
  },
  {
    key: ThirdPartyDevice.RECEIVING_CARD,
    alias: 'receiving_card',
    label: '接收卡',
    canClick: true
  }
])

const requiredMap = {
  [ThirdPartyDevice.BOX]: 1,
  [ThirdPartyDevice.GATEWAY]: 1,
  [ThirdPartyDevice.LED_CAMERA]: 1,
  [ThirdPartyDevice.TRAFFIC_CAMERA]: 1
}

const LineFromeTo = {
  1: ['msr', { key: ThirdPartyDevice.BOX, enable: 1 }],
  2: [ThirdPartyDevice.BOX, { key: ThirdPartyDevice.SENDING_CARD, enable: 1 }],
  3: [ThirdPartyDevice.SENDING_CARD, { key: ThirdPartyDevice.RECEIVING_CARD, enable: 1 }],
  4: [ThirdPartyDevice.RECEIVING_CARD, { key: 'led', enable: 1 }],
  5: ['msr', { key: ThirdPartyDevice.GATEWAY, enable: 1 }],
  6: ['msr', { key: ThirdPartyDevice.LED_CAMERA, enable: 1 }],
  15: ['msr', { key: ThirdPartyDevice.TRAFFIC_CAMERA, enable: 1 }],
  7: ['msr', { key: ThirdPartyDevice.TRAFFIC_CAMERA, enable: 1 }],
  8: ['msr', { key: ThirdPartyDevice.TRAFFIC_CAMERA, enable: 1 }],
  9: ['msr', { key: ThirdPartyDevice.LED_CAMERA, enable: 1 }],
  // 10: [ThirdPartyDevice.GATEWAY, { key: 'led', enable: 1 }],
  11: ['msr', { key: ThirdPartyDevice.GATEWAY, enable: 1 }, { key: ThirdPartyDevice.LED_CAMERA, enable: 1 }, { key: ThirdPartyDevice.TRAFFIC_CAMERA, enable: 1 }],
  12: [ThirdPartyDevice.BOX, { key: ThirdPartyDevice.SENDING_CARD, enable: 0 }, { key: ThirdPartyDevice.RECEIVING_CARD, enable: 1 }],
  13: [ThirdPartyDevice.BOX, { key: ThirdPartyDevice.SENDING_CARD, enable: 0 }, { key: ThirdPartyDevice.RECEIVING_CARD, enable: 0 }, { key: 'led', enable: 1 }],
  14: [ThirdPartyDevice.BOX, { key: ThirdPartyDevice.SENDING_CARD, enable: 0 }, { key: ThirdPartyDevice.RECEIVING_CARD, enable: 0 }, { key: 'led', enable: 1 }]
}

export default {
  name: 'FullLink',
  props: {
    device: {
      type: Object,
      required: true
    },
    theme: {
      type: String,
      default: 'dark'
    },
    square: {
      type: [Boolean, String],
      default: false
    },
    addListener: {
      type: Function,
      default: null
    },
    removeListener: {
      type: Function,
      default: null
    }
  },
  data () {
    return {
      linkDeviceMap: null,
      scale: 1,
      loading: false,
      loaded: false,
      led: null
    }
  },
  computed: {
    targetId () {
      return this.device.id
    },
    className () {
      return [this.theme, this.square ? 'square' : ''].join(' ')
    },
    linkState () {
      return { ...this.linkDeviceMap }
    },
    items () {
      const map = this.linkState
      return LinkItems.map(({ key, alias, label, info, canClick }) => {
        const status = map[key]
        return {
          key, status, canClick,
          label: info && map[info] || label,
          className: [
            status === Status.NONE
              ? 'u-hidden'
              : status === Status.LOADING
                ? 'loading'
                : status === Status.ERROR
                  ? 'tip el-icon-error u-color--error'
                  : status === Status.OK
                    ? ''
                    : 'tip el-icon-warning u-color--warning',
            alias || key,
            canClick && status !== Status.NONE ? 'u-pointer' : ''
          ].join(' ')
        }
      })
    },
    lines () {
      const state = this.linkState
      return Object.keys(LineFromeTo).map(key => {
        const from = LineFromeTo[key][0]
        const to = LineFromeTo[key].slice(1)
        const enable = state[from] > Status.NONE && !to.some(
          ({ key, enable }) => enable && state[key] === Status.NONE
        ) && !to.some(
          ({ key, enable }) => !enable && state[key] > Status.NONE
        )
        return {
          key: `line${key}`,
          className: [
            `l${key}`,
            enable
              ? this.hasLink(state[from]) && !to.some(({ key, enable }) => enable && !this.hasLink(state[key]))
                ? 'linked'
                : ''
              : 'u-hidden'
          ].join(' ')
        }
      })
    },
    styles () {
      return {
        transform: `scale(${this.scale})`
      }
    }
  },
  created () {
    if (this.addListener) {
      this.addListener(this.onMessage)
    }
    this.getThirdPartyDevicesByThirdPartyDevice()
    this.$timer = setInterval(this.getThirdPartyDevicesByThirdPartyDevice, 5000)
  },
  mounted () {
    this.checkScale()
    window.addEventListener('resize', this.checkScale)
  },
  beforeDestroy () {
    if (this.removeListener) {
      this.removeListener(this.onMessage)
    }
    window.removeEventListener('resize', this.checkScale)
    clearInterval(this.$timer)
  },
  methods: {
    hasLink (status) {
      return status > Status.LOADING && status !== Status.ERROR
    },
    onMessage (value, key) {
      console.log('full link', key, value)
      if (!key || key === ThirdPartyDevice.MULTI_FUNCTION_CARD || key === ThirdPartyDevice.RECEIVING_CARD) {
        this.$receiverStatus = value[ThirdPartyDevice.RECEIVING_CARD].status
        if (value[ThirdPartyDevice.MULTI_FUNCTION_CARD].status > Status.LOADING) {
          this.$multiCard = value[ThirdPartyDevice.MULTI_FUNCTION_CARD]
        }
        if (this.linkDeviceMap) {
          if (this.linkDeviceMap[ThirdPartyDevice.MULTI_FUNCTION_CARD] > Status.NONE && this.$multiCard) {
            const multiCardStatus = this.$multiCard.status
            if (multiCardStatus === Status.OK) {
              const switchStatus = this.$multiCard.switchStatus
              this.linkDeviceMap[ThirdPartyDevice.MULTI_FUNCTION_CARD] = switchStatus === Power.ON ? Status.OK : switchStatus === Power.OFF ? Status.ERROR : Status.WARNING
            } else {
              this.linkDeviceMap[ThirdPartyDevice.MULTI_FUNCTION_CARD] = multiCardStatus
            }
          }
          if (this.linkDeviceMap[ThirdPartyDevice.RECEIVING_CARD] > Status.NONE && this.$receiverStatus >= Status.OK) {
            this.linkDeviceMap[ThirdPartyDevice.RECEIVING_CARD] = this.$receiverStatus
          }
          this.onUpdateLed()
        }
      }
    },
    checkScale () {
      const elm = this.$refs.wrapper
      const width = elm.clientWidth
      const height = elm.clientHeight
      if (width === 0) {
        this.scale = this.square
          ? (height / 640).toFixed(2)
          : (height / 420).toFixed(2)
      } else if (height === 0) {
        this.scale = this.square
          ? (width / 804).toFixed(2)
          : (width / 966).toFixed(2)
      } else {
        this.scale = this.square
          ? Math.min(width / 804, height / 640).toFixed(2)
          : Math.min(width / 966, height / 420).toFixed(2)
      }
    },
    onOnlyBox () {
      this.linkDeviceMap = {
        msr: Status.OK,
        led: Status.ERROR,
        [ThirdPartyDevice.BOX]: Status.ERROR,
        [ThirdPartyDevice.GATEWAY]: Status.NONE,
        [ThirdPartyDevice.LED_CAMERA]: Status.NONE,
        [ThirdPartyDevice.TRAFFIC_CAMERA]: Status.NONE,
        [ThirdPartyDevice.SENDING_CARD]: Status.NONE,
        [ThirdPartyDevice.RECEIVING_CARD]: Status.NONE,
        [ThirdPartyDevice.MULTI_FUNCTION_CARD]: Status.NONE
      }
      this.led = {
        id: this.targetId,
        onlineStatus: 0,
        powerStatus: '',
        by: [ThirdPartyDevice.BOX]
      }
      return this.updateBoxStatus().then(() => {
        clearInterval(this.$timer)
        this.$timer = setInterval(this.updateBoxStatus, 5000)
        this.loaded = true
      })
    },
    updateBoxStatus () {
      return getDevice(this.targetId, { custom: true }).then(({ data }) => {
        this.linkDeviceMap[ThirdPartyDevice.BOX] = data.onlineStatus === 1 ? Status.OK : Status.ERROR
        this.onUpdateLed()
      })
    },
    getThirdPartyDevices () {
      Promise.all([
        getThirdPartyDevicesByThirdPartyDevice(this.targetId, [
          ThirdPartyDevice.GATEWAY,
          ThirdPartyDevice.RECEIVING_CARD,
          ThirdPartyDevice.SENDING_CARD,
          ThirdPartyDevice.LED_CAMERA,
          ThirdPartyDevice.TRAFFIC_CAMERA,
          ThirdPartyDevice.BOX,
          ThirdPartyDevice.MULTI_FUNCTION_CARD
        ], { custom: true }),
        Promise.resolve(this.$multiCard)
      ]).then(([{ data: nodes }, multiCard]) => {
        if (!nodes.length) {
          return this.onOnlyBox()
        }
        this.$deviceTypes = []
        const linkDeviceMap = {}
        const by = [ThirdPartyDevice.BOX]
        nodes.forEach(({ nodeType, instance }) => {
          if (nodeType === ThirdPartyDevice.RECEIVING_CARD) {
            if (instance) {
              linkDeviceMap[nodeType] = this.$receiverStatus ?? Status.LOADING
              by.push(nodeType)
            }
          } else if (nodeType === ThirdPartyDevice.MULTI_FUNCTION_CARD) {
            if (instance) {
              if (multiCard) {
                const multiCardStatus = multiCard.status
                if (multiCardStatus === Status.OK) {
                  const switchStatus = multiCard.switchStatus
                  linkDeviceMap[ThirdPartyDevice.MULTI_FUNCTION_CARD] = switchStatus === Power.ON ? Status.OK : switchStatus === Power.OFF ? Status.ERROR : Status.WARNING
                } else {
                  linkDeviceMap[ThirdPartyDevice.MULTI_FUNCTION_CARD] = multiCardStatus
                }
              } else {
                linkDeviceMap[ThirdPartyDevice.MULTI_FUNCTION_CARD] = Status.LOADING
              }
            }
          } else if (nodeType === ThirdPartyDevice.SENDING_CARD) {
            linkDeviceMap[nodeType] = Status.OK
          } else if (requiredMap[nodeType]) {
            if (instance) {
              const status = instance.onlineStatus === 1 ? Status.OK : Status.ERROR
              if (linkDeviceMap[nodeType] > Status.LOADING && linkDeviceMap[nodeType] !== status) {
                linkDeviceMap[nodeType] = Status.WARNING
              } else {
                linkDeviceMap[nodeType] = status
              }
              this.$deviceTypes.push(nodeType)
              if (nodeType === ThirdPartyDevice.BOX) {
                linkDeviceMap.boxInfo = instance.remark
              }
            }
          }
        })
        if (linkDeviceMap[ThirdPartyDevice.RECEIVING_CARD] > Status.NONE) {
          linkDeviceMap[ThirdPartyDevice.MULTI_FUNCTION_CARD] = Status.NONE
        }
        this.linkDeviceMap = {
          msr: Status.OK,
          led: Status.ERROR,
          [ThirdPartyDevice.BOX]: Status.NONE,
          [ThirdPartyDevice.GATEWAY]: Status.NONE,
          [ThirdPartyDevice.LED_CAMERA]: Status.NONE,
          [ThirdPartyDevice.TRAFFIC_CAMERA]: Status.NONE,
          [ThirdPartyDevice.SENDING_CARD]: Status.NONE,
          [ThirdPartyDevice.RECEIVING_CARD]: Status.NONE,
          [ThirdPartyDevice.MULTI_FUNCTION_CARD]: Status.NONE,
          ...linkDeviceMap
        }
        this.led = {
          id: this.targetId,
          onlineStatus: 0,
          powerStatus: '',
          by
        }
        this.onUpdateLed()
        if (this.linkDeviceMap[ThirdPartyDevice.RECEIVING_CARD] > Status.NONE) {
          getReceivingCardStatusFromServer(this.device.id)
        }
        this.loaded = true
        return true
      }).finally(() => {
        this.loading = false
      })
    },
    getThirdPartyDevicesByThirdPartyDevice () {
      if (this.loading) {
        return
      }
      this.loading = true
      if (!this.linkDeviceMap) {
        this.getThirdPartyDevices()
        return
      }
      getThirdPartyDevicesByThirdPartyDevice(this.targetId, this.$deviceTypes, { custom: true }).then(({ data: nodes }) => {
        const linkDeviceMap = {}
        nodes.forEach(({ nodeType, instance }) => {
          const onlineStatus = instance.onlineStatus === 1 ? Status.OK : Status.ERROR
          if (linkDeviceMap[nodeType] > Status.LOADING && linkDeviceMap[nodeType] !== onlineStatus) {
            linkDeviceMap[nodeType] = Status.WARNING
          } else {
            linkDeviceMap[nodeType] = onlineStatus
          }
        })
        this.linkDeviceMap = {
          ...this.linkDeviceMap,
          ...linkDeviceMap
        }
        this.onUpdateLed()
      }).finally(() => {
        this.loading = false
      })
    },
    onUpdateLed () {
      const linkDeviceMap = this.linkDeviceMap
      const by = this.led.by
      const mutliStatus = this.linkDeviceMap[ThirdPartyDevice.MULTI_FUNCTION_CARD]
      const linkError = by.some(key => linkDeviceMap[key] === Status.ERROR)
      const isOK = (mutliStatus === Status.NONE || mutliStatus === Status.OK) && !by.some(key => linkDeviceMap[key] !== Status.OK)
      this.linkDeviceMap.led = linkError ? Status.ERROR : isOK ? Status.OK : Status.WARNING
      this.led.onlineStatus = isOK ? 1 : 0
      this.led.powerStatus = mutliStatus === Status.LOADING
        ? 'el-icon-loading'
        : mutliStatus === Status.ERROR
          ? 'off'
          : mutliStatus === Status.WARNING
            ? 'power el-icon-warning u-color--warning'
            : ''
    },
    onClick (item) {
      if (!item.canClick) {
        return
      }
      this.$emit('click', item)
    }
  }
}
</script>

<style lang="scss" scoped>
@mixin getPosition($left, $top, $width, $height) {
  top: $top;
  left: $left;
  width: $width;
  height: $height;
}

.c-link-state {
  width: 100%;
  height: 100%;
  overflow: hidden;

  &__main {
    position: relative;
    width: 966px;
    height: 420px;
    transform-origin: left center;
  }

  &.dark {
    color: $black;
  }

  &.light {
    color: #fff;
  }

  &.square {
    .c-link-state__main {
      width: 804px;
      height: 640px;
    }

    .o-link-item {
      &.msr {
        top: 0;
        left: 0;
      }

      &.device {
        top: 75px;
        left: 286px;
      }

      &.sending_card {
        top: 75px;
        left: 472px;
      }

      &.receiving_card {
        top: 75px;
        left: 660px;
      }

      &.gateway {
        top: 432px;
        left: 52px;
        width: 138px;
        height: 138px;
      }

      &.led_camera {
        top: 280px;
        left: 460px;
      }

      &.traffic_camera {
        top: 400px;
        left: 290px;
      }

      &.led {
        top: 400px;
        left: 440px;
      }
    }

    .o-line {
      &.l1 {
        top: 166px;
        left: 232px;
        width: 60px;
        transform: none;
      }

      &.l2 {
        top: 166px;
        left: 418px;
        width: 60px;
      }

      &.l3 {
        top: 166px;
        left: 604px;
        width: 60px;
      }

      &.l4 {
        top: 202px;
        left: 728px;
        width: 198px;
      }

      &.l5 {
        top: 334px;
        left: 122px;
        width: 108px;
        transform: rotate(90deg);
      }

      &.l6,
      &.l15 {
        top: 334px;
        left: 122px;
        width: 120px;
      }

      &.l7 {
        top: 334px;
        left: 240px;
        width: 120px;
      }

      &.l8 {
        top: 454px;
        left: 240px;
      }

      &.l9 {
        top: 334px;
        left: 240px;
        width: 234px;
      }

      &.l10 {
        top: 522px;
        left: 184px;
        width: 256px;
      }

      &.l11 {
        top: 230px;
        left: 122px;
        width: 104px;
        height: 2px;
        transform: rotate(90deg);
        transform-origin: left;
      }
    }
  }
}

@keyframes move {
  from {
    background-position: 0 0;
  }
  to {
    background-position: 3em 0;
  }
}

.o-link-item {
  position: absolute;
  text-align: center;
  background-position: 0 0;
  background-size: 100% 100%;
  background-repeat: no-repeat;
  z-index: 1;

  &.el-icon-error::before {
    position: absolute;
    top: 50%;
    left: 50%;
    color: #ff0000;
    transform: translate(-50%, 50%);
  }

  &__name {
    position: absolute;
    top: 100%;
    left: 50%;
    font-size: 14px;
    line-height: 1;
    white-space: nowrap;
    transform: translateX(-50%);
  }

  &.msr {
    @include getPosition(0, 42px, 242px, 236px);
    background-image: url("~@/assets/link/msr.svg");
  }

  &.device {
    @include getPosition(270px, 0, 138px, 138px);
    background-image: url("~@/assets/link/box_u8h.png");
  }

  &.sending_card {
    @include getPosition(484px, 0, 138px, 138px);
    background-image: url("~@/assets/link/sending_card.png");
  }

  &.receiving_card {
    @include getPosition(720px, 0, 138px, 138px);
    background-image: url("~@/assets/link/receiving_card.png");

    .o-link-item__name {
      transform: translateX(8px);
    }
  }

  &.gateway {
    @include getPosition(260px, 270px, 138px, 138px);
    background-image: url("~@/assets/link/gateway.svg");
  }

  &.led_camera {
    @include getPosition(480px, 178px, 80px, 80px);
    background-image: url("~@/assets/link/camera.png");
  }

  &.traffic_camera {
    @include getPosition(480px, 278px, 80px, 80px);
    background-image: url("~@/assets/link/camera.png");
  }

  &.led {
    @include getPosition(604px, 208px, 352px, 198px);

    .o-link-item__name {
      top: -8px;
      left: auto;
      right: 0;
      bottom: auto;
      transform: translateY(-100%);
    }
  }

  &.loading::after {
    content: "\e6cf";
    position: absolute;
    top: calc(50% - $font-size--3xl / 2);
    left: calc(50% - $font-size--3xl / 2);
    width: $font-size--3xl;
    height: $font-size--3xl;
    color: #fff;
    font-size: $font-size--3xl;
    font-family: element-icons !important;
    animation: rotating 2s linear infinite;
  }

  &.tip::before {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 9;
  }

  &.led {
    &.tip::before {
      display: none;
    }

    &.loading::after {
      color: #606266;
    }
  }

  &__status {
    position: absolute;
    top: 6px;
    right: 8px;
    width: 24px;
    height: 24px;
    z-index: 9;

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

    &.power {
      display: inline-flex;
      justify-content: flex-end;
      align-items: center;
      font-size: $font-size;

      &::after {
        content: "电源";
        margin-left: $spacing--3xs;
        font-size: $font-size--sm;
        white-space: nowrap;
      }
    }
  }
}

.o-line {
  position: absolute;
  background-color: $gray--light;

  &.linked {
    background-size: 3em 1em;
    background-image: linear-gradient(
      -90deg,
      transparent 0em,
      transparent 1em,
      #026af2 1em,
      #026af2 2em,
      transparent 3em
    );
    animation: move 0.6s linear infinite;
  }

  &.l1 {
    transform: skewY(-30deg);
    transform-origin: left;
    @include getPosition(166px, 170px, 154px, 2px);
  }

  &.l2 {
    @include getPosition(402px, 70px, 86px, 2px);
  }

  &.l3 {
    @include getPosition(622px, 70px, 118px, 2px);
  }

  &.l4 {
    @include getPosition(790px, 100px, 110px, 2px);
    transform: rotate(90deg);
    transform-origin: left;
  }

  &.l5 {
    @include getPosition(152px, 256px, 150px, 2px);
    transform: skewY(30deg);
    transform-origin: left;
  }

  &.l6,
  &.l15 {
    @include getPosition(230px, 208px, 200px, 2px);
  }

  &.l6.linked ~ &.l15 {
    display: none;
  }

  &.l7 {
    @include getPosition(428px, 208px, 96px, 2px);
    transform: rotate(90deg);
    transform-origin: left;
  }

  &.l8 {
    @include getPosition(428px, 304px, 66px, 2px);
  }

  &.l9 {
    @include getPosition(428px, 208px, 66px, 2px);
  }

  &.l10 {
    @include getPosition(368px, 360px, 238px, 2px);
  }

  &.l11 {
    @include getPosition(402px, 70px, 86px, 2px);
  }

  &.l12 {
    @include getPosition(402px, 70px, 340px, 2px);
  }

  &.l13 {
    @include getPosition(402px, 70px, 386px, 2px);
  }

  &.l14 {
    transform: rotate(90deg);
    transform-origin: left;
    @include getPosition(788px, 68px, 160px, 2px);
  }
}
</style>
