<template>
  <div class="camera-container">
    <!-- 相机模块 -->
    <el-card class="camera-module">
      <div class="header-container">
        <div class="header-title">相机1模块</div>
        <div class="header-buttons">
          <el-button type="primary" @click="getCameraList">获取相机列表</el-button>
        </div>
      </div>

      <template v-if="cameraList.length > 0">
        <!-- 相机参数模块 - 紧凑版 -->
        <div class="camera-params compact">
          <div class="section-title">相机参数设置</div>
          <el-form inline class="params-form">
            <el-form-item label="分辨率">
              <el-select v-model="globalParams.resolution" style="width: 140px">
                <el-option label="2560×1440" value="2560x1440"></el-option>
                <el-option label="1920×1080" value="1920x1080"></el-option>
                <el-option label="1280×960" value="1280x960"></el-option>
              </el-select>
            </el-form-item>

            <el-form-item label="帧率">
              <el-select v-model="globalParams.fps" style="width: 80px">
                <el-option label="15" :value="15"></el-option>
                <el-option label="30" :value="30"></el-option>
                <el-option label="60" :value="60"></el-option>
              </el-select>
            </el-form-item>

            <el-form-item label="保存路径">
              <el-input v-model="globalParams.savePath" placeholder="/images" style="width: 180px"></el-input>
            </el-form-item>

            <el-form-item>
              <el-button-group>
                <el-button type="primary" @click="initAllCameras" icon="el-icon-refresh">初始化</el-button>
                <el-switch v-model="globalParams.lowFpsMode" active-text="低帧率" inactive-text="原始帧"
                  @change="toggleFpsMode" style="margin-left: 10px;" />
                <el-button @click="closeAllCameras" size="mini" icon="el-icon-close"
                  style="margin-left: 10px;">关闭所有</el-button>
              </el-button-group>
            </el-form-item>
          </el-form>
        </div>

        <!-- 视频帧显示区 -->
        <div class="section-title">视频帧显示</div>
        <el-row :gutter="20" class="frame-display-row">
          <el-col :span="8" v-for="camera in cameraList" :key="camera.id" class="frame-display-wrapper">
            <el-card class="frame-display">
              <div slot="header" class="clearfix">
                <div class="frame-title">
                  <span class="camera-id">相机 {{ camera.id }}_{{ camera.name }}</span>
                  <span class="camera-spec">({{ camera.width }}×{{ camera.height }} @{{ camera.fps }}fps)</span>
                </div>
              </div>
              <div class="frame-header">
                <el-select v-model="camera.cameraOrientation" style="width: 120px">
                  <el-option label="不指定" value="不指定"></el-option>
                  <el-option label="后侧" value="后侧"></el-option>
                  <el-option label="左侧" value="左侧"></el-option>
                  <el-option label="右侧" value="右侧"></el-option>
                </el-select>
                <el-button-group style="margin-left: 10px;">
                  <el-button @click="configCamera(camera)" size="mini">配置</el-button>
                  <el-button type="success" @click="startStream(camera)" size="mini"
                    icon="el-icon-video-play">链接</el-button>
                  <el-button type="danger" @click="stopStream(camera)" size="mini"
                    icon="el-icon-switch-button">断链</el-button>
                </el-button-group>
              </div>
              <div v-if="!camera.imageData" ref="frameDisplay" style="width: 100%; height: 280px; background: #000;">
              </div>
              <canvas v-else :ref="'canvas_' + camera.id" style="width: 100%; height: 100%; background: #000;"></canvas>
              <div class="status-info">
                <el-tag v-if="camera.statusInfo" type="info" size="small">{{ camera.statusInfo }}</el-tag>
              </div>
              <div class="status-log">
                <el-alert v-if="camera.captureLog" :title="camera.captureLog" type="info" :closable="false" show-icon
                  style="padding: 5px 10px;" />
              </div>
            </el-card>
          </el-col>
        </el-row>

        <!-- 全局控制模块 -->
        <div class="section-title">全局控制</div>
        <div class="global-controls">
          <el-button-group>
            <el-button type="success" @click="captureImage">捕获</el-button>
          </el-button-group>
        </div>
      </template>
      <div v-else class="empty-tip">
        <el-empty description="暂无相机，请点击右上角按钮获取相机列表"></el-empty>
      </div>
    </el-card>
  </div>
