<template>
  <a-modal
    :visible="show"
    :dialogStyle="{ top: '1vh' }"
    width="50vw"
    :footer="null"
    :zIndex="1050"
    @cancel="handleCancel"
    :maskClosable="false"
  >
    <div class="scanner-container" >
      <a-row>
        <a-space>
          <a-button type="primary" @click="capturePhoto">拍照</a-button>
          <a-button type="primary" @click="switchCamera('0')">文档</a-button>
          <a-button type="primary" @click="switchCamera('1')">人像</a-button>
          <a-button type="primary" @click="getVideoStatus">设备状态</a-button>
          <a-button type="primary" @click="closeVideo">关闭设备</a-button>
          <a-button type="primary" @click="openApp">开启程序</a-button>
          <a-button v-if="showid" type="primary" @click="startReadCard">读身份证</a-button>
          <a-button v-if="showid" @click="stopReadCard">停止读卡</a-button>
          <a-button type="primary" @click="rotateCamera">画面旋转</a-button>
        </a-space>
      </a-row>
      <a-row>
        <a-col :span="12">
          <div
            style="display: flex; justify-content: center; align-items: center; height: 300px;">
            <img
              v-if="previewImage"
              id="Base64IMG"
              :src="previewImage"
              height="300"
              alt=""
              style="display: block;margin: auto;"
            />
            <a-empty v-else/>
          </div>
        </a-col>
        <a-col :span="12">
          <a-textarea
            id="Console"
            style=" height: 300px;  width: 100%;  resize: none;border-radius: 0"
            :disabled="true"></a-textarea>
        </a-col>
        <a-col :span="24">
          <div
            :style="{...heightStyle, display: 'flex', justifyContent: 'center', alignItems: 'center'}">
            <img
              id="cam"
              :src="cameraurl"
              :style="rotateStyle"
              class="video"
              alt=""/>
          </div>
        </a-col>
        <a-col :span="24" v-if="showid">
          <a-form layout="horizontal">
            <a-form-item label="姓名">
              <a-input v-model="idCardForm.name" disabled/>
            </a-form-item>
            <a-form-item label="性别">
              <a-input v-model="idCardForm.sex" disabled/>
            </a-form-item>
            <a-form-item label="民族">
              <a-input v-model="idCardForm.nation" disabled/>
            </a-form-item>
            <a-form-item label="出生日期">
              <a-input v-model="idCardForm.birthday" disabled/>
            </a-form-item>
            <a-form-item label="地址">
              <a-textarea v-model="idCardForm.address" :rows="2" disabled/>
            </a-form-item>
            <a-form-item label="身份证号">
              <a-input v-model="idCardForm.cardID" disabled/>
            </a-form-item>
            <a-form-item label="签发机关">
              <a-input v-model="idCardForm.issueOrgan" disabled/>
            </a-form-item>
            <a-form-item label="有效期">
              <a-input v-model="idCardForm.validStart" disabled/>
              <a-input v-model="idCardForm.validEnd" disabled/>
            </a-form-item>

            <div class="id-photo-section">
              <div class="photo-preview">
                <img
                  id="PhotoDisplay"
                  :src="idCardForm.photoUrl"
                  class="id-photo"
                  alt=""/>
              </div>
              <a-button type="primary" @click="confirmIdCard">确认信息</a-button>
            </div>
          </a-form>
        </a-col>
      </a-row>
    </div>
    <template #title>
      <a-row>
        <a-col :span="24">
          <a-space>
            高拍仪
            <a-button @click="handleCancel">关闭</a-button>
          </a-space>
        </a-col>
      </a-row>
    </template>
    <div id="result"></div>
  </a-modal>
</template>

<script>
import axios from 'axios'

let ws
const SERVER_ADDR = 'ws://127.0.0.1:38088'

