<template>
  <wrapper
    fill
    margin
    horizontal
  >
    <div class="l-flex__fill l-flex--col c-sibling-item c-device-map">
      <div class="l-flex__none l-flex--row c-count">
        <div
          class="l-flex__none c-count__title u-color--black u-bold has-active u-ellipsis"
          @click="onChooseDepartment"
        >
          <i
            v-if="loading"
            class="el-icon-loading"
          />
          {{ group.name }}
        </div>
        <div class="l-flex__none c-count__item u-color--black u-bold u-text--center">
          <div>总数</div>
          <i
            v-if="monitor.loading"
            class="el-icon-loading"
          />
          <div v-else>{{ monitor.total }}</div>
        </div>
        <div class="l-flex__none c-count__item u-color--success dark u-bold u-text--center">
          <div>● 在线</div>
          <i
            v-if="monitor.loading"
            class="el-icon-loading"
          />
          <div v-else>{{ monitor.online }}</div>
        </div>
        <div class="l-flex__none c-count__item u-color--error dark u-bold u-text--center">
          <div>● 离线</div>
          <i
            v-if="monitor.loading"
            class="el-icon-loading"
          />
          <div v-else>{{ monitor.offline }}</div>
        </div>
        <div class="l-flex__none c-count__item u-color--info u-bold u-text--center">
          <div>● 未启用</div>
          <i
            v-if="monitor.loading"
            class="el-icon-loading"
          />
          <div v-else>{{ monitor.inactive }}</div>
        </div>
        <i
          class="el-icon-refresh o-icon md u-color--blue has-active"
          @click="onRefresh"
        />
      </div>
      <div class="l-flex__fill l-flex u-relative">
        <div
          ref="map"
          class="l-flex__fill"
        />
        <i
          v-if="deviceOptions.loaded"
          class="o-place el-icon-place has-active"
          @click="onPlace"
        />
        <device
          v-if="device"
          :key="device.id"
          class="c-device-map__device"
          :device="device"
          :status="device.status"
          always
        />
      </div>
    </div>
    <!-- <div
      ref="devicelist"
      v-loading="!deviceOptions.loaded"
      class="l-flex__none l-flex--col c-sibling-item u-width--lg u-overflow-y--auto"
    >
      <device
        v-for="item in deviceOptions.list"
        :key="item.id"
        class="c-sibling-item--v"
        :device="item"
      />
    </div> -->
    <department-drawer
      ref="departmentDrawer"
      @change="onGroupChanged"
      @loaded="onGroupLoaded"
    />
  </wrapper>
</template>

<script>
import AMapLoader from '@amap/amap-jsapi-loader'
import {
  subscribe,
  unsubscribe
} from '@/utils/mqtt'
import { ScreenshotCache } from '@/utils/cache'
import {
  GET_POWER_STATUS,
  getPowerStatusByMessage
} from '@/utils/adapter/nova'
import {
  getDevicesByQuery,
  getDeviceStatisticsByPath,
  getStatusReport
} from '@/api/device'
import Device from '../components/Device.vue'

const onlineIcon = require('./assets/icon_position1.svg')
const offlineIcon = require('./assets/icon_position2.svg')