</template>

<script>
import {
  baseURLConsole
} from '@/utils/url'
import { getStillCameraList, initializeStillCamera, setStillCameraOrientation, lowStillCamera, getWebSocketLink, closeStillCamera, captureStillCamera, disconnectStillCamera } from '@/api/stillCamera'
export default {
  name:'StillCamera',
  data() {
    return {
      globalParams: {
        fps: 30,//帧率
        resolution: '2560x1440',//分别率
        savePath: '/images/captures',//存储路径
        lowFpsMode: false,//低帧率模式开关
      },
      cameraList: [],//相机列表
    }
  },
  methods: {
    //获取相机列表
    getCameraList() {
      // 调用接口获取相机列表
      getStillCameraList().then(res => {
        console.log(res)
        if (res.data.length > 0) {
          let list = [];
          res.data.forEach(item => {
            list.push({
              id: item.camera_id,
              name: item.name,
              width: 0,
              height: 0,
              fps: 0,
              cameraOrientation: '不指定',//方位
              websocket: null,
              reconnectAttempts: 0,//重连次数限制
              imageData: null,
              objectURL: null,
              statusInfo: null,
              captureLog: '等待连接',
            })
          })
          this.cameraList = list;
          this.$message.success(`获取到 ${this.cameraList.length} 个相机`)
          //查询相机原有的webScoket链接并关闭
          getWebSocketLink().then(res => {
            console.log(res)
            if (res.data.connected_cameras.length > 0) {
              res.data.connected_cameras.forEach(item => {
                disconnectStillCamera(item)
              })
            }
          })
        } else {
          this.$message.error('当前没有相机')
        }
      })
    },
    //初始化所有相机
    initAllCameras() {
      // 调用接口初始化所有相机
      let cameraIds = [];
      const [width, height] = this.globalParams.resolution.split('x')
      //设置相机
      this.cameraList.forEach(camera => {
        cameraIds.push(camera.id)
      })
      //设置参数
      let param = {
        camera_ids: cameraIds.join(","),
        init_params: {
          resolution_width: parseInt(width),
          resolution_height: parseInt(height),
          fps: this.globalParams.fps
        }
      }
      initializeStillCamera(param).then(res => {
        console.log(res)
        this.cameraList.forEach(camera => {
          camera.width = parseInt(width)
          camera.height = parseInt(height)
          camera.fps = this.globalParams.fps
        })
        this.$message.success(`初始化所有相机成功`)
      })
    },

    // 切换帧率模式
    toggleFpsMode(isLowFps) {
      // 调用接口设置帧率模式
      let cameraIds = [];
      this.cameraList.forEach(camera => {
        cameraIds.push(camera.id)
      });

      let param = {
        camera_ids: cameraIds.join(','),
        fps: isLowFps ? 5 : 25
      };

      lowStillCamera(param).then(res => {
        this.$message.success(isLowFps ? '已启用低帧率模式' : '已恢复原始帧率');
      })
    },
    //配置相机
    configCamera(camera) {
      let param = {
        camera_id: camera.id,
        orientation: camera.cameraOrientation
      }
      //调用接口配置相机
      setStillCameraOrientation(param).then(res => {
        console.log(res)
        this.$message.success(`配置相机成功`)
      })
    },
    //开始链接webScoke
    startStream(camera) {
      // 创建canvas用于渲染帧数据
      if (camera.websocket) {
        camera.websocket.close();
        camera.websocket = null;
        // 等待一小段时间确保连接完全关闭
        setTimeout(() => {
          this.establishWebSocketConnection(camera);
        }, 100);
      } else {
        this.establishWebSocketConnection(camera);
      }
    },
    establishWebSocketConnection(camera) {
      const wsUrl = `ws://${baseURLConsole}/api/cameras/hd_static/ws/connect/${camera.id}`;
      camera.websocket = new WebSocket(wsUrl);
      camera.reconnectAttempts = 0;
      camera.websocket.onopen = (event) => {
        camera.captureLog = 'WebSocket已连接，等待相机响应...'
        //心跳检测
        setInterval(() => {
          if (camera.websocket.readyState === WebSocket.OPEN) {
            camera.websocket.send('ping');
          }
        }, 30000);
      };
      camera.websocket.onmessage = (event) => {
        try {
          const data = JSON.parse(event.data);
          // 处理连接状态消息
          if (data.status) {
            camera.captureLog = `连接成功: ${data.message}`
            return;
          }
          // 处理错误消息
          if (data.error) {
            if (data.error.includes('未初始化')) {
              camera.captureLog = '请先在"相机控制"部分初始化相机，然后再尝试连接视频流'
            }
            if (data.error.includes('已在流式传输中')) {
              camera.captureLog = '相机正在使用中，请等待一会儿后再尝试连接'
            }
            camera.captureLog = `错误: ${data.error}`
            return;
          }
          // 处理视频帧数据
          if (data.data) {
            // 创建Image对象加载图像
            const img = new Image();
            img.onload = () => {
              // 获取对应相机的canvas元素
              const canvasRef = `canvas_${camera.id}`;
              const canvas = this.$refs[canvasRef]?.[0];
              if (!canvas) return;

              // 获取容器尺寸
              const containerWidth = canvas.parentElement.clientWidth;
              const containerHeight = canvas.parentElement.clientHeight;

              // 计算保持比例的尺寸
              let width = img.width;
              let height = img.height;
              const ratio = width / height;

              if (width > containerWidth) {
                width = containerWidth;
                height = width / ratio;
              }

              if (height > containerHeight) {
                height = containerHeight;
                width = height * ratio;
              }

              // 设置canvas尺寸
              canvas.width = width;
              canvas.height = height;

              // 绘制图像
              const ctx = canvas.getContext('2d');
              ctx.clearRect(0, 0, canvas.width, canvas.height);
              ctx.drawImage(img, 0, 0, width, height);

              // 更新状态信息
              let statusInfo = `分辨率: ${img.width}x${img.height} | 显示尺寸: ${Math.round(width)}x${Math.round(height)}`;
              if (data.performance) {
                statusInfo += ` | 实际FPS: ${data.performance.actual_fps} | 目标FPS: ${data.performance.target_fps}`;
              }
              if (data.encode_time_ms) {
                statusInfo += ` | 编码耗时: ${data.encode_time_ms}ms`;
              }
              if (data.data_size_kb) {
                statusInfo += ` | 数据大小: ${data.data_size_kb}KB`;
              }
              camera.statusInfo = statusInfo;
              img.src = '';
            };
            img.src = `data:image/jpeg;base64,${data.data}`;
            camera.imageData = `data:image/jpeg;base64,${data.data}`;
          }
        } catch (error) {
          camera.captureLog = `解析消息数据时出错: ${error}`
        }
      }

      camera.websocket.onerror = (error) => {
        console.log(error)
        camera.captureLog = `WebSocket连接错误`
      };

      camera.websocket.onclose = (event) => {
        console.log(`WebSocket连接已关闭 (代码: ${event.code}, 原因: ${event.reason || '未知'})`);

        // 不同的关闭代码有不同的含义
        let closeReason = '';
        switch (event.code) {
          case 1000:
            closeReason = '正常关闭';
            break;
          case 1001:
            closeReason = '端点离开';
            break;
          case 1002:
            closeReason = '协议错误';
            break;
          case 1003:
            closeReason = '不支持的数据类型';
            break;
          case 1006:
            closeReason = '异常关闭';
            break;
          case 1011:
            closeReason = '服务器错误';
            break;
          default:
            closeReason = `未知错误 (${event.code})`;
        }


        camera.captureLog = `WebSocket连接已关闭: ${closeReason}`

        // 只有在非正常关闭的情况下才自动重连
        if (event.code !== 1000 && camera.reconnectAttempts < 5) {
          camera.reconnectAttempts++;
          const retryDelay = Math.min(1000 * Math.pow(2, camera.reconnectAttempts - 1), 10000); // 指数退避，最大10秒
          console.log(`连接异常关闭，${retryDelay / 1000}秒后尝试重连 (${camera.reconnectAttempts}/5)...`);
          camera.captureLog = `连接异常关闭，${retryDelay / 1000}秒后尝试重连 (${camera.reconnectAttempts}/5)...`
          setTimeout(() => this.startStream(camera), retryDelay);
        } else if (event.code === 1000) {
          console.log('WebSocket正常关闭，不进行重连');
        } else {
          console.log('重连次数已达上限，停止重连');
          camera.captureLog = '重连失败，请检查相机状态后手动重连'
        }
      };

    },
    //断开链接
    stopStream(camera) {
      //调用接口断开链接
      disconnectStillCamera(camera.id).then(res => {
        this.$message.success(`断开链接成功`)
        if (camera.websocket) {
          camera.websocket.close()
          this.$nextTick(() => {
            camera.websocket = null
            camera.reconnectAttempts = 0
            camera.imageData = null
            camera.objectURL = null
            camera.statusInfo = null
            camera.captureLog = '等待链接'
          })
        }
      })
    },

    //关闭所有相机
    closeAllCameras() {

      let cameraIds = [];
      this.cameraList.forEach(camera => {
        cameraIds.push(camera.id);
        if (camera.websocket) {
          camera.websocket.close();
          camera.websocket = null;
        }
      });

      let param = {
        camera_ids: cameraIds.join(',')
      };

      closeStillCamera(param).then(res => {
        console.log(res);
        this.cameraList = [];
        this.globalParams = {
          fps: 30,//帧率
          resolution: '2560x1440',//分别率
          savePath: '/images/captures',//存储路径
          lowFpsMode: false,//低帧率模式开关
        },
          this.$message.success('已关闭所有相机');
      })
    },
    //捕捉帧
    captureImage(camera) {
      let cameraIds = [];
      this.cameraList.forEach(camera => {
        cameraIds.push(camera.id);
      });
      //调用接口捕获帧
      let param = {
        camera_ids: cameraIds.join(','),
        user_name: this.$store.getters.username,
        save_dir: this.globalParams.savePath,
      }
      captureStillCamera(param).then(res => {
        this.$message.success(`捕获图像成功`)
      })
    },
    base64ToBlob(base64) {
      const byteString = atob(base64.split(',')[1]);
      const mimeType = base64.match(/:(.*?);/)[1];
      const ab = new ArrayBuffer(byteString.length);
      const ia = new Uint8Array(ab);
      for (let i = 0; i < byteString.length; i++) {
        ia[i] = byteString.charCodeAt(i);
      }
      return new Blob([ab], { type: mimeType });
    }
  },
  // 2. 组件销毁时清理
  beforeDestroy() {
    this.cameraList.forEach(camera => {
      if (camera.objectURL) URL.revokeObjectURL(camera.objectURL);
      if (camera.websocket) {
        camera.websocket.close()
        disconnectStillCamera(camera.id)
      }
    });
  }
}
</script>