export default {
  name: 'HeightScanner',

  props: {
    show: Boolean,
    showid: Boolean
  },

  data () {
    return {
      SERVER_ADDR,
      cameraurl: 'http://127.0.0.1:38088/video=stream&camidx=0',
      connected: false,
      clicked: false,
      iswaiting: false,
      previewImage: '',
      consoleText: '',
      idCardForm: {
        name: '',
        sex: '',
        nation: '',
        birthday: '',
        address: '',
        cardID: '',
        issueOrgan: '',
        validStart: '',
        validEnd: '',
        photoBuffer: '',
        photoUrl: ''
      },
      callback: {},
      context: 0,
      rotateAngle: 0,
      base_offset: 0,
      orgdrawwidth: 0,
      orgdrawheight: 0,
      containerHeight: 440,

      CLT_MSG: {
        // 打开相机 cap:相机ID
        BEGIN_CAP: 1,
        // 关闭相机 cap:相机ID
        STOP_CAP: 2,
        // 获取PID
        GET_PID: 3,
        // 获取分辨率
        GET_FBL: 4,
        // 设置分辨率
        SET_FBL: 5,
        // 设置MJPG格式
        SET_MJPG: 6,
        // 设置视频旋转
        SET_TURN: 7,
        // 设置视频截图保存的jpg质量
        SET_QUALITY: 8,
        // 设置自动裁边
        SET_AUTOCROP: 9,
        // 设置预设框选
        SET_PREST: 10,
        // 获取当前预设框选
        GET_PREST_INFO: 11,
        // 视频格式弹窗
        FORMAT_DLG: 12,
        // 视频属性弹窗
        PROPERTY_DLG: 13,
        // 设置截图颜色模式 彩色 黑白 灰度
        SET_COLOR_MODEL: 14,
        // 截图
        GET_PICTURE: 15,
        // 设置去底色参数
        SET_CLEAN_PARAM: 16,
        // 内存截图
        GET_PICTURE_MEM: 17,
        // base64测试
        TEST_BASE64: 18,
        // 制作PDF的临时目录
        SET_TEMPDIR: 19,
        // 开始制作PDF
        BEGIN_PDF: 20,
        // PDF截图 保存到PDF临时目录
        GET_PDF_PICTURES: 21,
        // 结束制作PDF
        END_PDF_PICTURES: 22,
        // PDF文件转为文件里的BASE64码
        PDF_TO_BASE64: 23,
        // 文件里的BASE64码转为PDF文件
        BASE64_TO_PDF: 24,
        // PDF文件转为BASE64字符串
        PDF_TO_BASE64_STR: 25,
        // 删除制作PDF的图片文件
        DEL_PICTURE_PDF: 26,
        // 打印图片
        PRINT_IMAGE: 27,
        // 设置显示错误弹窗
        SHOW_ERROR_BOX: 28,
        // 获取视频连接事件
        GET_EVENT: 29,
        // 预设框选鼠标消息
        PRESET_MOUSEDOWN: 30,
        PRESET_MOUSEMOVE: 31,
        PRESET_MOUSEUP: 32,
        // 人证对比
        IDCARD_COMPARE: 40,
        // 读卡
        READ_IDCARD: 51,
        // 开始循环读卡
        BEGIN_READ_IDCARD: 52,
        // 停止循环读卡
        STOP_READ_IDCARD: 53,
        // 水平
        HCOPY_IDCARD: 54,
        // 垂直
        VCOPY_IDCARD: 55,

        PCOPY_IDCARD: 56,
        // 反面
        NCOPY_IDCARD: 57
      },
      SVR_BIN_MSG: {
        CAP_FRAME: 1, // 后台帧消息
      }
    }
  },
  computed: {
    rotateStyle () {
      return {
        transform: `rotate(${this.rotateAngle}deg)`,
        transition: 'transform 0.5s ease',
        maxHeight: `${this.rotateAngle % 180 === 0 ? '440px' : '660px'}`
      }
    },
    heightStyle () {
      return {
        width: '100%',
        height: `${this.containerHeight}px`,
      }
    }
  },
  methods: {
    rotateCamera () {
      console.log('>>> 旋转镜头')
      // 每次点击增加 90 度，超过 360 度时归零
      this.rotateAngle = (this.rotateAngle + 90) % 360
      // 根据旋转角度设置不同的高度
      this.containerHeight = this.rotateAngle % 180 === 0 ? 440 : 660
    },
    // handleImgError (e) {
    //   e.target.style.display = 'none'
    // },
    // 页面load消息
    onLoad () {
      return new Promise((resolve, reject) => {
        // 添加重试计数器
        let retryCount = 0
        const maxRetries = 5

        const tryConnect = () => {
          if (ws) {
            resolve()
            return
          }

          ws = new WebSocket(this.SERVER_ADDR)

          ws.onopen = (evt) => {
            if (this.connected) {
              resolve()
            }
            this.connected = true
            this.output('已经连接到服务器')
            this.switchCamera(0)
          }

          ws.onclose = (evt) => {
            if (!this.connected) {
              // 检查重试次数
              if (retryCount < maxRetries) {
                retryCount++
                this.output(`连接失败,正在进行第${retryCount}次重试...`)
                window.location.href = 'FLShell://'
                setTimeout(tryConnect, 3 * 1000)
              } else {
                this.output('连接失败,已达到最大重试次数')
                reject(new Error('连接失败,已达到最大重试次数'))
              }
            } else {
              this.output('已断开服务器连接')
              this.connected = false
            }
            ws = null
          }

          ws.onerror = (evt) => {
            this.output('高拍仪连接错误')
            reject(new Error('连接错误'))
          }
          ws.onmessage = (evt) => {
            if (evt.data.length === 0) {
              return
            }
            if (typeof evt.data === 'string') {
              const msg = JSON.parse(evt.data)
              console.log('msg1:', msg)
              if (msg.err && msg.err !== 0) {
                this.output('错误 - ' + msg.err + ', ' + msg.msg)
                reject(new Error('错误 - ' + msg.err + ', ' + msg.msg))
              } else if (msg.msg) {
                console.log('msg2:', msg)
                this.output(msg.msg)
              }
              if (msg.user_data) {
                this.callback[msg.user_data](msg)
                if (typeof msg.user_data === 'number') {
                  this.callback[msg.user_data] = undefined
                }
              }
            } else {
              const file = new FileReader()
              file.onload = (evt) => {
                if (evt.target.readyState == FileReader.DONE) {
                  const data = new Uint8Array(evt.target.result)
                  if (data[0] == this.SVR_BIN_MSG.CAP_FRAME) {
                    return this.drawImage(data)
                  }
                }
              }
              file.readAsArrayBuffer(evt.data)
            }
          }
          const canvas = document.getElementById('cam')
          canvas.onmousedown = (e) => {
            canvas.style.cursor = 'crosshair'
            this.clicked = true
            this.sendMsg(this.CLT_MSG.PRESET_MOUSEDOWN, { origX: e.layerX, origY: e.layerY }, (msg) => {
              if (msg.err !== 0) {
                console.warn('鼠标按下事件出错:', msg.err)
              }
            })
          }
          canvas.onmousemove = (e) => {
            if (!this.clicked) {
              canvas.style.cursor = 'default'
              return
            }
            canvas.style.cursor = 'crosshair'
            this.sendMsg(this.CLT_MSG.PRESET_MOUSEMOVE, { offx: e.layerX, offy: e.layerY }, (msg) => {
              if (msg.err !== 0) {
                console.warn('鼠标移动事件出错:', msg.err)
              }
            })
          }
          onmouseout = canvas.onmouseup = (e) => {
            canvas.style.cursor = 'default'
            if (!this.clicked) {
              return
            }
            this.sendMsg(this.CLT_MSG.PRESET_MOUSEUP, { offx: e.layerX, offy: e.layerY }, (msg) => {
              if (msg.err !== 0) {
                console.warn('鼠标抬起事件出错:', msg.err)
              }
            })
            this.clicked = false
          }
        }

        tryConnect() // 开始首次连接
      })
    },
    newInstancePost (url, data) {
      // 重新创建是为了 避免axios的拦截器影响写卡读卡请求
      // 请求 如果携带了 Cookie，token到卡服务 会导致请求失败
      // 需要注意的是因为没有增加响应拦截器 所以返回结果的res里面会多一层data
      const instance = axios.create()
      return instance.post(url, data, {
        withCredentials: false,
        headers: {
          'Content-Type': 'text/plain;charset=UTF-8',
          Accept: '*/*',
        }
      })
    },
    // 打开视频
    switchCamera (data) {
      this.cameraurl = 'http://127.0.0.1:38088/video=stream&camidx=' + data + '&tid=' + Date.parse(new Date())
    },
    // 关闭视频
    async closeVideo () {
      const params = ''
      // const url = this.cameraurl.replace('stream', 'close')
      this.cameraurl = ''
      const msg = await this.newInstancePost('http://127.0.0.1:38088/card=idcard', params)
      if (msg.data.code != '0') {
        document.getElementById('result').innerHTML = msg.data.message
      }
    },
    // 获取设备状态
    async getVideoStatus () {
      document.getElementById('result').innerHTML = ''
      const params = ''
      const url = 'http://127.0.0.1:38088/video=status'
      this.cameraurl = ''
      const msg = await this.newInstancePost(url, params)
      if (msg.data.code != '0') {
        document.getElementById('result').innerHTML = '获取设备状态失败，返回代码 = ' + msg.data.code + '，返回信息 = ' + msg.data.message
      } else {
        document.getElementById('result').innerHTML = '文档摄像头:' + msg.data.video0 +
          ', 人像1:' + msg.data.video1
      }
    },
    openApp () {
      window.location.href = 'cameraServicetis:0' // 启动 CameraService 服务程序
    },
    async capturePhoto () {
      // if (!this.name) {
      //  return this.$showMessage('请输入照片名称')
      // }
      // if (!this.useType) {
      //   return this.$showMessage('请选择文件使用类型')
      // }
      let camidx = 0
      if (this.cameraurl.indexOf('http://127.0.0.1:38088/video=stream&camidx=0') != -1) {
        camidx = 0
      } else if (this.cameraurl.indexOf('http://127.0.0.1:38088/video=stream&camidx=1') != -1) {
        camidx = 1
      } else {
        this.$message.info('请先打开视频')
        return
      }
      const param = {
        camidx: camidx,
        image_process_info: {
          cut_type: '0',
          multi_object: '0',
          rotate: `${this.rotateAngle}`,
        },
      }
      const msg = await this.newInstancePost('http://127.0.0.1:38088/video=grabimage', param)
      if (msg.data.code != '0') {
        this.$message.info(msg.data.message)
        return
      }
      console.log('msg========================>', msg)
      this.previewImage = 'data:image/jpg;base64,' + msg.data.images[0].base64
      this.output('base64 长度 : ' + this.previewImage)
      this.handleOk()
      this.name = ''
      // this.$emit('photo-finish', { img: this.previewImage })
      // post('http://127.0.0.1:38088/video=grabimage', param)
    },
    // 读身份证
    async startReadCard () {
      const msg = await this.newInstancePost('http://127.0.0.1:38088/card=idcard', {})
      if (msg.data.code != '0') {
        this.$message.info(msg.data.message, 2000)
        return
      } else {
        this.updateIdCardInfo(msg.data.IDCardInfo)
      }
      this.output('读取身份证')
      this.output('开始读卡')
    },
    // 停止读身份证
    stopReadCard () {
      this.sendMsg(this.CLT_MSG.STOP_READ_IDCARD, (msg) => {
        if (msg.err !== 0) {
          return
        }
        this.output('停止读卡')
      })
    },
    // 视频帧绘制函数
    drawImage (data) {
      // 校验视频是否在一定时间内的
      if (!this.checkTimestamp(data[2] * 256 + data[1])) {
        return
      }
      // 视频帧头有时间戳 注意偏移 和 大小端处理
      const width = data[this.base_offset + 1] * 256 + data[this.base_offset]
      const height = data[this.base_offset + 3] * 256 + data[this.base_offset + 2]

      const cam = document.getElementById('cam')
      const context = cam.getContext('2d')
      if (this.orgdrawwidth != width || this.orgdrawheight != height) {
        this.orgdrawwidth = width
        this.orgdrawheight = height
      }

      const image = context.createImageData(width, height)
      for (let n = 0; n != height; ++n) {
        for (let m = 0; m != width; ++m) {
          const imageOffset = ((height - n - 1) * width + m) * 4
          const dataOffset = (n * width + m) * 3 + this.baseOffset + 4
          image.data[imageOffset] = data[dataOffset + 2]
          image.data[imageOffset + 1] = data[imageOffset + 1]
          image.data[imageOffset + 2] = data[imageOffset]
          image.data[imageOffset + 3] = 255
        }
      }
      context.putImageData(image, 0, 0)
    },
    checkTimestamp (svrTime) {
      const myDate = new Date()
      let cltTime = myDate.getSeconds() * 1000 + myDate.getMilliseconds()
      if (cltTime < svrTime) {
        cltTime += 60 * 1000
      }
      return cltTime - svrTime < 3000
    },
    sendMsg (proto, msg, cb) {
      if (!ws) {
        return
      }

      if (typeof msg !== 'object') {
        if (typeof msg === 'function') {
          cb = msg
          msg = {}
        } else {
          msg = {}
        }
      }

      msg.proto = proto
      if (typeof cb === 'function') {
        this.callback[++this.context] = cb
        msg.user_data = this.context
      }

      ws.send(JSON.stringify(msg))
    },
    output (content) {
      if (content.length == 0) {
        return
      }
      const console = document.getElementById('Console')
      console.value += content
      console.value += '\r'
      console.scrollTop = console.scrollHeight
    },
    // 显示读到的身份证信息
    updateIdCardInfo (msg) {
      document.getElementById('Name').value = msg.name
      document.getElementById('Sex').value = msg.sex
      document.getElementById('Nation').value = msg.nation
      document.getElementById('Born').value = msg.birthday
      document.getElementById('Address').value = msg.address
      document.getElementById('CardNo').value = msg.cardID
      document.getElementById('Police').value = msg.issueOrgan
      document.getElementById('ActivityLFrom').value = msg.validStart
      document.getElementById('ActivityLTo').value = msg.validEnd
      // document.getElementById('DeviceNo').value = msg.validEnd
      document.getElementById('PhotoBuffer').value = msg.fingerBase64
      document.getElementById('PhotoDisplay').src = 'data:image/jpg;base64,' + msg.photoBase64
      this.IdCardInfo = msg
    },
    confirmIdCard () {
      const param = this.IdCardInfo
      if (this.IdCardInfo) {
        this.$emit('confirm-info', param)
        this.IdCardInfo = {}
      }
    },
    handleOk () {
      this.$emit('confirmPhoto', this.previewImage)
    },
    handleCancel () {
      this.$emit('cancel')
    }
  },

  mounted () {
    this.onLoad().then(() => {
      this.$emit('ready')
    }).catch(error => {
      this.$emit('error', error)
    })
  },

  beforeDestroy () {
    if (ws) {
      ws.close()
      ws = null
    }
  }
}
</script>
<style lang="less" scoped>
.video {
  width: 70%;
  height: 440px;
  display: flex;
  flex-direction: column;
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
}
</style>
