<script setup lang="ts">
import { computed, defineComponent, onMounted, ref, useTemplateRef } from 'vue';
import { message, Modal } from 'ant-design-vue';
import { debounce } from 'lodash-es';

import SvgIcon from '@/components/SvgIcon.vue';
import { useDictData } from '@/hooks/dict-data';
import { useRequest } from '@/hooks/request';
import { t } from '@/i18n';
import {
  addProtocolParam,
  batchDeleleProtocolParam,
  getProtocolParamList,
  getProtocolStandardParam,
  updateProtocolParam,
} from '@/api';
import { getTablePageSorts } from '@/utils';
import { DictCode, ProtocolRecognitionResult, ProtocolType } from '@/constants';

import CustomParams from './CustomParams.vue';
import GatewayParamExt from './GatewayParamExt.vue';
import SelectStandardParams from './SelectStandardParams.vue';

import type { FormInstance, TableProps } from 'ant-design-vue';
import type { DefaultOptionType, SelectValue } from 'ant-design-vue/es/select';
import type { Key } from 'ant-design-vue/es/table/interface';
import type {
  CheckedType,
  FormRules,
  PageParams,
  ProtocolBaseInfo,
  ProtocolCandidateResult,
  ProtocolParamInfo,
  ProtocolStandardParam,
} from '@/types';

interface Props {
  info: Partial<ProtocolBaseInfo>;
  isRecognized: boolean;
  disabledForm?: boolean;
}

const props = defineProps<Props>();

const VNodes = defineComponent({
  props: {
    vnodes: {
      type: Object,
      required: true,
    },
  },
  render() {
    return this.vnodes;
  },
});

const { dictData: protocolTypes, getDictData: getProtocolTypes } = useDictData(DictCode.AllProtocolType);
const { dictData: baudRateList, getDictData: getBaudRateList } = useDictData(DictCode.BaudRate);
const { dictData: readContinuousAddr, getDictData: getReadContinuousAddr } = useDictData(DictCode.ReadContinuousAddr);
const { dictData: byteOrderList, getDictData: getByteOrderList } = useDictData(DictCode.ByteOrder);
const { dictData: addrOrderList, getDictData: getAddrOrderList } = useDictData(DictCode.AddrOrder);

const isModbusRtuProtocol = computed(() => {
  return props.info.protocolType?.includes(ProtocolType.ModbusRTU);
});

const isModbusTcpProtocol = computed(() => {
  return props.info.protocolType?.includes(ProtocolType.ModbusTCP);
});

const isModbusProtocol = computed(() => {
  return isModbusRtuProtocol.value || isModbusTcpProtocol.value;
});

const isS7Protocol = computed(() => {
  return props.info.protocolType?.includes(ProtocolType.S7);
});

const formRef = ref<FormInstance>();

const rules = computed<FormRules<ProtocolBaseInfo>>(() => {
  return {
    protocolName: [
      {
        required: true,
        message: t('common.plzEnter', { name: t('setupProtocol.protocolName') }),
        trigger: 'blur',
      },
    ],
    protocolType: [
      {
        required: true,
        message: t('setupProtocol.plzSelectProtocolType'),
        trigger: 'blur',
      },
    ],
    deviceType: [
      {
        required: true,
        message: t('common.plzSelect', { name: t('setupProtocol.deviceType') }),
        trigger: 'blur',
      },
    ],
    dataBit: [
      {
        required: true,
        message: t('common.plzSelect', { name: t('setupProtocol.dataBit') }),
        trigger: 'blur',
      },
    ],
    parityBit: [
      {
        required: true,
        message: t('common.plzSelect', { name: t('setupProtocol.parityBit') }),
        trigger: 'blur',
      },
    ],
    stopBit: [
      {
        required: true,
        message: t('common.plzSelect', { name: t('setupProtocol.stopBit') }),
        trigger: 'blur',
      },
    ],
    baudRate: [
      {
        required: true,
        message: t('common.plzSelect', { name: t('setupProtocol.baudRate') }),
        trigger: 'blur',
      },
    ],
    dataSendInterval: [
      {
        required: true,
        message: t('common.plzEnter', { name: t('setupProtocol.dataSendInterval') }),
        trigger: 'blur',
      },
    ],
    highFreqSendInterval: [
      {
        required: true,
        message: t('common.plzEnter', { name: t('setupProtocol.highFreqSendInterval') }),
        trigger: 'blur',
      },
    ],
    readTimeout: [
      {
        required: true,
        message: t('common.plzEnter', { name: t('setupProtocol.readTimeout') }),
        trigger: 'blur',
      },
    ],
    nextDataReadDelay: [
      {
        required: true,
        message: t('common.plzEnter', { name: t('setupProtocol.nextDataReadDelay') }),
        trigger: 'blur',
      },
    ],
    nextRoundDataReadDelay: [
      {
        required: true,
        message: t('common.plzEnter', { name: t('setupProtocol.nextRoundDataReadDelay') }),
        trigger: 'blur',
      },
    ],
    byteOrder: [
      {
        required: true,
        message: t('common.plzSelect', { name: t('setupProtocol.byteOrder') }),
        trigger: 'blur',
      },
    ],
    addrOrder: [
      {
        required: true,
        message: t('common.plzSelect', { name: t('setupProtocol.addrOrder') }),
        trigger: 'blur',
      },
    ],
  };
});