<style scoped>
.camera-container {
  padding: 20px;
  background-color: #f5f7fa;
}

/* 相机模块样式 */
.camera-module {
  border-radius: 8px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  margin-bottom: 20px;
}

/* 模块标题样式 */
.section-title {
  font-size: 16px;
  font-weight: bold;
  color: #303133;
  margin-bottom: 15px;
  padding-bottom: 8px;
  border-bottom: 2px solid #409eff;
}

/* 相机参数模块样式 */
.camera-params {
  padding: 20px;
  margin-bottom: 20px;
  background-color: #fff;
  border-radius: 6px;
  box-shadow: 0 1px 6px rgba(0, 0, 0, 0.1);
  border: 1px solid #ebeef5;
  position: relative;
}

.camera-params::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 4px;
  height: 100%;
  background-color: #409eff;
  border-radius: 4px 0 0 4px;
}

/* 紧凑版参数模块 */
.camera-params.compact {
  padding: 15px;
}

.camera-params.compact .params-form {
  margin-top: 10px;
}

.camera-params.compact .el-form-item {
  margin-bottom: 0;
  margin-right: 15px;
}

.camera-params.compact .el-form-item__label {
  font-size: 12px;
  color: #606266;
  padding-right: 5px;
}

.camera-params.compact .el-input-number {
  line-height: 28px;
}


