<template>
  <Page :auto-content-height="true">
    <div class="flex h-full gap-[8px]">
      <div class="h-full tree-box">
        <DpTree class="h-full w-[300px]" @checked="onNodeChecked" />
      </div>
      <div class="bg-background flex-1">
        <div class="video-play-area flex h-full flex-wrap">
          <div
            v-for="i in playerNum"
            :style="playerStyle"
            class="player"
            :class="`layer-${i} ${currentSelectPlayerIndex == i ? selected : ''}`"
            @click="playerSelect(i)"
          >
            <video
              style="width: 100%; height: 100%"
              :ref="setItemRef"
              muted
              autoplay
            ></video>
          </div>
        </div>
      </div>
    </div>
  </Page>
</template>

<script setup lang="ts">
import DpTree from './dp-tree.vue';
import { Page } from '@vben/common-ui';
import { onMounted, onUnmounted, ref, toRaw } from 'vue';
import mpegts from 'mpegts.js';
import { addFFmpegStreamProxy, addStreamProxy } from '#/api/sis/stream';
import { message } from 'ant-design-vue';
import { checkHEVCSupport } from '#/utils/video';
import type { AddStreamProxyResult } from '#/api/sis/stream/model';

/**
 * 屏幕播放器数量
 */
const selected = 'selected';
const playerNum = ref(1);
/**
 * 屏幕播放器样式
 */
const playerStyle = ref({
  width: '100%',
  height: '100%',
});
const currentSelectPlayerIndex = ref(-1);

function playerSelect(index: number) {
  if (index === currentSelectPlayerIndex.value) {
    currentSelectPlayerIndex.value = -1;
    return;
  }
  currentSelectPlayerIndex.value = index;
}

const itemRefs = ref<HTMLVideoElement[]>([]);
const setItemRef = (el: any) => {
  if (el) {
    itemRefs.value.push(el);
  }
};

/**
 * 处理带有子节点的数据
 * @param node
 * @param newNode
 */
function handleParentNoe(node: any, newNode: any[] = []) {
  node.forEach((item: any) => {
    if (item.level === 6) {
      newNode.push(toRaw(item.data));
    }
    if (item.children && item.children.length >= 1) {
      handleParentNoe(item.children, newNode);
    }
  });
}

/**
 * 节点选中时间处理
 * @param _val 选中节点id
 * @param checked 是否选中
 * @param node 节点数据
 */
function onNodeChecked(
  _val: any,
  { checked, node }: { checked: boolean; node: any },
) {
  // 此次操作需要新增或者删除节点
  let checkNode: any = [];
  handleParentNoe([node], checkNode);
  // 新增
  if (checked) {
    /**
     * 如果当前页面有选择播放未知，并且播放视频只有一个，则播放到制定位置
     */
    if (currentSelectPlayerIndex.value !== -1 && checkNode.length == 1) {
      doPlayer(checkNode[0], currentSelectPlayerIndex.value - 1);
    }
    // 批量播放 currentSelectPlayerIndex 将不再生效
    else {
      // 如果此次播放数量小于当前播能播放
      const freeArr: number[] = []; // 空闲播放器数量
      for (let i = 0; i < playerNum.value; i++) {
        const playerData = playerList[i];
        if (!playerData) {
          freeArr.push(i);
        }
      }
      // 要播放的视频数量，小于等于空闲播放器数量，则填充空闲即可
      if (checkNode.length <= freeArr.length) {
        for (let j = 0; j < checkNode.length; j++) {
          doPlayer(checkNode[j], freeArr[j]);
        }
      }
      // 直接覆盖原有的播放视频
      else {
        for (let i = 0; i < playerNum.value; i++) {
          doPlayer(checkNode[i], i);
        }
      }
    }
  }
  // 删除
  else {
    checkNode.forEach((item: any) => {
      for (let i = 0; i < playerNum.value; i++) {
        const player = playerList[i];
        if (player && player.data.id === item.id) {
          closePlayer(i);
        }
      }
    });
  }
}

// 播放器数据, 每一个位置代表页面上行的一个矩形
const playerList: any[] = [];

function streamProxy(nodeData: any, cb: Function) {
  let params = {};
  if (nodeData.nvrIp) {
    params = {
      videoIp: nodeData.nvrIp,
      videoPort: nodeData.nvrPort,
      factoryNo: nodeData.nvrFactoryNo,
      account: nodeData.nvrAccount,
      pwd: nodeData.nvrPwd,
      channelId: nodeData.nvrChannelNo,
    };
  } else {
    params = {
      videoIp: nodeData.deviceIp,
      videoPort: nodeData.devicePort,
      factoryNo: nodeData.factoryNo,
      account: nodeData.deviceAccount,
      pwd: nodeData.devicePwd,
      channelId: nodeData.channelNo,
    };

    if (isSupportH265) {
      addStreamProxy(params).then((res) => cb(res));
    } else {
      addFFmpegStreamProxy(params).then((res) => cb(res));
    }
  }
}

/**
 * 开始播放视频流
 * @param nodeData 播放的节点数据
 * @param index 播放器的索引信息
 */
function doPlayer(nodeData: any, index: number = 0) {
  console.log('index=', index);
  if (mpegts.isSupported()) {
    streamProxy(nodeData, (res: AddStreamProxyResult) => {
      const url = res.flv;
      // 将url 绑定到 nodeData
      nodeData.url = url;
      closePlayer(index);
      const videoConfig = {
        type: 'flv',
        url: url,
        isLive: true,
        hasAudio: false,
        hasVideo: true,
        enableWorker: true, // 启用分离的线程进行转码
        enableStashBuffer: false, // 关闭IO隐藏缓冲区
        stashInitialSize: 256, // 减少首帧显示等待时长
      };
      const playerConfig = {
        enableErrorRecover: true, // 启用错误恢复
        autoCleanupMaxBackwardDuration: 30,
        autoCleanupMinBackwardDuration: 10,
      };
      const player = mpegts.createPlayer(videoConfig, playerConfig);
      const videoElement = itemRefs.value[index];
      if (videoElement) {
        player.attachMediaElement(videoElement);
        player.load();
        player.play();
        playerList[index] = {
          player,
          data: nodeData,
          el: videoElement,
        };
      } else {
        console.log('视频播放元素获取异常');
      }
    });
  } else {
    message.error('浏览器不支持播放');
  }
}

function closePlayer(index: number) {
  // 如果播放器存在，尝试关闭
  const pData = playerList[index];
  if (pData) {
    try {
      const player = pData.player;
      player.pause(); // 暂停
      player.unload(); // 卸载
      player.destroy(); // 销毁
      playerList[index] = null;
    } catch (e) {
      console.log('播放器关闭失败，e=', e);
    }
  }
}

function catchUp() {
  playerList.forEach((playerData) => {
    if (playerData) {
      const { player, el } = playerData;
      const end = player.buffered.end(player.buffered.length - 1);
      const diff = end - el.currentTime;
      if (diff > 2) {
        // 如果延迟超过2秒
        el.currentTime = end - 0.5; // 跳转到接近直播点
      }
    }
  });
}

let isSupportH265 = false;
onMounted(() => {
  // 检测浏览器是否支持h265
  isSupportH265 = checkHEVCSupport();
  setInterval(catchUp, 10000);
});

onUnmounted(() => {
  for (let i = 0; i < playerList.length; i++) {
    closePlayer(i);
  }
});
</script>
<style>
.player {
  border: 1px solid #e4e4e7;
  cursor: pointer;

  video {
    width: 100%;
    height: 100%;
    object-fit: fill;
  }
}

.player.selected {
  border: 2px solid deepskyblue;
}
</style>