const validateProtocolInfo = async () => {
  await formRef.value?.validate();
};

onMounted(() => {
  getProtocolTypes();
  getBaudRateList();
  getReadContinuousAddr();
  getByteOrderList();
  getAddrOrderList();
  getCurrentProtocolParams();
});

const handleReadContinuousAddrChange = (checked: CheckedType) => {
  const [first, second] = readContinuousAddr.value;

  if (checked === first?.dictValue) {
    props.info.readContinuousAddrCode = Number(first?.dictEngValue);
  } else {
    props.info.readContinuousAddrCode = Number(second?.dictEngValue);
  }
};

const handleByteOrderChange = (_value: SelectValue, option: DefaultOptionType) => {
  props.info.byteOrderCode = option.key;
};

const handleAddrOrderChange = (_value: SelectValue, option: DefaultOptionType) => {
  props.info.addrOrderCode = option.key;
};

const paramList = ref<ProtocolParamInfo[]>([]);
const paramTotal = ref(0);
const paramNameOrCode = ref('');
const pageParams = ref<PageParams>({
  pageIndex: 1,
  pageSize: 10,
  pageSorts: [
    {
      column: 'id',
      asc: true,
    },
  ],
});

const paramTableDataSource = computed(() => {
  return props.info.id ? paramList.value : filteredLocalParams.value;
});

const { isLoading, handleRequest } = useRequest();
let recognizeResult: string[] = [];

const getCurrentProtocolParams = () => {
  paramList.value = [];
  paramTotal.value = 0;

  handleRequest(async () => {
    const { id } = props.info;

    if (!id) {
      return;
    }

    const { records, total } = await getProtocolParamList({
      ...pageParams.value,
      baseInfoId: id,
      paramName: paramNameOrCode.value,
      paramCode: paramNameOrCode.value,
      recognizeResult,
    });

    paramList.value = records;
    paramTotal.value = total;
  });
};

const handleParamNameOrCodeChange = debounce(() => {
  filteredParamNameOrCode.value = paramNameOrCode.value;
  pageParams.value.pageIndex = 1;
  selectedParamIds.value = [];
  getCurrentProtocolParams();
}, 100);

const handleParamReset = () => {
  paramNameOrCode.value = '';
  handleParamNameOrCodeChange();
};

const handleParamTableChange: TableProps['onChange'] = (pagination, filters, sorter) => {
  recognizeResult = (filters.recognizeResult as string[] | null) || [];
  pageParams.value.pageIndex = pagination.current || 1;
  pageParams.value.pageSize = pagination.pageSize || 10;
  pageParams.value.pageSorts = getTablePageSorts(sorter);
  filteredParamNameOrCode.value = paramNameOrCode.value;
  selectedParamIds.value = [];
  getCurrentProtocolParams();
};

const selectedParamIds = ref<number[]>([]);

const handleParamSelectChange = (selectedRowKeys: Key[]) => {
  selectedParamIds.value = selectedRowKeys as number[];
};

const deleteSelectedParams = () => {
  Modal.confirm({
    title: t('setupProtocol.confirmDeleteParams'),
    async onOk() {
      try {
        if (props.info.id) {
          await batchDeleleProtocolParam(selectedParamIds.value);
        } else {
          deleteLocalParams();
        }

        selectedParamIds.value = [];
        message.success(t('setupProtocol.deleteParamsSuccessful'));

        pageParams.value.pageIndex = 1;
        getCurrentProtocolParams();
      } catch (err) {
        message.error((err as Error).message);
        console.error(err);
      }
    },
  });
};

const filterProtocolCandidateResult = (input: string, option: ProtocolCandidateResult) => {
  return option.platformProtocolGatewayParamName.toLowerCase().indexOf(input.toLowerCase()) >= 0;
};