/* 视频帧显示区样式 */
.frame-display-row {
  margin-bottom: 20px;
}

.frame-display {
  height: 100%;
  border-radius: 8px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  margin-bottom: 15px;
  background-color: #fff;
  border: 1px solid #ebeef5;
}

/* 全局控制模块样式 */
.global-controls {
  margin-top: 20px;
  padding: 20px;
  background-color: #fff;
  border-radius: 6px;
  box-shadow: 0 1px 6px rgba(0, 0, 0, 0.1);
  border: 1px solid #ebeef5;
  text-align: center;
}

.header-container {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20px;
}

.header-title {
  font-size: 24px;
  font-weight: bold;
  color: #303133;
  padding-left: 10px;
  border-left: 4px solid #409eff;
}

.header-buttons {
  display: flex;
  gap: 10px;
}

.patient-info {
  margin-bottom: 20px;
  border-radius: 8px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}

.info-item {
  display: flex;
  align-items: center;
  margin-bottom: 10px;
  font-size: 14px;
}

.info-item label {
  width: 80px;
  color: #666;
  font-weight: 500;
}

.camera-list {
  display: flex;
  flex-direction: column;
  gap: 15px;
}

.camera-item {
  display: flex;
  justify-content: space-between;
  padding: 15px;
  border: 1px solid #ebeef5;
  border-radius: 6px;
  background-color: #fff;
  transition: all 0.3s;
  margin-bottom: 10px;
}

