<script lang="ts" setup>
import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue';
import { FitAddon } from '@xterm/addon-fit';
import { Terminal } from '@xterm/xterm';
import '@xterm/xterm/css/xterm.css';
import { RESULT_CODE_ERROR } from '@/constants/result-constants';
import { OptionsType, WS } from '@/plugins/ws-websocket';
const ws = new WS();
const emit = defineEmits(['connect', 'close', 'message']);
const props = defineProps({
  /**终端ID，必传 */
  id: {
    type: String,
    required: true,
  },
  /**ws连接地址，必传 如/ws/view */
  url: {
    type: String,
    required: true,
  },
  /**网元类型，必传 */
  neType: {
    type: String,
    required: true,
  },
  /**网元ID，必传 */
  neId: {
    type: String,
    required: true,
  },
  /**窗口单行字符数 */
  cols: {
    type: Number,
    default: 80,
  },
  /**窗口行数 */
  rows: {
    type: Number,
    default: 40,
  },
  /**ws发送requestId前缀 如ssh_id */
  prefix: {
    type: String,
    default: 'ssh',
  },
  /**消息处理函数 */
  processMessages: {
    type: Function,
    default: undefined,
  },
});

/**终端输入DOM节点实例对象 */
const terminalDom = ref<HTMLElement | undefined>(undefined);

/**终端输入实例对象 */
const terminal = ref<any>(null);

/**终端输入渲染 */
function handleRanderXterm(container: HTMLElement | undefined) {
  if (!container) return;
  const xterm = new Terminal({
    cols: props.cols,
    rows: props.rows,
    lineHeight: 1.2,
    fontSize: 12,
    fontFamily: "Monaco, Menlo, Consolas, 'Courier New', monospace",
    theme: {
      background: '#000000',
    },
    cursorBlink: true, // 光标闪烁
    cursorStyle: 'block',
    scrollback: 1000, // 设置历史缓冲区大小为 1000 行
    scrollSensitivity: 15,
    tabStopWidth: 4,
    disableStdin: true, // 禁止输入
  });
  // 挂载
  xterm.open(container);
  // 自适应尺寸
  const fitAddon = new FitAddon();
  xterm.loadAddon(fitAddon);
  // 终端尺寸变化触发
  xterm.onResize(({ cols, rows }) => {
    // console.log('尺寸', cols, rows);
    ws.send({
      requestId: `resize_${props.id}`,
      type: 'resize',
      data: { cols, rows },
    });
  });

  // 创建 ResizeObserver 实例
  var observer = new ResizeObserver(entries => {
    fitAddon.fit();
  });
  // 监听元素大小变化
  observer.observe(container);

  terminal.value = xterm;
}

/**连接打开后回调 */
function wsOpen(ev: any) {
  // console.info('wsOpen', ev);
  nextTick(() => {
    handleRanderXterm(terminalDom.value);
    // 连接事件
    emit('connect', {
      timeStamp: ev.timeStamp,
      cols: terminal.value.cols,
      rows: terminal.value.rows,
      neType: props.neType,
      neId: props.neId,
      id: props.id,
    });
  });
}

/**连接错误后回调 */
function wsError(ev: any) {
  console.error('wsError', ev);
  if (terminal.value != null) {
    let message = 'disconnected';
    terminal.value.write(`\x1b[31m${message}\x1b[m\r\n`);
  } else if (terminalDom.value) {
    terminalDom.value.style.background = '#000';
    terminalDom.value.style.color = '#ff4d4f';
    terminalDom.value.style.height = '60%';
    terminalDom.value.innerText = 'disconnected';
  }
}

/**连接关闭后回调 */
function wsClose(code: number) {
  // console.warn('wsClose', code);
  if (terminal.value != null) {
    let message = 'disconnected ' + code;
    terminal.value.write(`\x1b[31m${message}\x1b[m\r\n`);
  }
  // 关闭事件
  emit('close', {
    code: code,
    neType: props.neType,
    neId: props.neId,
    id: props.id,
  });
}

/**接收消息后回调 */
function wsMessage(res: Record<string, any>) {
  emit('message', res);
  // console.log('wsMessage', res);
  const { code, requestId, data } = res;
  if (code === RESULT_CODE_ERROR) {
    console.warn(res.msg);
    return;
  }
  if (!requestId) return;
  if (terminal.value != null) {
    let text = '';
    // 处理消息
    if (props.processMessages) {
      text = props.processMessages(data); 
    }else{
      text = processMessage(data);
    }
    // 无消息是则不输出
    if (text === '') {
      return;
    }
    terminal.value.write(text);
  }
}

/**终端消息处理*/
function processMessage(data: string): string {
  // 查找的开始输出标记
  const parts: string[] = data.split('\u001b[?2004l\r');
  if (parts.length > 0) {
    if (parts[0].startsWith('^C') || parts[0].startsWith('\r')) {
      return '';
    }
    let text = parts[parts.length - 1];
    // 找到最后输出标记
    let lestIndex = text.lastIndexOf('\u001b[?2004h\u001b]0;');
    if (lestIndex !== -1) {
      text = text.substring(0, lestIndex);
    }
    if (text === '' || text === '\r\n' || text.startsWith('^C\r\n')) {
      return '';
    }
    // 是否还有最后输出标记
    lestIndex = text.lastIndexOf('\u001b[?2004h');
    if (lestIndex !== -1) {
      text = text.substring(0, lestIndex);
    }
    // console.log({ parts, text });
    return text;
  }
  return data;
}

onMounted(() => {
  if (props.neType && props.neId) {
    // 建立链接
    const options: OptionsType = {
      url: props.url,
      params: {
        neType: props.neType,
        neId: props.neId,
        cols: props.cols,
        rows: props.rows,
      },
      onmessage: wsMessage,
      onerror: wsError,
      onopen: wsOpen,
      onclose: wsClose,
    };
    ws.connect(options);
  }
});

onBeforeUnmount(() => {
  if (ws.state() === WebSocket.OPEN) ws.close();
});

// 给组件设置属性 ref="xxxTerminal"
// setup内使用 const xxxTerminal = ref();
defineExpose({
  /**清除 */
  clear: () => {
    if (terminal.value != null) {
      terminal.value.clear();
    }
  },
  /**发送命令 */
  send: (type: string, data: Record<string, any>) => {
    ws.send({
      requestId: `${props.prefix}_${props.id}`,
      type,
      data,
    });
  },
  /**模拟按下 Ctrl+C */
  ctrlC: () => {
    ws.send({
      requestId: `${props.prefix}_${props.id}`,
      type: 'ctrl-c',
    });
  },
});
</script>

<template>
  <div ref="terminalDom" :id="id" class="terminal"></div>
</template>

<style lang="css" scoped>
.terminal {
  width: 100%;
  height: 100%;
}
</style>