const handleProtocolCandidateChange = (value: SelectValue, params: ProtocolParamInfo) => {
  handleRequest(async () => {
    const paramInfo = await getProtocolStandardParam(value as number);
    selectedParamId.value = params.id;
    selectedParamName.value = params.paramName;
    selectedParamInfo.value = params;
    selectStandardParamsRef.value?.setIsAddParams(false);
    selectStandardParamsRef.value?.setParamsForm(paramInfo);
    selectStandardParamsRef.value?.setSelectedParams(paramInfo);
    selectStandardParamsRef.value?.showFormView();
  });
};

const handleGatewayParamExtChange = debounce(
  async (
    ext: {
      paramName: string;
      paramCode: string;
      gatewayParamExt: string;
    },
    param: ProtocolParamInfo,
  ) => {
    const { paramName, paramCode, gatewayParamExt } = ext;

    try {
      await updateProtocolParam({
        ...param,
        id: param.id,
        gatewayParamName: paramName,
        gatewayParamCode: paramCode,
        gatewayParamExt,
      });

      getCurrentProtocolParams();
    } catch (err) {
      if (err instanceof Error) {
        message.error(err.message);
      }

      console.error(err);
    }
  },
  300,
);

const customParamsRef = useTemplateRef('customParams');
const selectStandardParamsRef = useTemplateRef('selectStandardParams');

const addCustomParams = () => {
  customParamsRef.value?.setIsAddParams(true);
  customParamsRef.value?.showView();
};

const addStandardParams = () => {
  selectedParamName.value = '';
  selectedParamInfo.value = undefined;
  selectStandardParamsRef.value?.setIsAddParams(true);
  selectStandardParamsRef.value?.showView();
};

const selectedParamId = ref<number>();
const selectedParamName = ref<string>('');
const selectedParamInfo = ref<ProtocolParamInfo>();

const changeToCustomParams = (params: ProtocolParamInfo) => {
  selectedParamId.value = params.id;
  customParamsRef.value?.setIsAddParams(false);
  customParamsRef.value?.showView();

  // 延时执行自定义参数表单的初始化，避免表单重置时会保留首次初始化的值，而不是默认的空值
  setTimeout(() => {
    customParamsRef.value?.setParamsForm(params);
  }, 0);
};

const changeToStandardParams = (params: ProtocolParamInfo) => {
  selectedParamId.value = params.id;
  selectedParamName.value = params.paramName;
  selectedParamInfo.value = params;
  selectStandardParamsRef.value?.setIsAddParams(false);
  selectStandardParamsRef.value?.showView();
};

const localParamList = ref<ProtocolParamInfo[]>([]);
const filteredParamNameOrCode = ref('');

const filteredLocalParams = computed(() => {
  const paramInput = filteredParamNameOrCode.value.toLowerCase();

  return localParamList.value.filter((item) => {
    return (
      item.platformParamName?.toLowerCase().indexOf(paramInput) >= 0 ||
      item.platformParamCode?.toLowerCase().indexOf(paramInput) >= 0 ||
      item.gatewayParamName?.toLowerCase().indexOf(paramInput) >= 0 ||
      item.gatewayParamCode?.toLowerCase().indexOf(paramInput) >= 0
    );
  });
});

const addLocalStandardParams = (params: ProtocolStandardParam[]) => {
  const allParamIds = localParamList.value.map((item) => item.id);
  const nonRepeatingParams = params.filter((item) => !allParamIds.includes(item.id));
  localParamList.value.push(...(nonRepeatingParams as unknown as ProtocolParamInfo[]));

  if (nonRepeatingParams.length < params.length) {
    message.info(t('setupProtocol.paramsHaveFilteredOut'));
  }
};

const editLocalStandardParam = (param: ProtocolStandardParam) => {
  const allParamIds = localParamList.value.map((item) => item.id);

  if (allParamIds.includes(param.id)) {
    message.error(t('setupProtocol.paramAlreadyExists'));
    return;
  }

  const selectedParamIndex = localParamList.value.findIndex((item) => item.id === selectedParamId.value);
  localParamList.value.splice(selectedParamIndex, 1, param as unknown as ProtocolParamInfo);
};

const addLocalCustomParam = (param: Partial<ProtocolParamInfo>) => {
  localParamList.value.push({
    ...param,
    id: Date.now(),
  } as ProtocolParamInfo);
};

const deleteLocalParams = () => {
  localParamList.value = localParamList.value.filter((item) => !selectedParamIds.value.includes(item.id));
};

const submitLocalParams = async () => {
  if (props.info.id) {
    for (const item of localParamList.value) {
      await addProtocolParam({
        ...item,
        baseInfoId: props.info.id,
      });
    }
  }
};