.camera-item:hover {
  transform: translateY(-3px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}

.selected-camera {
  border: 2px solid #409eff;
  background-color: #f0f7ff;
}

.frame-title {
  display: flex;
  align-items: center;
  font-size: 16px;
}

.camera-id {
  font-weight: bold;
  margin-right: 8px;
}

.camera-spec {
  font-size: 12px;
  color: #909399;
}

.frame-header {
  padding: 8px 15px;
  display: flex;
  justify-content: center;
  background-color: #f5f7fa;
  border-bottom: 1px solid #ebeef5;
}

.camera-info {
  flex: 1;
}

.info-row {
  display: flex;
  align-items: center;
  margin-bottom: 8px;
}

.info-row label {
  width: 70px;
  font-size: 12px;
  color: #666;
  text-align: right;
  padding-right: 8px;
  flex-shrink: 0;
}

.camera-actions {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
}

.frame-display-row {
  margin-bottom: 20px;
}

.frame-display {
  height: 100%;
  border-radius: 8px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}

.frame-display-wrapper {
  height: 100%;
}

.global-controls {
  margin-top: 20px;
  text-align: center;
  padding: 15px;
  background-color: #f5f7fa;
  border-radius: 6px;
}

.capture-controls {
  margin-top: 15px;
  padding: 15px;
  background-color: #f5f7fa;
  border-radius: 6px;
}

.button-group {
  display: flex;
  justify-content: center;
  gap: 10px;
  margin-bottom: 15px;
}
</style>