export default {
  name: 'DeviceMap',
  components: {
    Device
  },
  data () {
    return {
      monitor: { loading: true },
      deviceOptions: {
        list: [],
        loaded: false
      },
      loading: true,
      group: {},
      device: null
    }
  },
  computed: {
    mapDevices () {
      return this.deviceOptions.list.filter(i => i.longitude && i.latitude)
    }
  },
  created () {
    this.$timer = -1
    subscribe([
      '+/+/online',
      '+/+/offline',
      '+/+/calendar/update',
      '+/+/multifunctionCard/invoke/reply'
    ], this.onMessage)
  },
  beforeDestroy () {
    ScreenshotCache.clear()
    clearTimeout(this.$timer)
    this.monitor = { loading: true }
    unsubscribe([
      '+/+/online',
      '+/+/offline',
      '+/+/calendar/update',
      '+/+/multifunctionCard/invoke/reply'
    ], this.onMessage)
    this.map?.destroy()
  },
  methods: {
    getStatusReport (options) {
      if (!options.power.length) {
        return
      }
      getStatusReport(options.power).then(
        ({ data }) => {
          const powerMap = {}
          data.forEach(item => {
            powerMap[item.deviceId] = item.switchStatus
          })
          options.power.forEach(id => {
            if (options.map[id].status === -2) {
              options.map[id].status = powerMap[id] ?? -1
            }
          })
        },
        ({ isCancel }) => {
          if (!isCancel && !this.monitor.loading) {
            setTimeout(() => {
              this.getStatusReport(options)
            }, 1000)
          }
        }
      )
    },
    onMessage (topic, message) {
      if (!this.deviceOptions.loaded) {
        return
      }
      const result = /^\d+\/(\d+)\/(online|offline)|calendar\/update|multifunctionCard\/invoke\/reply$/.exec(topic)
      if (!result) {
        return
      }
      const deviceId = result[1]
      const status = result[2]
      const device = this.deviceOptions.map?.[deviceId]
      if (device) {
        switch (status) {
          case 'calendar/update':
            device.flag = -Date.now()
            break
          case 'multifunctionCard/invoke/reply':
            message = message && JSON.parse(message)
            switch (message.function) {
              case GET_POWER_STATUS:
                device.status = this.getPowerStatus(message)
                break
              default:
                break
            }
            break
          default:
            if (status === (device.onlineStatus === 1 ? 'online' : 'offline')) {
              return
            }
            this.refreshDevices()
            break
        }
      }
    },
    getPowerStatus (message) {
      const { success, data } = getPowerStatusByMessage(message)
      if (success && data.length) {
        return data[0].switchStatus
      }
      return -1
    },
    onGroupLoaded () {
      this.loading = false
    },
    onGroupChanged ({ path, name }) {
      if (!this.group || this.group.path !== path) {
        this.group = { path, name }
        this.refreshDevices(true)
      }
    },
    onChooseDepartment () {
      this.$refs.departmentDrawer.show().then(visible => {
        this.loading = !visible
      })
    },
    onRefresh () {
      this.refreshDevices()
    },
    refreshDevices (force) {
      if (!force && this.monitor.loading) {
        return
      }
      const monitor = {
        loading: true,
        total: '-',
        online: '-',
        offline: '-',
        inactive: '-'
      }
      this.device = null
      this.map?.destroy()
      this.map = null
      this.monitor = monitor
      clearTimeout(this.$timer)
      this.deviceOptions = { loaded: false }
      getDeviceStatisticsByPath(this.group.path).then(({ data }) => {
        const { deactivatedTotal, notConnectedTotal, offLineTotal, onLineTotal, total } = data
        monitor.total = total
        monitor.online = onLineTotal
        monitor.offline = offLineTotal + notConnectedTotal
        monitor.inactive = deactivatedTotal
      }).finally(() => {
        monitor.loading = false
        if (!this.monitor.loading) {
          this.getDevices(this.monitor.total - this.monitor.inactive)
        }
      })
    },
    sort (a, b) {
      if (a.onlineStatus === b.onlineStatus) {
        return a.createTime <= b.createTime ? 1 : -1
      }
      return a.onlineStatus === 1 ? -1 : 1
    },
    getDevices (total) {
      if (!total || total === '-') {
        this.deviceOptions = { list: [], loaded: true }
        return
      }
      const options = { list: [], loaded: false }
      this.deviceOptions = options
      getDevicesByQuery({
        pageNum: 1,
        pageSize: total,
        activate: 1,
        org: this.group.path
      }, { custom: true }).then(
        ({ data }) => {
          const map = {}
          const ids = []
          options.list = data.sort(this.sort).map(device => {
            if (device.onlineStatus === 1) {
              device.status = -2
              ids.push(device.id)
            } else {
              device.status = -1
            }
            device.flag = 0
            map[device.id] = device
            return device
          })
          options.power = ids
          options.map = map
          options.loaded = true
          this.getStatusReport(options)
          this.initMap()
        },
        ({ isCancel }) => {
          if (!isCancel && !this.monitor.loading) {
            this.$timer = setTimeout(total => {
              this.getDevices(total)
            }, 2000, total)
          }
        }
      )
    },
    onDeviceChange (device) {
      if (!device) {
        this.map.setFitView()
        return
      }
      this.activeDevice(device.id, [device.marker])
    },
    initMap () {
      AMapLoader.load({
        key: process.env.VUE_APP_GAODE_MAP_KEY,
        version: '2.0',
        plugins: ['']
      }).then(AMap => {
        this.map = new AMap.Map(this.$refs.map)
        const markerList = []
        for (const device of this.mapDevices) {
          const marker = new AMap.Marker({
            position: [+device.longitude, +device.latitude],
            icon: new AMap.Icon({
              image: device.onlineStatus === 1 ? onlineIcon : offlineIcon,
              size: new AMap.Size(32, 32),
              imageSize: new AMap.Size(32, 32)
            }),
            label: {
              content: `<div class='o-online-status--${device.onlineStatus === 1 ? 'online' : 'offline'}'>${device.name}</div>`,
              direction: 'top',
              offset: new AMap.Pixel(0, -10) // 设置文本标注偏移量
            },
            extData: {
              id: device.id
            }
          })
          marker.on('click', e => {
            this.activeDevice(e.target.getExtData().id, e.target)
          })
          device.marker = marker
          markerList.unshift(marker)
        }
        this.map.add(markerList)
        this.map.setFitView()
        this.map.on('click', () => {
          this.device = null
        })
      })
    },
    onPlace () {
      this.map?.setFitView()
    },
    activeDevice (id, marker) {
      this.map.setFitView(marker)
      this.device = this.mapDevices.find(device => device.id === id)
    }
  }
}
</script>

<style lang="scss" scoped>
@mixin markLabel {
  color: #ffffff;
  border: none;
  padding: 5px 10px;
}

.c-device-map {
  border-radius: $radius $radius 0 0;
  background-color: #fff;

  &__device {
    position: absolute;
    top: $spacing;
    left: $spacing;
    width: $width--lg;
    z-index: 9;
  }

  ::v-deep {
    .o-online-status {
      &--online {
        @include markLabel();
        background-color: #333333;
      }
      &--offline {
        @include markLabel();
        background-color: #f90c0c;
      }
    }

    .amap-marker-label {
      border: none;
      padding: 0;
    }
  }
}

.c-count {
  justify-content: space-between;
  padding: $spacing--xs $spacing;

  &__title {
    width: 200px;
  }

  &__item > div:first-child {
    margin-bottom: 10px;
  }
}

.o-place {
  display: inline-flex;
  justify-content: center;
  align-items: center;
  position: absolute;
  right: $spacing;
  bottom: $spacing;
  width: 48px;
  height: 48px;
  font-size: 24px;
  border-radius: 50%;
  background-color: #fff;
  z-index: 9;
}
</style>