/**
 * 协议必须至少配置一条参数，此函数用于提供给外部进行判断是否可以提交协议的创建、修改等操作
 */
const isAtLeastOneParam = async () => {
  let isAtLeastOne: boolean;

  if (props.info.id) {
    const { total } = await getProtocolParamList({
      pageIndex: 1,
      pageSize: 1,
      baseInfoId: props.info.id,
    });

    isAtLeastOne = total > 0;
  } else {
    isAtLeastOne = localParamList.value.length > 0;
  }

  if (!isAtLeastOne) {
    throw new Error(t('setupProtocol.protocolAtLeastOneParam'));
  }
};

defineExpose({
  validateProtocolInfo,
  isAtLeastOneParam,
  submitLocalParams,
  getCurrentProtocolParams,
});
</script>

<template>
  <div>
    <AForm ref="formRef" :model="info" :rules="rules" layout="vertical" :disabled="disabledForm">
      <div class="protocol-label">{{ $t('common.basicInfo') }}</div>
      <ARow :gutter="33">
        <ACol flex="20%">
          <AFormItem :label="$t('setupProtocol.protocolName')" name="protocolName">
            <AInput v-model:value="info.protocolName" :placeholder="$t('common.plzEnter')" :disabled="false" />
          </AFormItem>
        </ACol>
        <ACol flex="20%">
          <AFormItem :label="$t('setupProtocol.protocolType')" name="protocolType">
            <ASelect v-model:value="info.protocolType" :placeholder="$t('common.plzSelect')" disabled>
              <ASelectOption v-for="item in protocolTypes" :key="item.dictValueId" :value="item.dictValue">
                {{ item.dictValue }}
              </ASelectOption>
            </ASelect>
          </AFormItem>
        </ACol>
        <ACol flex="20%">
          <AFormItem :label="$t('setupProtocol.deviceType')" name="deviceType">
            <ASelect v-model:value="info.deviceType" :placeholder="$t('common.plzSelect')" disabled>
              <!-- <ASelectOption :value=""></ASelectOption> -->
            </ASelect>
          </AFormItem>
        </ACol>
        <ACol v-if="isModbusRtuProtocol" flex="20%">
          <AFormItem :label="$t('setupProtocol.dataBit')" name="dataBit">
            <ARadioGroup v-model:value="info.dataBit">
              <ARadio :value="5">5</ARadio>
              <ARadio :value="6">6</ARadio>
              <ARadio :value="7">7</ARadio>
              <ARadio :value="8">8</ARadio>
            </ARadioGroup>
          </AFormItem>
        </ACol>
        <ACol v-if="isModbusRtuProtocol" flex="20%">
          <AFormItem :label="$t('setupProtocol.parityBit')" name="parityBit">
            <ARadioGroup v-model:value="info.parityBit">
              <ARadio value="N">N</ARadio>
              <ARadio value="O">O</ARadio>
              <ARadio value="E">E</ARadio>
            </ARadioGroup>
          </AFormItem>
        </ACol>
        <ACol v-if="isModbusRtuProtocol" flex="20%">
          <AFormItem :label="$t('setupProtocol.stopBit')" name="stopBit">
            <ARadioGroup v-model:value="info.stopBit">
              <ARadio value="1">1</ARadio>
              <ARadio value="1.5">1.5</ARadio>
              <ARadio value="2">2</ARadio>
            </ARadioGroup>
          </AFormItem>
        </ACol>
        <ACol v-if="isModbusRtuProtocol" flex="20%">
          <AFormItem :label="$t('setupProtocol.baudRate')" name="baudRate">
            <ASelect v-model:value="info.baudRate" :placeholder="$t('common.plzSelect')">
              <ASelectOption v-for="item in baudRateList" :key="item.dictValueId" :value="Number(item.dictValue)">
                {{ item.dictValue }}
              </ASelectOption>
            </ASelect>
          </AFormItem>
        </ACol>
        <ACol flex="20%">
          <AFormItem :label="$t('setupProtocol.dataSendInterval')" name="dataSendInterval">
            <AInputNumber
              v-model:value="info.dataSendInterval"
              :placeholder="$t('common.plzEnter')"
              addon-after="s"
              :min="0"
              :precision="0"
            />
          </AFormItem>
        </ACol>
        <ACol flex="20%">
          <AFormItem :label="$t('setupProtocol.highFreqSendInterval')" name="highFreqSendInterval">
            <AInputNumber
              v-model:value="info.highFreqSendInterval"
              :placeholder="$t('common.plzEnter')"
              addon-after="s"
              :min="0"
              :precision="0"
            />
          </AFormItem>
        </ACol>
        <ACol flex="20%">
          <AFormItem :label="$t('setupProtocol.readTimeout')" name="readTimeout">
            <AInputNumber
              v-model:value="info.readTimeout"
              :placeholder="$t('common.plzEnter')"
              addon-after="ms"
              :min="0"
              :precision="0"
            />
          </AFormItem>
        </ACol>
        <ACol flex="20%">
          <AFormItem :label="$t('setupProtocol.nextDataReadDelay')" name="nextDataReadDelay">
            <AInputNumber
              v-model:value="info.nextDataReadDelay"
              :placeholder="$t('common.plzEnter')"
              addon-after="ms"
              :min="0"
              :precision="0"
            />
          </AFormItem>
        </ACol>
        <ACol flex="20%">
          <AFormItem :label="$t('setupProtocol.nextRoundDataReadDelay')" name="nextRoundDataReadDelay">
            <AInputNumber
              v-model:value="info.nextRoundDataReadDelay"
              :placeholder="$t('common.plzEnter')"
              addon-after="ms"
              :min="0"
              :precision="0"
            />
          </AFormItem>
        </ACol>
        <ACol v-if="!isS7Protocol" flex="20%">
          <AFormItem :label="$t('setupProtocol.readContinuousAddr')" name="readContinuousAddr">
            <ASwitch
              v-model:checked="info.readContinuousAddr"
              :checked-value="readContinuousAddr[0]?.dictValue"
              :un-checked-value="readContinuousAddr[1]?.dictValue"
              @change="handleReadContinuousAddrChange"
            />
          </AFormItem>
        </ACol>
        <ACol v-if="!isS7Protocol" flex="20%">
          <AFormItem :label="$t('setupProtocol.readContinuousAddrLength')" name="readContinuousAddrLength">
            <AInputNumber
              v-model:value="info.readContinuousAddrLength"
              :placeholder="$t('common.plzEnter')"
              :min="0"
              :precision="0"
            />
          </AFormItem>
        </ACol>
      </ARow>
      <div v-if="!isS7Protocol" class="protocol-label advanced-label">{{ $t('common.advancedSettings') }}</div>
      <ARow v-if="!isS7Protocol" :gutter="33">
        <ACol flex="20%">
          <AFormItem :label="$t('setupProtocol.byteOrder')" name="byteOrder">
            <ASelect
              v-model:value="info.byteOrder"
              :placeholder="$t('common.plzSelect')"
              @change="handleByteOrderChange"
            >
              <ASelectOption v-for="item in byteOrderList" :key="item.dictEngValue" :value="item.dictValue">
                {{ item.dictValue }}
              </ASelectOption>
            </ASelect>
          </AFormItem>
        </ACol>
        <ACol flex="20%">
          <AFormItem :label="$t('setupProtocol.addrOrder')" name="addrOrder">
            <ASelect
              v-model:value="info.addrOrder"
              :placeholder="$t('common.plzSelect')"
              @change="handleAddrOrderChange"
            >
              <ASelectOption v-for="item in addrOrderList" :key="item.dictEngValue" :value="item.dictValue">
                {{ item.dictValue }}
              </ASelectOption>
            </ASelect>
          </AFormItem>
        </ACol>
      </ARow>
    </AForm>
    <div class="protocol-label params-label">
      {{ $t('setupProtocol.protocolParams') }}
      <span class="protocol-params-tip">
        <SvgIcon name="info-cirlce-o" />
        {{ $t('setupProtocol.protocolTypeTipForPlc') }}
      </span>
    </div>
    <div class="protocol-params-query">
      <div>
        <AInput
          v-model:value="paramNameOrCode"
          class="protocol-params-input"
          :placeholder="$t('setupProtocol.plzEnterParamOrCode')"
          allow-clear
        >
          <template #prefix>
            <SvgIcon name="search-o" :size="12" color="#B2B2B2" />
          </template>
        </AInput>
        <AButton type="primary" @click="handleParamNameOrCodeChange">{{ $t('common.query') }}</AButton>
        <AButton @click="handleParamReset">{{ $t('common.reset') }}</AButton>
      </div>
      <div>
        <AButton danger :disabled="selectedParamIds.length === 0" @click="deleteSelectedParams">
          {{ $t('setupProtocol.deleteSelected') }}
        </AButton>
        <AButton @click="addCustomParams">{{ $t('setupProtocol.addCustomParams') }}</AButton>
        <AButton @click="addStandardParams" type="primary">
          {{ $t('setupProtocol.addStandardParams') }}
        </AButton>
      </div>
    </div>
    <ATable
      class="hvac-table"
      :data-source="paramTableDataSource"
      row-key="id"
      :row-selection="{ selectedRowKeys: selectedParamIds, onChange: handleParamSelectChange }"
      :scroll="{ x: 4000 }"
      :loading="isLoading"
      :pagination="{
        current: pageParams.pageIndex,
        pageSize: pageParams.pageSize,
        total: paramTotal,
        showSizeChanger: true,
        hideOnSinglePage: false,
        showTotal: (total) => $t('common.pageTotal', { total }),
      }"
      @change="handleParamTableChange"
    >
      <ATableColumn
        v-if="isRecognized"
        key="paramName"
        :title="$t('setupProtocol.protocolParamFields.customParamName')"
        data-index="paramName"
        :width="180"
        fixed="left"
      />
      <ATableColumn
        v-if="isRecognized"
        key="recognizeResult"
        :title="$t('setupProtocol.protocolParamFields.result')"
        data-index="recognizeResult"
        :width="110"
        fixed="left"
        :sorter="true"
        :filters="[
          { text: $t('setupProtocol.recognizeSuccess'), value: ProtocolRecognitionResult.Success },
          { text: $t('setupProtocol.recognizeMultipleResults'), value: ProtocolRecognitionResult.MultipleResults },
          { text: $t('setupProtocol.recognizeFailed'), value: ProtocolRecognitionResult.Unrecognized },
          { text: $t('setupProtocol.recognizeNull'), value: '-' },
        ]"
      >
        <template #default="{ record }">
          <div
            v-if="record.recognizeResult"
            :class="[
              'protocol-recognize-result',
              {
                'protocol-recognize-success': record.recognizeResult === ProtocolRecognitionResult.Success,
                'protocol-recognize-unrecognized': record.recognizeResult === ProtocolRecognitionResult.Unrecognized,
                'protocol-recognize-multiple-results':
                  record.recognizeResult === ProtocolRecognitionResult.MultipleResults,
              },
            ]"
          >
            {{ record.recognizeResult }}
          </div>
        </template>
      </ATableColumn>
      <ATableColumn
        class="table-column-item-required"
        :title="$t('setupProtocol.protocolParamFields.platformParamName')"
        data-index="platformParamName"
        :width="188"
        fixed="left"
      >
        <template #default="{ record }">
          <a v-if="record.platformParamName && !record.candidateResults" @click="changeToStandardParams(record)">
            {{ record.platformParamName }}
          </a>
          <ADropdown v-else-if="record.recognizeResult === ProtocolRecognitionResult.Unrecognized">
            <template #overlay>
              <AMenu>
                <AMenuItem key="select" @click="changeToStandardParams(record)">
                  {{ $t('setupProtocol.selectFromProtocolLibrary') }}
                </AMenuItem>
                <AMenuItem key="custom" @click="changeToCustomParams(record)">
                  {{ $t('common.custom') }}
                </AMenuItem>
              </AMenu>
            </template>
            <AButton class="manually-match-button">
              {{ $t('setupProtocol.manuallyMatch') }}
              <SvgIcon name="down" />
            </AButton>
          </ADropdown>
          <ASelect
            v-else-if="record.recognizeResult === ProtocolRecognitionResult.MultipleResults"
            class="multiple-results-select"
            :value="record.platformParamName || undefined"
            :options="JSON.parse(record.candidateResults || '[]')"
            :field-names="{ label: 'platformProtocolGatewayParamName', value: 'platformProtocolId' }"
            :dropdown-match-select-width="176"
            :placeholder="$t('common.plzSelect')"
            show-search
            :filter-option="filterProtocolCandidateResult"
            @change="handleProtocolCandidateChange($event, record)"
          >
            <template #dropdownRender="{ menuNode: menu }">
              <div class="multiple-results-select-header">
                <div class="multiple-results-select-button" @mousedown="changeToStandardParams(record)">
                  {{ $t('setupProtocol.selectFromProtocolLibrary') }}
                </div>
                <div class="multiple-results-select-button" @mousedown="changeToCustomParams(record)">
                  {{ $t('common.custom') }}
                </div>
              </div>
              <VNodes :vnodes="menu" />
            </template>
          </ASelect>
        </template>
      </ATableColumn>
      <ATableColumn
        class="table-column-item-required"
        :title="$t('setupProtocol.protocolParamFields.platformParamCode')"
        data-index="platformParamCode"
        :width="280"
      />
      <ATableColumn
        class="table-column-item-required"
        :title="$t('setupProtocol.protocolParamFields.gatewayParamName')"
        data-index="gatewayParamName"
        :width="270"
      >
        <template #default="{ record }">
          <GatewayParamExt
            v-if="record.gatewayParamExt"
            :ext-info="JSON.parse(record.gatewayParamExt || '{}')"
            @change="handleGatewayParamExtChange($event, record)"
          />
          <div v-else>{{ record.gatewayParamName }}</div>
        </template>
      </ATableColumn>
      <ATableColumn
        class="table-column-item-required"
        :title="$t('setupProtocol.protocolParamFields.gatewayParamCode')"
        data-index="gatewayParamCode"
        :width="200"
      />
      <ATableColumn :title="$t('setupProtocol.protocolParamFields.unit')" data-index="unit" />
      <!-- <ATableColumn :title="$t('setupProtocol.protocolParamFields.module')" data-index="module" /> -->
      <ATableColumn :title="$t('setupProtocol.protocolParamFields.groupName')" data-index="groupType" />
      <ATableColumn :title="$t('setupProtocol.protocolParamFields.readWriteType')" data-index="readWriteType" />
      <ATableColumn
        v-if="isS7Protocol"
        class="table-column-item-required"
        :title="$t('setupProtocol.protocolParamFields.registerType')"
        data-index="registerType"
      />
      <ATableColumn
        v-if="isS7Protocol"
        class="table-column-item-required"
        :title="$t('setupProtocol.protocolParamFields.addrNumber')"
        data-index="addrNumber"
      />
      <ATableColumn
        v-if="isModbusProtocol"
        class="table-column-item-required"
        :title="$t('setupProtocol.protocolParamFields.readFunctionCode')"
        data-index="readFuncCode"
      />
      <ATableColumn
        v-if="isModbusProtocol"
        :title="$t('setupProtocol.protocolParamFields.writeFunctionCode')"
        data-index="writeFuncCode"
      />
      <ATableColumn
        class="table-column-item-required"
        :title="$t('setupProtocol.protocolParamFields.registerAddress')"
        data-index="registerAddr"
      />
      <ATableColumn
        :title="$t('setupProtocol.protocolParamFields.parsingType')"
        data-index="parsingType"
        :width="150"
      />
      <ATableColumn
        v-if="isS7Protocol"
        :title="$t('setupProtocol.protocolParamFields.wordLength')"
        data-index="wordLength"
      />
      <ATableColumn
        v-if="isS7Protocol"
        :title="$t('setupProtocol.protocolParamFields.quantity')"
        data-index="quantity"
      />
      <ATableColumn
        v-if="isModbusProtocol"
        :title="$t('setupProtocol.protocolParamFields.addressLength')"
        data-index="addrLength"
      />
      <ATableColumn :title="$t('setupProtocol.protocolParamFields.coefficient')" data-index="coefficient" />
      <ATableColumn
        :title="$t('setupProtocol.protocolParamFields.isHighFrequencyParameter')"
        data-index="isHighFreqParam"
        :width="150"
      />
      <ATableColumn
        :title="$t('setupProtocol.protocolParamFields.writeCalculationFormula')"
        data-index="writeCalcFormula"
      />
      <ATableColumn
        :title="$t('setupProtocol.protocolParamFields.readCalculationFormula')"
        data-index="readCalcFormula"
      />
      <ATableColumn :title="$t('setupProtocol.protocolParamFields.decimalPlaces')" data-index="decimalPlace" />
      <ATableColumn :title="$t('setupProtocol.protocolParamFields.bitAddress1')" data-index="bitAddress1" />
      <ATableColumn :title="$t('setupProtocol.protocolParamFields.bitAddress1Status')" data-index="bitAddress1Status" />
      <ATableColumn :title="$t('setupProtocol.protocolParamFields.bitAddress1Detail')" data-index="bitAddress1Detail" />
      <ATableColumn :title="$t('setupProtocol.protocolParamFields.bitAddress2')" data-index="bitAddress2" />
      <ATableColumn :title="$t('setupProtocol.protocolParamFields.bitAddress2Status')" data-index="bitAddress2Status" />
      <ATableColumn :title="$t('setupProtocol.protocolParamFields.bitAddress2Detail')" data-index="bitAddress2Detail" />
      <ATableColumn :title="$t('setupProtocol.protocolParamFields.bitAddress3')" data-index="bitAddress3" />
      <ATableColumn :title="$t('setupProtocol.protocolParamFields.bitAddress3Status')" data-index="bitAddress3Status" />
      <ATableColumn :title="$t('setupProtocol.protocolParamFields.bitAddress3Detail')" data-index="bitAddress3Detail" />
    </ATable>
    <CustomParams
      ref="customParams"
      :protocol-id="info.id"
      :param-id="selectedParamId"
      :is-modbus-rtu-protocol="isModbusRtuProtocol"
      :is-modbus-tcp-protocol="isModbusTcpProtocol"
      :is-s7-protocol="isS7Protocol"
      @add-local-param="addLocalCustomParam"
      @refresh-list="getCurrentProtocolParams"
    />
    <SelectStandardParams
      ref="selectStandardParams"
      :device-type="info.deviceType"
      :protocol-id="info.id"
      :param-id="selectedParamId"
      :param-name="selectedParamName"
      :param-info="selectedParamInfo"
      :is-modbus-rtu-protocol="isModbusRtuProtocol"
      :is-modbus-tcp-protocol="isModbusTcpProtocol"
      :is-s7-protocol="isS7Protocol"
      @add-local-params="addLocalStandardParams"
      @edit-local-param="editLocalStandardParam"
      @refresh-list="getCurrentProtocolParams"
    />
  </div>
</template>

<style lang="scss" scoped>
:deep(.ant-form) {
  .ant-form-item {
    width: 192px;
  }

  .ant-input-number-group .ant-input-number-group-addon {
    padding-inline: 14px;
    color: #333;
    background-color: initial;
  }

  .ant-input-number-disabled + .ant-input-number-group-addon {
    background-color: rgb(0 0 0 / 2%);
  }

  .ant-radio-wrapper span.ant-radio + * {
    padding-inline: 6px;
  }

  .ant-input-number {
    width: 100%;
  }

  .ant-switch {
    height: 24px;
  }

  .ant-switch-handle {
    width: 20px;
    height: 20px;
  }

  .ant-switch.ant-switch-checked .ant-switch-handle {
    inset-inline-start: calc(100% - 22px);
  }
}

.protocol-label {
  padding-left: 8px;
  margin-bottom: 24px;
  font-size: 16px;
  font-weight: 500;
  line-height: 24px;
  color: #000;
  border-left: 2px solid var(--antd-color-primary);

  &.advanced-label,
  &.params-label {
    margin-top: 16px;
  }

  &.params-label {
    display: flex;
    align-items: center;
  }
}

.protocol-params-input {
  width: 256px;
  margin-right: 40px;
}

.protocol-params-tip {
  display: inline-flex;
  align-items: center;
  height: 22px;
  font-size: 12px;
  font-weight: 500;
  line-height: 22px;
  color: #b2b2b2;

  i {
    margin-right: 5px;
    margin-left: 16px;
  }
}

.protocol-params-query {
  display: flex;
  justify-content: space-between;
  margin-bottom: 16px;

  > div:first-child button + button {
    margin-left: 12px;
  }

  > div:last-child button + button {
    margin-left: 16px;
  }
}

.protocol-recognize-result {
  height: 24px;
  padding: 2px 8px;
  font-size: 12px;
  line-height: 20px;
  text-align: center;
  border: 1px solid var(--antd-color-border);
  border-radius: 4px;

  &.protocol-recognize-success {
    color: #52c41a;
    background: #f6ffed;
    border-color: #b7eb8f;
  }

  &.protocol-recognize-unrecognized {
    color: #f56c6c;
    background: #fff1f0;
    border-color: #ffa39e;
  }

  &.protocol-recognize-multiple-results {
    color: var(--antd-color-primary);
    background: var(--antd-color-primary-opacity-10);
    border-color: var(--antd-color-primary-opacity-60);
  }
}

.manually-match-button {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  color: #333;

  i {
    color: #8c94a8;
  }

  &:hover {
    color: #333;
  }
}

.multiple-results-select {
  width: 100%;
}

.multiple-results-select-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px;
  padding-top: 8px;
  margin-bottom: 8px;
  border-bottom: 1px solid #e4e7ed;

  .multiple-results-select-button {
    height: 24px;
    padding: 1px 8px;
    font-size: 14px;
    line-height: 22px;
    text-shadow: 0 -2px 12px rgb(0 0 0 / 10%);
    cursor: pointer;
    border-radius: 4px;
    box-shadow: 0 -2px 12px 0 rgb(0 0 0 / 10%);

    &:first-child {
      color: #fff;
      background: var(--antd-color-primary);
    }

    &:last-child {
      color: var(--antd-color-primary);
      background: #fff;
      border: 1px solid var(--antd-color-primary);
    }
  }
}
</style>
