package com.brilliance.isc.doc.convert;

import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import com.brilliance.dzxt.framework.Result;
import com.brilliance.dzxt.packet.model.Desc;
import com.brilliance.dzxt.utils.FastJsonUtil;
import com.brilliance.dzxt.utils.MsgUtil;
import com.brilliance.mda.runtime.mda.config.IniConfig;
import com.brilliance.mda.runtime.mda.util.Inis;
import com.brilliance.mda.runtime.mda.util.MdaUtils;
import com.brilliance.swift.SwiftTransfer;
import com.brilliance.swift.exception.CommonRuntimeException;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
import com.brilliance.isc.bo.Ack;
import com.brilliance.isc.bo.Bnl;
import com.brilliance.isc.bo.Pta;
import com.brilliance.isc.bo.Smh;
import com.brilliance.isc.common.cache.pta.PtaCacheService;
import com.brilliance.isc.common.component.PtsptaToolComponent;
import com.brilliance.isc.common.context.AppEnv;
import com.brilliance.isc.common.context.SettleContext;
import com.brilliance.isc.common.context.SpringEnvContext;
import com.brilliance.isc.common.doc.TextResultVo;
import com.brilliance.isc.common.exception.CommonServiceException;
import com.brilliance.isc.common.tools.service.ObjectStroeToolService;
import com.brilliance.isc.common.transaction.vo.BaseTransactionVo;
import com.brilliance.isc.common.trndoc.vo.DoceotVo;
import com.brilliance.isc.common.util.StringUtils;
import com.brilliance.isc.common.util.Utils;
import com.brilliance.isc.common.vo.PtsptaVo;
import com.brilliance.isc.constant.ErrorCode;
import com.brilliance.isc.doc.constant.DocConstant;
import com.brilliance.isc.doc.handle.impl.common.SimpleTextProvider;
import com.brilliance.isc.doc.service.impl.DocCommonService;
import com.brilliance.isc.doc.utils.JSONXMLParserSSTF;
import com.brilliance.isc.doc.utils.XmlTransformerUtil;
import com.brilliance.isc.exception.SSTFException;
import com.brilliance.isc.mda.dao.AckMapper;
import com.brilliance.isc.mda.dao.BnlMapper;
import com.brilliance.isc.swift.objmtconvert.annotation.FieldDescription;
import com.brilliance.isc.swift.objmtconvert.annotation.TagName;
import com.brilliance.isc.swift.objmtconvert.manager.MtCreatorManager;
import com.brilliance.isc.swift.objmtconvert.model.AbsMTCreator;
import com.brilliance.isc.swift.objmtconvert.model.commontag.TagResult;
import com.brilliance.isc.swift.objmtconvert.model.mt9xx.MT999Creator;
import com.brilliance.isc.swift.objmtconvert.result.MtResult;
import com.brilliance.isc.swift.objmtconvert.result.SwtExpandable;
import com.brilliance.isc.swift.objmtconvert.util.FormatUtils;
import com.brilliance.isc.swift.objmtconvert.util.SSTFGson;
import com.brilliance.isc.swift.swifteditor.tag.message.AbstractMessageArea;
import com.brilliance.isc.swift.swifteditor.tag.message.MessageFormat;
import com.brilliance.isc.swift.swifteditor.tag.message.SEQList;
import com.brilliance.isc.swift.swifteditor.tag.message.SWFMessage;
import com.brilliance.isc.exception.RestBizException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.itextpdf.text.pdf.BaseFont;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang.StringEscapeUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ResolvableType;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.w3c.dom.*;
import org.xhtmlrenderer.pdf.ITextFontResolver;
import org.xhtmlrenderer.pdf.ITextRenderer;
import sun.misc.BASE64Decoder;

import javax.annotation.PostConstruct;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * 对应原makeAndConvert处理
 * 现在只实现了999的转换，后续可以按照情况
 */
@Component
public class ResultConvert {

    private final String SWIFTZ_CHARS = " \r\n!\"#%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz{";
    private final Set<Character> SWIFTZ_SET = new HashSet<>();
    //SWIFT 换行符必须为\r\n
    private String CR = "\r\n";
    private int MAX_LINE_150 = 150;

    private static final String FONT = "cour.ttf";

    private String SWT_PDF_TEMPLATE_NAME = "SWT_PDF";

    private String SWT_PURE_PDF_TEMPLATE_NAME = "SWT_PURE_PDF";

    private Pattern pattern = Pattern.compile("_\\d+");

    private final Logger logger = LoggerFactory.getLogger(ResultConvert.class);
    @Autowired
    private AckMapper ackMapper;

    @Autowired
    private TagCodeFormater tagCodeFormater;

    @Autowired
    private MTCreatorSplitor mtCreatorSplitor;

    @Autowired
    private Properties xmlDocProperties;

    @Autowired
    private Properties xmlDocPropertiesForFmt;

    @Autowired
    private AppEnv appEnv;

    @Autowired
    private ObjectStroeToolService objectStroeToolService;

    @Autowired
    private PtaCacheService ptaCacheService;

    @Autowired
    private BnlMapper bnlMapper;

    @Autowired
    private IniConfig erxIni;
    @Autowired
    private IniConfig erxValIni;
    @Autowired
    private IniConfig elcIni;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private DocCommonService docCommonService;

    private static final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    private static DocumentBuilder builder;


    @PostConstruct
    public void buildSWIFTZ() {
        for (char c : SWIFTZ_CHARS.toCharArray()) {
            Character character = c;
            SWIFTZ_SET.add(character);
        }
        try {
            builder = factory.newDocumentBuilder();
        } catch (ParserConfigurationException e) {
            logger.error("init builder error", e);
        }
    }

    public TextResultVo convert(BaseTransactionVo baseTransactionVo, DoceotVo doceotVo, TextResultVo textResultVo) {
        //有沟通、但无密押
        if (DocConstant.COR_TYP_SWT.equals(doceotVo.getCortyp()) && "C".equals(doceotVo.getAdrbicaut())) {
            String message = convertSwift999ToMessage((AbsMTCreator) textResultVo.getAttach(), textResultVo);
            textResultVo.setData(message);
        }
        //换行符替换为\r\n
        if (DocConstant.COR_TYP_SWT.equals(doceotVo.getCortyp())) {
            String message = textResultVo.getData();
            if (!MdaUtils.isEmpty(message)) {
                message = message.replaceAll("[\\r]?\\n", CR);
            }
            textResultVo.setData(message);
            if (!MdaUtils.isEmpty(textResultVo.getAttach())) {
                //更新MT报文类型
                doceotVo.setMt(((AbsMTCreator) textResultVo.getAttach()).getMsgType());
            } else {
                //直接从报文block2中截取报文类型
                if (!MdaUtils.isEmpty(message)) {
                    int index = message.indexOf("{2:");
                    doceotVo.setMt(message.substring(index + 4, index + 7));
                }

            }
            showSwift(textResultVo);
        } else if (DocConstant.COR_TYP_ERX.equals(doceotVo.getCortyp())) {
            // 如果是保存或暂存，则不需要格式化
            if (SettleContext.isSavTransSta() || SettleContext.isPedTransSta()) {
                return textResultVo;
            }
            // xml报文格式化
            showPlainErxMessage(textResultVo);
        } else if (DocConstant.COR_TYP_ELC.equals(doceotVo.getCortyp())) {
            //电证系列报文处理
            if (SettleContext.isSavTransSta()) {
                Result elcResult = (Result) textResultVo.getAttach();
                if (!elcResult.isSuccess()) {
                    throw new RestBizException("S00001", "电证报文格式不正确，请检查！");
                }
            }
            //如果是保存或暂存，则不需要格式化
            if (SettleContext.isSavTransSta() || SettleContext.isPedTransSta()) {
//                String elcJson = elcResult.getElcsModelForJson();
//                //更新为JSON
//                textResultVo.setData(elcJson);
                textResultVo.setExtension(DocConstant.XML);
                return textResultVo;
            }
            showElcsModel(textResultVo);
        } else if (DocConstant.COR_TYP_FMT.equals(doceotVo.getCortyp())) {
            // 如果是保存或暂存，则不需要格式化
            if (SettleContext.isSavTransSta() || SettleContext.isPedTransSta()) {
                return textResultVo;
            }
            // fmt报文格式化
            showPlainFmtMessage(textResultVo);
        }
        return textResultVo;
    }

    private void checkSwiftMessage(String mt, String message, SWFMessageObject swfMessageObject) {

        //校验值存在起头冒号的情况
        StringJoiner sj = new StringJoiner(";");
        if (!MdaUtils.isEmpty(swfMessageObject.getTagDespList())) {
            swfMessageObject.getTagDespList().forEach(tagDesp -> {
                if (!MdaUtils.isEmpty(tagDesp.getValue())) {
                    String[] lines = tagDesp.getValue().split("\r?\n");
                    for (int i = 0; i < lines.length; i++) {
                        if (lines[i].startsWith(":")) {
                            sj.add(tagDesp.tag + "的第" + (i + 1) + "行存在起头冒号");
                        } else if (lines[i].startsWith("-")) {
                            sj.add(tagDesp.tag + "的第" + (i + 1) + "行存在起头减号");
                        }
                    }
                }
            });
        }

        LinkedHashSet<Character> errorChars = new LinkedHashSet<Character>();
        int begIdx = message.indexOf("{4:");
        int endIdx = message.lastIndexOf("}");
        String block4 = message.substring(begIdx + 3, endIdx);
        for (char c : block4.toCharArray()) {
            Character character = c;
            if (!SWIFTZ_SET.contains(character)) {
                errorChars.add(character);
            }
        }

        StringJoiner errorStrJoiner = new StringJoiner(",");
        errorChars.stream().forEach(character -> errorStrJoiner.add(character.toString()));
        String errorMessage = mt + "报文中存在不合法的字符:" + errorStrJoiner + "，请检查输入内容";
        boolean hasError = !errorChars.isEmpty() || sj.length() > 0;
        if (sj.length() > 0 && !errorChars.isEmpty()) {
            errorMessage = sj.toString() + ";" + errorMessage;
        } else if (sj.length() > 0) {
            errorMessage = sj.toString();
        }
        if (SettleContext.isSavTransSta() && hasError) {
            throw new RestBizException("S00001", errorMessage);
        } else if (SettleContext.isViewDocTransSta() && hasError) {
            //TODO 给预览时前端一个提示
            swfMessageObject.setValidateMessage(errorMessage);
        }
    }

    /**
     * 优化E结算类报文的key翻译（未来所有E结算的报文码表都用ersovr.ini文件，后续把ypt的报文种类维护进去）
     */
    private void fillErxFormatData(TextResultVo textResultVo, String data) {
        JSONObject jsonObject1 = new JSONObject(true);
        JSONObject jsonObject = JSONXMLParserSSTF.toJSONObject(jsonObject1, data, false);
        JSONObject rootObject = jsonObject.getJSONObject("ROOT");
        JSONObject msgHead = rootObject.getJSONObject("MsgHead");
        List<TagDesp> despList = new ArrayList<>();
        //头的字段映射还是用xmldoc里面的配置
        fillDespListForXml("E-HEAD", msgHead, false, despList, erxIni, xmlDocProperties);
        JSONObject msgBody = rootObject.getJSONObject("MsgBody");
        String type = msgHead.getStr("MSGTYP");
        if (StringUtils.isEmpty(type)) {
            type = msgHead.getStr("msgtyp");
        }
        String sectionStr = "E-" + type;
        fillDespListForXml(sectionStr, msgBody, true, despList, erxIni, xmlDocProperties);
        textResultVo.setFormatData(despList);
    }

    private void fillDespListForXml(String sectionStr, JSONObject rawMsg, boolean isBody, List<TagDesp> despList, IniConfig config, Properties defaultCodeTable) {
        Properties template;
        if (isBody) {
            TagDesp tagDesp = new TagDesp();
            tagDesp.setTag("#BODY#");
            despList.add(tagDesp);
        }
        Map section = config.getBlock(sectionStr);
        Map<String, String> common = config.getBlock("E-COMMON");
        template = sectionToProperties(section);
        if (template == null || template.isEmpty()) {
            //降级使用通用配置
            template = defaultCodeTable;
        }
        fillDespList(template, common, rawMsg, despList);
    }

    private void fillFormatDataForGnz(TextResultVo textResultVo, String data) {
        JSONObject jsonObject1 = new JSONObject(true);
        JSONObject jsonObject = JSONXMLParserSSTF.toJSONObject(jsonObject1, data, false);
        JSONObject rootObject = jsonObject.getJSONObject("ROOT");
        JSONObject msgHead = rootObject.getJSONObject("MsgHead");
        String type = msgHead.getStr("MSGTYP");
        if (StringUtils.isEmpty(type)) {
            type = msgHead.getStr("msgtyp");
        }
        JSONObject msgBody = rootObject.getJSONObject("MsgBody");
        List<TagDesp> despList = new ArrayList<>();
        fillDespListForGnz(msgHead, type, despList);
        TagDesp tagDesp = new TagDesp();
        tagDesp.setTag("#BODY#");
        despList.add(tagDesp);
        fillDespListForGnz(msgBody, type, despList);
        textResultVo.setFormatData(despList);
    }

    private void fillDespList(Properties xmlProperties, Map<String, String> common, JSONObject msgHead, List<TagDesp> despList) {
        fillDespList(xmlProperties, common, msgHead, despList, 0);
    }

    /**
     * 国内证来报格式化使用(避免和E结算配置冲突)
     */
    private void fillDespListForGnz(JSONObject msgHead, String type, List<TagDesp> despList) {
        fillDespListForGnz(msgHead, type, despList, 0);
    }

    private void fillDespList(Properties xmlProperties, Map<String, String> common, JSONObject msgHead, List<TagDesp> despList, int lvl) {
        for (String tag : msgHead.keySet()) {
            TagDesp tagDesp = new TagDesp();
            String desp = getFullKey(xmlProperties, common, tag);
            Object value = msgHead.get(tag);
            tagDesp.setTag(tag);
            tagDesp.setDesp(desp);
            tagDesp.setLvl(lvl);
            if (value instanceof JSONObject) {
                tagDesp.setValue("[Object]");
                despList.add(tagDesp);
                fillDespList(xmlProperties, common, (JSONObject) value, despList, lvl + 1);
            } else if (value instanceof JSONArray) {
                tagDesp.setValue("[Object]");
                despList.add(tagDesp);
                JSONArray jsonArray = (JSONArray) value;
                for (int i = 0; i < jsonArray.size(); i++) {
                    JSONObject jsonObject = jsonArray.getJSONObject(i);
                    this.fillDespList(xmlProperties, common, jsonObject, despList, lvl + 1);
                }
            } else {
                String valueStr = msgHead.getStr(tag);
                valueStr = getErsTagVal(tag, valueStr);
                tagDesp.setValue(valueStr);
                despList.add(tagDesp);
            }
        }
    }

    /**
     * 对E结算展示报文的值做映射
     */
    private String getErsTagVal(String tag, String valueStr) {
        Map<String, String> block = erxValIni.getBlock(tag.toUpperCase());
        //有些TAG对应的值需要转换
        if (!CollectionUtils.isEmpty(block) && block.containsKey(valueStr)) {
            return block.get(valueStr);
        } else {
            return valueStr;
        }
    }

    /**
     * 国内证报文格式化
     * 部分属性不显示 待补充
     *
     * @param msgHead
     * @param despList
     * @param lvl
     */
    private void fillDespListForGnz(JSONObject msgHead, String type, List<TagDesp> despList, int lvl) {
        for (String tag : msgHead.keySet()) {
            TagDesp tagDesp = new TagDesp();
            String desp = Inis.getIniStr("boltxt.ini", type, tag + "_cn");
            if (StringUtils.isEmpty(desp)) {
                desp = Inis.getIniStr("boltxt.ini", "Tag-Translation", tag + "_cn");
            }
            Object value = msgHead.get(tag);
            tagDesp.setTag(tag);
            tagDesp.setDesp(desp);
            tagDesp.setLvl(lvl);
            if (value instanceof JSONObject) {
                tagDesp.setValue("");//eg申请人信息
                tagDesp.setDesp(this.generateSpace(lvl, tagDesp.getDesp()));
                despList.add(tagDesp);
                this.fillDespListForGnz((JSONObject) value, type, despList, lvl + 1);
            } else if (value instanceof JSONArray) {
                tagDesp.setDesp(this.generateSpace(lvl, tagDesp.getDesp()));
                despList.add(tagDesp);
                JSONArray jsonArray = (JSONArray) value;
                for (int i = 0; i < jsonArray.size(); i++) {
                    JSONObject jsonObject = jsonArray.getJSONObject(i);
                    this.fillDespListForGnz(jsonObject, type, despList, lvl + 1);
                }
            } else {
                String valueStr = msgHead.getStr(tag);
                if (StringUtils.isNotEmpty(valueStr)) {
                    valueStr = getErsTagVal(tag, valueStr);
                    tagDesp.setValue(valueStr);
                    tagDesp.setDesp(this.generateSpace(lvl, tagDesp.getDesp()));
                    despList.add(tagDesp);
                }
            }
        }
    }

    public String generateSpace(int lev, String desc) {
        if (0 == lev || StringUtils.isEmpty(desc)) {
            return desc;
        } else {
            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 0; i < lev; i++) {
                stringBuilder.append("&nbsp;&nbsp;&nbsp;&nbsp;");
            }
            stringBuilder.append(desc);
            return stringBuilder.toString();
        }
    }

    public void showPlainGnzMessage(TextResultVo textResultVo) {
        this.fillFormatDataForGnz(textResultVo, textResultVo.getData());
        textResultVo.setData(XmlTransformerUtil.prettyFormat(textResultVo.getData()));
    }

    private String getFullKey(Properties xmlProperties, Map<String, String> common, String ersKey) {
        String property = "";
        String upper = ersKey.toUpperCase();
        if (xmlProperties != null) {
            property = xmlProperties.getProperty(upper);
            if (StringUtils.isEmpty(property)) {
                property = xmlProperties.getProperty(ersKey);
            }
        }
        if (!StringUtils.isEmpty(property)) {
            return property;
        }
        if (common != null) {
            property = common.get(upper);
        }

        return !StringUtils.isEmpty(property) ? property : ersKey;

    }


    public String convertSwift999ToMessage(AbsMTCreator srcMt, TextResultVo textResultVo) {
        MT999Creator mt999Creator = new MT999Creator();
        mt999Creator.setSendBic(srcMt.getSendBic());
        mt999Creator.setRecvBic(srcMt.getRecvBic());
        Map<String, String> maps = this.parseAbsMtCreatorFor999(srcMt);
        String tag21 = maps.get("tag21");
        String tag20 = maps.get("tag20");
        String tag79 = "";
        if (!srcMt.getMsgType().contains("99")) {
            SWFMessage swfMessage = MessageFormat.getSWFMessage(srcMt.getMsgType());
            String mtName = srcMt.getMsgType() + " " + (swfMessage == null ? srcMt.getMsgType() : swfMessage.getName());
            if (mtName != null) {
                tag79 += mtName.toUpperCase();
                tag79 += CR;
            }
        }
        tag79 += maps.get("tag79");
        //开始转换79
        if (!MdaUtils.isEmpty(tag20)) {
            mt999Creator.setTransactionReferenceNumber(tag20);
        }
        if (!MdaUtils.isEmpty(tag21)) {
            mt999Creator.setRelatedReference(tag21);
        }
        String tag79Val = StringUtils.convertBlock(tag79, 35, 50);
        mt999Creator.setNarrative(tag79Val);
        textResultVo.setAttach(mt999Creator);
        MtResult mtResult = MtCreatorManager.packMT(mt999Creator);
        if (mtResult.isSuccess()) {
            String message = mtResult.getMessage();
            logger.debug("生成MT999报文：{}", message);
            return message;
        } else {
            logger.error("生成MT999错误：{}",
                    String.format("mtResut boolean:%s ,Description===\r\n%s",
                            mtResult.isSuccess(),
                            mtResult.getDescription()));
//            throw new RestBizException(ErrorCode.MT_MESSAGE_BUILD_ERROR,"MT999组报失败");
            return mtResult.getMessage();
        }
    }

    /**
     * 解析AbsMTCreator成MT999，20 21 79
     */
    private Map<String, String> parseAbsMtCreatorFor999(AbsMTCreator srcMt) {
        Map<String, String> maps = new HashMap<>();
        try {
            String tag79Str = "";
            for (Field declaredField : srcMt.getClass().getDeclaredFields()) {
                declaredField.setAccessible(true);
                TagName tagName = declaredField.getAnnotation(TagName.class);
                String tagNameStr = "";
                if (tagName != null) {
                    tagNameStr = tagName.value()[0];
                }
                //取参考号
                if ("20".equals(tagNameStr)) {
                    String tag20 = (String) declaredField.get(srcMt);
                    if (!maps.containsKey("tag20")) {
                        maps.put("tag20", tag20);
                    }
                    continue;
                } else if ("21".equals(tagNameStr)) {
                    String tag21 = (String) declaredField.get(srcMt);
                    if (!maps.containsKey("tag21")) {
                        maps.put("tag21", tag21);
                    }
                    continue;
                }
                //取其它字段拼接
                Object value = declaredField.get(srcMt);
                String desp = "";
                FieldDescription fieldDescription = declaredField.getAnnotation(FieldDescription.class);
                if (fieldDescription != null) {
                    desp = fieldDescription.value().toUpperCase();
                }
                /**
                 * Empty Field
                 */
                if (tagNameStr.indexOf("15") > -1 && value != null) {
                    value = "";
                    desp = desp + " " + tagNameStr.substring(2);
                } else if (MdaUtils.isEmpty(value)) {
                    continue;
                }
                /**
                 * 开始取值
                 */
                String valOfStr = "";
                if (value instanceof String) {
                    valOfStr = (String) value;
                } else if (value instanceof List) {
                    List valueList = (List) value;
                    for (Object arg : valueList) {
                        if (arg instanceof String) {
                            valOfStr += arg;
                        } else if (arg instanceof AbsMTCreator) {
                            //递归调用
                            Map<String, String> tmpMaps = this.parseAbsMtCreatorFor999((AbsMTCreator) arg);
                            if (!maps.containsKey("tag20")) {
                                maps.put("tag20", tmpMaps.get("tag20"));
                            }
                            if (!maps.containsKey("tag21")) {
                                maps.put("tag21", tmpMaps.get("tag21"));
                            }
                            tag79Str += tmpMaps.get("tag79");
                        } else {
                            logger.error("组MT999，错误的数据类型", tagNameStr);
                            throw new RestBizException(ErrorCode.MT_MESSAGE_BUILD_ERROR, "MT999组报失败，错误的数据类型");
                        }
                    }
                } else {
                    //复杂对象，则判断类型是否有getTagResult函数
                    Class clazz = declaredField.getType();
                    String specialMethod = "getTagResult";
                    try {
                        Method method = clazz.getDeclaredMethod(specialMethod);
                        TagResult tagResult = (TagResult) method.invoke(value);
                        valOfStr = tagResult.getValue();
                    } catch (NoSuchMethodException e) {
                        continue;
                    } catch (InvocationTargetException e) {
                        logger.error("组MT999，原报文Tag{}生成内容失败", tagNameStr);
                        throw new RestBizException(ErrorCode.MT_MESSAGE_BUILD_ERROR, "MT999组报失败");
                    }
                }
                if (StringUtils.isEmpty(desp) || StringUtils.isEmpty(valOfStr)) {
                    continue;
                }
                //组值，2是1个冒号+1个空格的占位长度
                if (desp.length() + 2 + valOfStr.length() > 50) {
                    if (!srcMt.getMsgType().contains("99")) {
                        tag79Str += desp + ":";
                        tag79Str += CR;
                    }
                    tag79Str += valOfStr;
                    tag79Str += CR;
                } else {
                    if (!srcMt.getMsgType().contains("99")) {
                        tag79Str += desp + ":";
                    }
                    tag79Str += valOfStr;
                    tag79Str += CR;
                }
            }
            maps.put("tag79", tag79Str);
        } catch (IllegalAccessException ignore) {
            logger.error("被忽略的生成MT999错误：{}", ignore);
        }
        return maps;
    }

    /**
     * Swift 报文展开
     *
     * @param textResultVo 需要格式化的数据
     * @return
     */
    public Object showSwift(TextResultVo textResultVo) {
        AbsMTCreator srcMt = (AbsMTCreator) textResultVo.getAttach();
        if (srcMt == null) {
            logger.warn("请将MTCreator附加给TextResultVo");
            return null;
        }
        List<AbsMTCreator> absMTCreators = mtCreatorSplitor.splitMT(srcMt);
        if (absMTCreators.size() == 1 && SettleContext.isSavTransSta()) {
            //等于自己，就不用再解析，切割报文了
            SWFMessageObject swfMessageObject = showMTCreator(absMTCreators.get(0), false, false);
            swfMessageObject.setMessage(textResultVo.getData());
            checkSwiftMessage(absMTCreators.get(0).getMsgType(), swfMessageObject.getMessage(), swfMessageObject);
            textResultVo.setFormatData(Collections.singletonList(swfMessageObject));
            return null;
        }
        boolean needReBuildMT = absMTCreators.size() > 1;
        List<SWFMessageObject> swfMessageObjects = IntStream.range(0, absMTCreators.size())
                //如果分片了，例如760，首笔creator因为在外围被pack了，需要重置内部的mt对象
                .mapToObj(x -> showMTCreator(absMTCreators.get(x), needReBuildMT, x == 0)).collect(Collectors.toList());
        textResultVo.setFormatData(swfMessageObjects);
        if (swfMessageObjects.size() == 1) {
            swfMessageObjects.get(0).setMessage(textResultVo.getData());
        }
        //校验报文
        swfMessageObjects.forEach(swfMessageObject -> {
            checkSwiftMessage(swfMessageObject.getMt(), swfMessageObject.getMessage(), swfMessageObject);
        });
        return swfMessageObjects;
    }

    private void invoke(Object obj, String methodName) {
        try {
            Method method = AbsMTCreator.class.getDeclaredMethod(methodName);
            method.setAccessible(true);
            method.invoke(obj);
        } catch (Exception ex) {
            logger.error("执行方法异常：", ex);
        }
    }

    public SWFMessageObject showMTCreator(AbsMTCreator srcMt, boolean needReBuildMT, boolean reInitMt) {
        SWFMessageObject swfMessageObject = new SWFMessageObject();
        if (needReBuildMT) {
            if (reInitMt) {
                String sndbic = srcMt.getSendBic();
                String rcvbic = srcMt.getRecvBic();
                invoke(srcMt, "initAbstractMT");
                srcMt.setSendBic(sndbic);
                srcMt.setRecvBic(rcvbic);
                logger.info("重新构建Creator里面的MT对象");
            }
            MtResult mtResult = MtCreatorManager.packMT(srcMt);
            swfMessageObject.setMessage(mtResult.getMessage());
            if (reInitMt) {
                logger.info("重新生成报文:{}", mtResult.getMessage());
            } else {
                logger.info("生成切片报文:{}", mtResult.getMessage());
            }
        }
//        if (SettleContext.isSavTransSta()) {
//            //当前为保存模式，不用展开报文了
//            logger.info("无须展开报文{}", srcMt.getMsgType());
//            swfMessageObject.setMt(srcMt.getMsgType());
//            return swfMessageObject;
//        }

        logger.info("预览生成{}报文", srcMt.getMsgType());

        swfMessageObject.setMt(srcMt.getMsgType());
        SWFMessage swfMessage = MessageFormat.getSWFMessage(srcMt.getMsgType());
        String mtName = (swfMessage == null ? srcMt.getMsgType() : swfMessage.getName());
        swfMessageObject.setMtdesp(mtName);
        String sndBicName = loadBICName(srcMt.getSendBic());
        String rcvBicName = loadBICName(srcMt.getRecvBic());
        swfMessageObject.setSndbic(sndBicName);
        swfMessageObject.setRcvbic(rcvBicName);
        if (srcMt.getAbstractMT() != null) {
            swfMessageObject.setLvl(srcMt.getAbstractMT().getMessagePriority());
        }
        swfMessageObject.setDir(">");
        List<TagDesp> tagsList = new ArrayList<>();
        swfMessageObject.setTagDespList(tagsList);
        for (Field declaredField : srcMt.getClass().getDeclaredFields()) {
            declaredField.setAccessible(true);
            // 设置字段可访问（必须，否则报错）
            declaredField.setAccessible(true);
            ResolvableType resolvableType = ResolvableType.forField(declaredField);

            if (List.class.isAssignableFrom(resolvableType.resolve()) && resolvableType.getGeneric(0).resolve() != null
                    && AbsMTCreator.class.isAssignableFrom(resolvableType.getGeneric(0).resolve())) {
                try {
                    List subList = (List) declaredField.get(srcMt);
                    if (subList == null) {
                        continue;
                    }
                    subList.forEach(item -> {
                        SWFMessageObject subswf = showMTCreator((AbsMTCreator) item, false, false);
                        tagsList.addAll(subswf.tagDespList);
                    });
                } catch (IllegalAccessException e) {
                    logger.error("报文展开取值失败:{}", e);
                }
                continue;
            }
            if (!declaredField.isAnnotationPresent(TagName.class)) {
                continue;
            }

            if (!declaredField.isAnnotationPresent(FieldDescription.class)) {
                continue;
            }
            TagName tagName = declaredField.getAnnotation(TagName.class);
            String tagNameStr = tagName.value()[0];

            //取其它字段拼接
            Object value = null;
            try {
                value = declaredField.get(srcMt);
            } catch (IllegalAccessException e) {
                logger.error("报文展开取值失败：{}", e);
            }
            String desp = declaredField.getAnnotation(FieldDescription.class).value();
            /**
             * Empty Field
             */
            if (tagNameStr.indexOf("15") > -1 && value != null) {
                value = "";
                desp = desp + " " + tagNameStr.substring(2);
                desp = String.format("<h3>%s</h3>", desp);
            } else if (MdaUtils.isEmpty(value)) {
                continue;
            }
            if (value instanceof List) {
                String despBak = desp;
                ((List) value).forEach(item -> {
                    if (item == null) {
                        return;
                    }
                    TagDesp tagDesp = formatValue(srcMt.getMsgType(), tagNameStr, despBak, item, declaredField, item.getClass());
                    if (tagDesp != null) {
                        tagsList.add(tagDesp);
                    }
                });
            } else {
                TagDesp tagDesp = formatValue(srcMt.getMsgType(), tagNameStr, desp, value, declaredField, declaredField.getType());
                if (tagDesp != null) {
                    tagsList.add(tagDesp);
                }
            }

        }
        return swfMessageObject;
    }

    public String loadBICName(String param) {
        if (MdaUtils.isEmpty(param)) {
            return "";
        }
        //逻辑终端地址 转换一下
        if (param.length() == 12) {
            param = param.substring(0, 8) + param.substring(9);
        }
        if (param.length() > 11) {
            return param;
        }
        Pta pta = ptaCacheService.selectByPtyextkeyAndUsg(param, "MAA");
        if (pta != null) {
            PtsptaToolComponent ptsptaToolComponent = SpringEnvContext.getBean(PtsptaToolComponent.class);
            PtsptaVo ptspta = ptsptaToolComponent.getPtsptaFromPtaINR(pta.getInr());
            return (param + " BIC identified as:\r\n" + ptspta.getAdrblk()).toUpperCase();
        } else {
            return param;
        }
    }

    private TagDesp formatValue(String msgTyp, String tagNameStr, String desp, Object value, Field declaredField, Class<?> clazz) {
        //将Value取为字符串
        String valOfStr = "";
        if (value instanceof String && !SwtExpandable.class.isAssignableFrom(clazz)) {
            valOfStr = (String) value;
        } else {
            //复杂对象，则判断类型是否有getTagResult函数
            String specialMethod = "getTagResult";
            if (value instanceof SwtExpandable || SwtExpandable.class.isAssignableFrom(clazz)) {
                specialMethod = "getExpandResult";
            }
            try {
                TagResult tagResult;
                //如果value是String类型，说明是复核时查看报文
                if (value instanceof String) {
                    String param = (String) value;
                    // 获取类的构造方法
                    Constructor<?> constructor = clazz.getConstructor(String.class);
                    // 调用构造方法创建对象
                    Object obj = constructor.newInstance(tagNameStr);
                    Method method = clazz.getDeclaredMethod(specialMethod, String.class);
                    tagResult = (TagResult) method.invoke(obj, param);
                } else {
                    Method method = clazz.getDeclaredMethod(specialMethod);
                    tagResult = (TagResult) method.invoke(value);
                }
                valOfStr = tagResult.getValue();
                if (valOfStr != null) {
                    valOfStr = valOfStr.trim();
                }
                //复杂对象的结果为空串，则结果不用了
                if (MdaUtils.isEmpty(valOfStr)) {
                    return null;
                }
                if (!MdaUtils.isEmpty(tagResult.getTagEnd())) {
                    tagNameStr = tagResult.getTagBefore() + tagResult.getTagEnd();
                } else {
                    tagNameStr = tagResult.getTagBefore();
                }
            } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException | InstantiationException e) {
                logger.error("展开原报文内容失败{}:{}", tagNameStr, e);
                return null;
            }
        }

        //做一些格式化处理
        valOfStr = exchangeTagStr(tagNameStr, valOfStr);
        //760 判断77U、77L
        char lastChar = declaredField.getName().charAt(declaredField.getName().length() - 1);
        String seq = "";
        if (Character.isUpperCase(lastChar) && !Character.isUpperCase(tagNameStr.charAt(tagNameStr.length() - 1))) {
            seq = "." + lastChar;
        }
        //格式化码表
        valOfStr = tagCodeFormater.formatCodeContent(msgTyp, tagNameStr + seq, valOfStr);

        TagDesp tagDesp = new TagDesp();
        tagDesp.setTag(tagNameStr);
        tagDesp.setDesp(desp);
        tagDesp.setValue(valOfStr);
        return tagDesp;
    }


    public Object showPlainSwiftMessage(TextResultVo textResultVo, Smh smh) {
        ObjectMapper objectMapper = new ObjectMapper();
        String acktxt = null;
        if (smh.getDir().equals(">")) {
            Ack ackTmp = new Ack();
            ackTmp.setObjtyp("TRN");
            ackTmp.setObjinr(smh.getTrninr());
            ackTmp.setObjsub(smh.getInr());
            ackTmp.setAcksrv(smh.getMsgtyp());
            Ack wrkAck = ackMapper.selectOneByVoS(ackTmp);
            if (wrkAck != null && StringUtils.isNotEmpty(wrkAck.getAcktxt())) {
                acktxt = wrkAck.getSta().equals("A") ? "ACK:<br/>" + wrkAck.getAcktxt() : "NCK:<br/>" + wrkAck.getAcktxt();
            }
        }
        String swiftText = textResultVo.getData();
        String formatDataPath = appEnv.getSubDataPath(Utils.replacePath(smh.getDocpth()), smh.getDocfil() + ".json");
        // 老国结报文不会读取JSON文件
        if (!swiftText.startsWith(":MT:")) {
            byte[] bytes = objectStroeToolService.readObjectFile(formatDataPath);
            if (bytes != null) {
                String formatData = new String(bytes, StandardCharsets.UTF_8);
                // 将JSON字符串还原为List
                List<SWFMessageObject> swfMessageObjects;
                try {
                    swfMessageObjects = objectMapper.readValue(formatData, new TypeReference<List<SWFMessageObject>>() {
                    });
                    if (swfMessageObjects.size() == 1) {
                        swfMessageObjects.get(0).setAcktxt(acktxt);
                        textResultVo.setFormatData(swfMessageObjects);
                        swfMessageObjects.get(0).setDir(smh.getDir());
                        SWFMessageObject swfMessageObject = swfMessageObjects.get(0);
                        String sndBicName = loadBICName(swfMessageObject.getSndbic());
                        String rcvBicName = loadBICName(swfMessageObject.getRcvbic());
                        swfMessageObject.setSndbic(sndBicName);
                        swfMessageObject.setRcvbic(rcvBicName);
                        return swfMessageObjects.get(0);
                    }
                } catch (JsonProcessingException e) {
                    logger.warn("解析报文JSON异常:{}", e);
                }
            }
        }
        SWFMessage swfMessage = SWFMessage.loadSwiftMessage(swiftText);
        SWFMessageObject swfMessageObject = new SWFMessageObject();

        String mtName = swfMessage.getName();
        String mtType = swfMessage.getMtType();
        swfMessageObject.setMtdesp(mtName);
        swfMessageObject.setMt(mtType);
        String sndBicName = loadBICName(swfMessage.getBscInfo().get("sndbic"));
        String rcvBicName = loadBICName(swfMessage.getBscInfo().get("rcvbic"));
        swfMessageObject.setSndtime(swfMessage.getBscInfo().get("sndtim"));
        swfMessageObject.setRcvtime(swfMessage.getBscInfo().get("rcvtim"));
        swfMessageObject.setSndbic(sndBicName);
        swfMessageObject.setRcvbic(rcvBicName);
        swfMessageObject.setLvl(swfMessage.getBscInfo().get("prt"));
        swfMessageObject.setMessage(swiftText);
        swfMessageObject.setAcktxt(acktxt);
        swfMessageObject.setDir(smh.getDir());
        swfMessageObject.setBlock3_108(swfMessage.getBscInfo().get("108"));
        swfMessageObject.setBlock3_111(swfMessage.getBscInfo().get("111"));
        swfMessageObject.setBlock3_121(swfMessage.getBscInfo().get("121"));

        List<TagDesp> tagsList = new ArrayList<>();
        swfMessageObject.setTagDespList(tagsList);
        String packageName = AbsMTCreator.class.getPackage().getName();
        try {
            Class<?> mtCreaterClass = Class.forName(packageName + ".mt" + mtType.charAt(0) + "xx.MT" + mtType + "Creator");
            swfMessage.collectAllTagLine().forEach(tagLine -> {
                String tagNameStr = tagLine.getName();

                String desp = StringEscapeUtils.escapeHtml(tagLine.getDesp());
                String value = tagLine.getValueForHuman();
                if (tagNameStr.contains("15") && value != null) {
                    if (!((AbstractMessageArea) tagLine.parent).isLoaded()) {
                        //如果父元素没有被装载，那么这个15就不要显示了
                        return;
                    }
                    value = "";
                    desp = desp + " " + tagNameStr.substring(2);
                    desp = String.format("<h3>%s</h3>", desp);
                } else if (MdaUtils.isEmpty(value)) {
                    return;
                }
                String seq = "";
                if (tagLine.parent instanceof SEQList) {
                    seq = tagLine.parent.getName();
                }

                //报文展开相关逻辑
                Field[] fields = mtCreaterClass.getDeclaredFields();
                int size = tagsList.size();
                for (Field field : fields) {
                    TagName annotation = field.getAnnotation(TagName.class);
                    if (annotation != null) {
                        String[] tagValues = annotation.value();
                        if (Arrays.asList(tagValues).contains(tagNameStr)) {
                            if (StringUtils.isNotEmpty(seq)) {
                                if (seq.equals(field.getName().substring(field.getName().length() - 1))) {
                                    tagsList.add(formatValue(mtType, tagNameStr, desp, value, field, field.getType()));
                                    break;
                                }
                            } else {
                                tagsList.add(formatValue(mtType, tagNameStr, desp, value, field, field.getType()));
                                break;
                            }
                        }
                    }
                }
                if (tagsList.size() == size) {
                    //做一些格式化处理
                    value = exchangeTagStr(tagNameStr, value);
                    //格式化码表
                    value = tagCodeFormater.formatCodeContent(swfMessage.getMtType(), tagNameStr + seq, value);
                    TagDesp tagDesp = new TagDesp();
                    tagDesp.setTag(tagNameStr);
                    tagDesp.setDesp(desp);
                    tagDesp.setValue(value);
                    tagsList.add(tagDesp);
                }
            });
        } catch (ClassNotFoundException e) {
            logger.warn("格式化报文异常:{}", e);
        }
        textResultVo.setFormatData(Collections.singletonList(swfMessageObject));
        try {
            String formatDataString = objectMapper.writeValueAsString(textResultVo.getFormatData());
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(formatDataString.getBytes());
            objectStroeToolService.saveObjectFileByStream(byteArrayInputStream, formatDataPath);
        } catch (JsonProcessingException e) {
            logger.warn("存储报文JSON异常:{}", e);
        }
        return swfMessageObject;
    }

    public String exchangeTagStr(String tag, String valStr) {
        switch (tag) {
            case "30":
            case "31C":
            case "31E":
            case "31L":
            case "44C":
                return FormatUtils.fmt6NDate(valStr);
            case "31D":
                String val = "";
                if (!MdaUtils.isEmpty(valStr) && valStr.length() >= 6) {
                    String dateStr = valStr.substring(0, 6);
                    val = FormatUtils.fmt6NDate(dateStr);
                    if (valStr.length() > 6) {
                        val += " " + valStr.substring(6);
                    }
                }
                return val;

        }
        return valStr;
    }


    public void showPlainMessage(TextResultVo textResultVo, Smh smh) {
        if (DocConstant.COR_TYP_SWT.equals(smh.getCortyp())) {
            String orimsgtyp = smh.getOrimsgtyp();
            if (StringUtils.isNotEmpty(orimsgtyp) && orimsgtyp.startsWith("ISO")) {
                showIsoMessage(textResultVo, smh);
            } else {
                showPlainSwiftMessage(textResultVo, smh);
            }
        } else if (DocConstant.CIPS.equals(smh.getDocfxt()) || DocConstant.CNAP.equals(smh.getDocfxt()) || DocConstant.FXPS.equals(smh.getDocfxt()) ||
                DocConstant.HVPS.equals(smh.getDocfxt()) || DocConstant.SWTE.equals(smh.getDocfxt()) || DocConstant.CIPE.equals(smh.getDocfxt()) || DocConstant.FXPE.equals(smh.getDocfxt())) {
            showJsnMessage(textResultVo);
            if(DocConstant.FXPS.equals(smh.getDocfxt()) || DocConstant.FXPE.equals(smh.getDocfxt())){
                textResultVo.setFormat("fmt");
            }
        } else if (DocConstant.COR_TYP_ERX.equals(smh.getCortyp())) {
            showPlainErxMessage(textResultVo);
        } else if (DocConstant.COR_TYP_ELC.equals(smh.getCortyp())) {
            Result result = MsgUtil.unPackNoSignMessage(textResultVo.getData());
            if (!result.isSuccess()) {
                result = MsgUtil.unPackNoSignMessage(SSTFGson.getValueByPath(textResultVo.getData(), "message"));
            }
            textResultVo.setAttach(result);
            showElcsModel(textResultVo);
        } else if (DocConstant.COR_TYP_FMT.equals(smh.getCortyp())) {
            showPlainFmtMessage(textResultVo);
        } else if (DocConstant.COR_TYP_GNZ.equals(smh.getCortyp())) {
            showPlainGnzMessage(textResultVo);
        }
    }

    /**
     * ISO 报文展开
     *
     * @param textResultVo
     */
    public void showIsoMessage(TextResultVo textResultVo, Smh smh) {
        String docpth = "";
        if ("<".equals(smh.getDir())) {
            docpth += "swtin";
        } else {
            docpth += "swtout";
        }
        docpth += "/mxiso/arc/" + Utils.fmtDate(smh.getCredattim());
        String filePath = appEnv.getSubDataPath(docpth, smh.getDocfil() + ".xml");
        if (!objectStroeToolService.isExistObjectFile(filePath)) {
            throw new CommonServiceException("找不到ISO报文,路径为" + filePath);
        }
        textResultVo.setFormat("iso");
        textResultVo.setExtension("xml");
        byte[] bytes = objectStroeToolService.readObjectFile(filePath);
        if (bytes != null) {
            String formatData = new String(bytes, StandardCharsets.UTF_8);
            IsoMessageObject isoMessageObject = new IsoMessageObject();
            isoMessageObject.setMessage(formatData);
            //优先读取ISO格式化文件
            String json = "";
            String jsonPath = appEnv.getSubDataPath(docpth, smh.getDocfil() + ".json");
            byte[] jsonBytes = objectStroeToolService.readObjectFile(jsonPath);
            if (jsonBytes != null) {
                json = new String(jsonBytes, StandardCharsets.UTF_8);
            } else {
//                json = SwiftTransfer.mx2Message(formatData, "CN", new FormatIsoHelper());
                json = SwiftTransfer.mx2Message(formatData, "CN");
            }
            List<TagDesp> tagDespList = Utils.toListObject(TagDesp.class, json);
            isoMessageObject.setTagDespList(tagDespList);
            //获取报文类型
            Optional<TagDesp> tagDesp = tagDespList.stream().filter(tag -> "app.header.msgDefId".equals(tag.getTag())).findFirst();
            String messageType = "";
            if (tagDesp.isPresent()) {
                messageType = tagDesp.get().getValue();
            }
            if (messageType.contains("(")) {
                messageType = messageType.substring(0, messageType.indexOf("("));
            }
            isoMessageObject.setMxType(messageType);
            //展示mt报文
            TextResultVo mtTextResultVo = new TextResultVo();
            mtTextResultVo.setData(textResultVo.getData());
            SWFMessageObject swfMessageObject = (SWFMessageObject) this.showPlainSwiftMessage(mtTextResultVo, smh);
            isoMessageObject.setSwfMessageObject(swfMessageObject);
            textResultVo.setFormatData(isoMessageObject);
            //存储ISO格式化报文
            if (!objectStroeToolService.isExistObjectFile(jsonPath)) {
                ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(json.getBytes());
                objectStroeToolService.saveObjectFileByStream(byteArrayInputStream, jsonPath);
            }
        }
    }

    private void parseElement(Element parent, int level, List<TagDesp> tagDespList) {
        // 获取DataPDU子集
        NodeList childNodes = parent.getChildNodes();

        for (int i = 0; i < childNodes.getLength(); i++) {
            Node node = childNodes.item(i);
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                Element element = (Element) node;
                TagDesp tagDesp = new TagDesp();
                tagDesp.setLvl(level);
                tagDesp.setTag(element.getTagName());
                String des = Inis.getIniStr("swttxt.ini", "Tag-Translation", element.getTagName());
                tagDesp.setDesp(des == null ? element.getTagName() : des.trim());
                //当前标签有 Ccy 属性, 用换行符拼接value
                NamedNodeMap attributes = element.getAttributes();
                for (int j = 0; j < attributes.getLength(); j++) {
                    Attr attribute = (Attr) attributes.item(j);
                    if ("Ccy".contains(attribute.getName())) {
                        tagDesp.setValue(attribute.getValue());
                    }
                }
                if (null != element.getChildNodes() && element.getChildNodes().getLength() == 1) {
                    String text = element.getTextContent().trim();
                    if (!text.isEmpty()) {
                        tagDesp.setValue(null == tagDesp.getValue() ? text : tagDesp.getValue() + "\n" + text);
                    }
                }
                //机构处理
                if ("BICFI".equals(tagDesp.getTag())) {
                    tagDesp.setValue(this.loadBICName(tagDesp.getValue()));
                }
                tagDesp.setDesp(this.generateSpace(level, tagDesp.getDesp()));
                if (!"AppHdr".equals(tagDesp.getTag())) {
                    tagDespList.add(tagDesp);
                } else {
                    //AppHdr手动折行,不按空格分隔
                    TagDesp tagDesp1 = new TagDesp();
                    TagDesp tagDesp2 = new TagDesp();
                    TagDesp tagDesp3 = new TagDesp();
                    tagDesp1.setLvl(tagDesp.getLvl());
                    tagDesp2.setLvl(tagDesp.getLvl());
                    tagDesp3.setLvl(tagDesp.getLvl() + 1);
                    tagDesp1.setTag("AppHdr");
                    tagDesp1.setDesp("Business Application Header V02");
                    tagDesp2.setDesp("(head.001.001.02)");
                    tagDesp3.setDesp("Xmlns");
                    tagDesp1.setValue("");
                    tagDesp2.setValue("");
                    tagDesp3.setValue(attributes.getNamedItem("xmlns").getNodeValue());
                    tagDesp1.setDesp(generateSpace(tagDesp1.getLvl(), tagDesp1.getDesp()));
                    tagDesp2.setDesp(generateSpace(tagDesp2.getLvl(), tagDesp2.getDesp()));
                    tagDesp3.setDesp(generateSpace(tagDesp3.getLvl(), tagDesp3.getDesp()));
                    tagDespList.add(tagDesp1);
                    tagDespList.add(tagDesp2);
                    tagDespList.add(tagDesp3);
                }
                parseElement(element, level + 1, tagDespList);
            }
        }
    }

    public void showElcsModel(TextResultVo textResultVo) {
        try {
            Result elcResult = (Result) textResultVo.getAttach();
            elcResult.setStrict(false);
            Map<String, String> flatMap = elcResult.getElcsFlatHierarchy();
            Map<String, Object> rtnMap = new LinkedHashMap<>();
            Set<String> removeDuplicate = new HashSet<>();
            Map<String, String> ENDWITH = elcIni.getBlock("ENDWITH");
            Map<String, String> special = elcIni.getBlock(elcResult.getMessageType().toUpperCase());
            flatMap.entrySet().forEach(entry -> {
                String key = entry.getKey();//实际key
                String lastPart = key.substring(key.lastIndexOf(".") + 1);
                lastPart = StringUtils.isNotEmpty(lastPart) ? lastPart.toUpperCase() : "";
                String fieldKey;
                StringBuilder fieldKeySb = new StringBuilder();//查找字段的key
                if (key.endsWith(".Ccy") || key.endsWith(".amount")) {
                    //币种金额不需要独立展示
                    return;
                }
                int kind = 0; //默认字符串，1为数组对象，2为数组字符串
                String val = entry.getValue();
                Object valObj = entry.getValue();
                if (val.startsWith("[{")) {
                    valObj = FastJsonUtil.toList(val, Map.class);
                    kind = 1;
                } else if (val.startsWith("[")) {
                    valObj = FastJsonUtil.toList(val, String.class);
                    kind = 2;
                }
                //key需要格式化下，因为数组拼接的扁平化key包含_+数字
                Matcher matcher = pattern.matcher(key);
                int lastIndex = 0;
                while (matcher.find()) {
                    fieldKeySb.append(key.substring(lastIndex, matcher.start()));
                    //循环域展示这一层级
                    String field = fieldKeySb.toString();
                    if (removeDuplicate.add(field)) {
                        Desc desc = Utils.getTargetFieldAnnotationByPath(elcResult.getElcsModel(), field, Desc.class);
                        if (desc != null) {
                            Map<String, Object> arrayItem = new HashMap<>();
                            arrayItem.put("value", "&nbsp;");
                            arrayItem.put("kind", 1);
                            arrayItem.put("typeCode", desc.typeName());
                            arrayItem.put("label", desc.value());
                            rtnMap.put(field, arrayItem);
                        }
                    }
                    lastIndex = matcher.end();
                }
                fieldKeySb.append(key.substring(lastIndex));
                fieldKey = fieldKeySb.toString();
                //对elcdoc.ini中配置的需要额外展示
                if (!CollectionUtils.isEmpty(ENDWITH)) {
                    for (Map.Entry<String, String> item : ENDWITH.entrySet()) {
                        String itemKey = item.getKey();
                        if (fieldKey.endsWith(itemKey) && removeDuplicate.add(itemKey)) {
                            int dotIndex1 = key.indexOf(".");
                            int dotIndex2 = key.lastIndexOf(".");
                            String key2;
                            if (dotIndex1 == dotIndex2) {
                                key2 = key.substring(dotIndex1 + 1);
                            } else {
                                key2 = key.substring(dotIndex1 + 1, dotIndex2);
                            }
                            Map<String, Object> itemMap = new HashMap<>();
                            itemMap.put("value", "&nbsp;");
                            itemMap.put("label", item.getValue());
                            rtnMap.put(key2, itemMap);
                        }
                    }
                }
                int dotIndex = key.indexOf(".");
                //去掉每种报文的顶层节点
                if (dotIndex > 0) {
                    key = key.substring(dotIndex + 1);
                    fieldKey = fieldKey.substring(dotIndex + 1);
                }
                //行号加工
                if (key.endsWith(".BkCd") || key.endsWith(".MmbId") || key.endsWith(".BrnchId.Id") || key.equals("OrigSender") || key.equals("OrigReceiver") || key.endsWith(".IssBkCd") || key.endsWith(".PresnBkCode")|| key.endsWith(".InstgDrctPty02")|| key.endsWith(".InstgPty02")|| key.endsWith(".InstdDrctPty02")|| key.endsWith(".InstdPty02")) {
                    String bnkNo = val;
                    Bnl bnl = bnlMapper.selectByFqcyjg(bnkNo);
                    if (bnl != null) {
                        String bnkName = bnl.getFukrhm();
                        valObj = val + " :" + bnkName;
                    } else if ("0000".equals(val)) {
                        valObj = val + " :国家处理中心";
                    }
                }
                //取elcdoc.ini中对值得映射翻译
                Map<String, String> block = elcIni.getBlock(lastPart);
                if (!CollectionUtils.isEmpty(block) && block.containsKey(val)) {
                    valObj = val + " :" + block.get(val);
                }

                Desc desc = Utils.getTargetFieldAnnotationByPath(elcResult.getElcsModel(), fieldKey, Desc.class);
                String typeCode = null;
                String label;
                if (desc != null) {
                    typeCode = desc.typeName();
                    label = desc.value();
                } else {
                    if (!CollectionUtils.isEmpty(special)) {
                        label = special.get(key);
                        Matcher mat = Pattern.compile("/E0\\d/").matcher(val);
                        if (mat.find()) {
                            valObj = val.substring(mat.end());
                            Bnl bnl1 = bnlMapper.selectByFqcyjg(valObj.toString());
                            if (bnl1 != null) {
                                String bnkName = bnl1.getFukrhm();
                                valObj = valObj + " :" + bnkName;
                            }
                        }
                    } else {
                        label = key;
                    }
                }
                Map<String, Object> itemMap = new HashMap<>();
                itemMap.put("value", valObj);
                itemMap.put("kind", kind);
                itemMap.put("typeCode", typeCode);
                itemMap.put("label", label);
                rtnMap.put(key, itemMap);
            });
            textResultVo.setFormatData(rtnMap);
        } catch (Exception e) {
            logger.error("处理ELC电证报文错误:{}", e);
            throw new SSTFException("ELC9001", "电证报文展报异常", e);
        }
        textResultVo.setData(SSTFGson.toPrettyJson(textResultVo.getData()));
    }

    public void showPlainErxMessage(TextResultVo textResultVo) {
        String data = textResultVo.getData();
        this.fillErxFormatData(textResultVo, data);
    }

    /**
     * 通用格式的JSON串展示报文：List<ResultConvert.TagDesp>格式
     */
    public void showJsnMessage(TextResultVo textResultVo) {
        //反序列化为List<TagDesp>对象
        String data = textResultVo.getData();
        try {
            List<TagDesp> tagDesps = objectMapper.readValue(data, new TypeReference<List<TagDesp>>() {
            });
            textResultVo.setFormatData(tagDesps);
            textResultVo.setFormat("jsn");
        } catch (Exception e) {
            logger.error("处理{}报文错误:{}", e);
            throw new SSTFException("ELC9001", "来报展报异常", e);
        }
    }

    /**
     * 将section转为property
     */
    private Properties sectionToProperties(Map<String, String> section) {
        if (section == null) {
            return null;
        }
        Properties properties = new Properties();
        for (Map.Entry<String, String> entry : section.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            properties.setProperty(key.trim(), value.trim());
        }
        return properties;
    }

    /**
     * 用于swift报文转pdf落地
     *
     * @param swiftText swift报文内容
     * @param smh       当前这笔来报的smh对象
     * @param pdfPath   pdf文件存储的路径
     * @return 成功返回true
     */
    public boolean savePdfForSwiftMessage(String swiftText, Smh smh, String pdfPath) {
        TextResultVo textResultVo = new TextResultVo();
        textResultVo.setData(swiftText);
        showPlainSwiftMessage(textResultVo, smh);
        List<SWFMessageObject> swfMessageObjects = (List<SWFMessageObject>) textResultVo.getFormatData();
        if (CollectionUtils.isEmpty(swfMessageObjects)) {
            logger.warn("swift报文存pdf格式的展示内容失败：转化的内容为空.smhinr={}", smh.getInr());
            return false;
        }
        try {
            swfMessageObjects.forEach(swfMessageObject -> {
                TextResultVo pdfVo = renderSwiftMessageObjectToPdf(swfMessageObject, false, true);
                byte[] pdfContent = pdfVo.getBinary();
                String cleanPath = cleanPath(pdfPath);
                boolean flag = objectStroeToolService.saveObjectFileByStream(new ByteArrayInputStream(pdfContent), cleanPath);
                if (!flag) {
                    throw new CommonRuntimeException("swift报文存pdf格式的展示内容到失败:对象存储存储失败.smhinr=" + smh.getInr());
                }
            });
        } catch (Exception e) {
            logger.error(e.getMessage());
            return false;
        }
        if (logger.isInfoEnabled()) {
            logger.info("swift报文存pdf格式的展示内容成功");
        }
        return true;
    }

    /**
     * 通用获取pdf数据【用于预览和打印】
     *
     * @param textResultVo 前端报文预览信息
     * @return pdf数据信息
     */
    public TextResultVo renderMessageObjectToPdf(TextResultVo textResultVo) {
        String format = textResultVo.getFormat();
        Assert.hasText(format, "生成pdf失败：格式信息为空");
        if ("jsn".equalsIgnoreCase(format)) {
            return renderJsnMessageObjectToPdf((List<TagDesp>) textResultVo.getFormatData());
        } else if ("iso".equalsIgnoreCase(format)) {
            return renderEjsMessageObjectToPdf((List<TagDesp>) textResultVo.getFormatData());
        } else if ("source".equalsIgnoreCase(format) || "isosource".equalsIgnoreCase(format)) {
            return renderPureMessageToPdf(textResultVo.getData(), "xml");
        } else if ("elc".equalsIgnoreCase(format)) {
            TextResultVo rs = new TextResultVo();
            rs.setBinary(docCommonService.renderMessageObjectToPdf((List<TagDesp>) textResultVo.getFormatData(), "ELC",
                    new FormatHtmlHelper()));
            return rs;
        } else if ("fmt".equalsIgnoreCase(format)) {
            TextResultVo rs = new TextResultVo();
            rs.setBinary(docCommonService.renderMessageObjectToPdf((List<TagDesp>) textResultVo.getFormatData(), "FMT",
                    new FormatSwtHelper()));
            return rs;
        } else {
            Assert.state(false, () -> "获取pdf信息失败：无法识别的报文类型=" + format);
        }
        //不可能走到这里
        return null;
    }

    private TextResultVo renderPureMessageToPdf(String msg, String msgType) {
        TextResultVo rs = new TextResultVo();
        rs.setBinary(docCommonService.renderPureMessageObjectToPdf(msg, msgType));
        return rs;
    }

    private TextResultVo renderEjsMessageObjectToPdf(List<TagDesp> showMessage) {
        TextResultVo rs = new TextResultVo();
        rs.setBinary(docCommonService.renderEjsMessageObjectToPdf(showMessage));
        return rs;
    }

    private TextResultVo renderJsnMessageObjectToPdf(List<TagDesp> showMessage) {
        String msgTyp = String.valueOf(System.currentTimeMillis());
        TextResultVo rs = new TextResultVo();
        rs.setBinary(docCommonService.renderJsnMessageObjectToPdf(showMessage, msgTyp));
        return rs;
    }

    /**
     * 用于前端打印和下载使用
     */
    public TextResultVo renderSwiftMessageObjectToPdf(SWFMessageObject swfMessageObject, boolean isSrcMsg) {
        return renderSwiftMessageObjectToPdf(swfMessageObject, isSrcMsg, false);
    }

    /**
     * 用于前端打印和下载使用
     */
    public TextResultVo renderSwiftMessageObjectToPdf(SWFMessageObject swfMessageObject, boolean isSrcMsg, boolean isAuto) {
        String docName = SWT_PDF_TEMPLATE_NAME;
        if (isSrcMsg) {
            docName = SWT_PURE_PDF_TEMPLATE_NAME;
        }
        SimpleTextProvider simpleTextProvider = SimpleTextProvider.builder().onlyHtml(true).docName(docName).templateName(docName + "_LET.ftl").build();
        Map<String, Object> data = new HashMap<>();
        DoceotVo doceotVo = new DoceotVo();
        doceotVo.setDocuil("CN");
        data.put("item", swfMessageObject);
        if (isAuto) {
            data.put("user", "auto");
        } else {
            data.put("user", SettleContext.getSessionUserVo().getLogName());
        }
        data.put("date", new SimpleDateFormat("dd.MM.yyyy").format(new Date()));
        data.put("time", new SimpleDateFormat("HH:mm:ss").format(new Date()));
        data.put("formatContent", new FormatSwtHelper());
        TextResultVo textResultVo = simpleTextProvider.outputMessage(data, doceotVo, data);

        //html转pdf
        //将html 内容bos转为PDF
        try (ByteArrayOutputStream bos2OfPdf = new ByteArrayOutputStream()) {
            String html = textResultVo.getData();
            byte[] buff = new BASE64Decoder().decodeBuffer(html);
            html = new String(buff, "utf-8");
            ITextRenderer render = new ITextRenderer();
            ITextFontResolver fontResolver = render.getFontResolver();
            fontResolver.addFont(FONT, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);

            render.setDocumentFromString(html);
            render.layout();
            render.createPDF(bos2OfPdf);
            textResultVo.setData(null);
            textResultVo.setFormat("pdf");
            textResultVo.setExtension("pdf");
            textResultVo.setBinary(bos2OfPdf.toByteArray());
        } catch (Exception e) {
            logger.error("Pdf生成异常：{}", e);
            throw new CommonServiceException("Pdf生成异常");
        }
        return textResultVo;
    }

    @Setter
    @Getter
    public static class TagDesp {
        private String tag;
        private String desp;
        private String value;
        private int lvl;
    }

    @Setter
    @Getter
    public static class SWFMessageObject {
        private String sndtime;
        private String rcvtime;
        private String mtdesp;
        private String sndbic;
        private String rcvbic;
        private String mt;
        private String lvl = "N";
        /**
         * > 发报，<收报
         */
        private String dir;

        private List<TagDesp> tagDespList;

        private String message;
        private String acktxt;
        /**
         * 报文的校验结果
         */
        private String validateMessage;
        private String block3_108;
        private String block3_111;
        private String block3_121;
    }

    @Setter
    @Getter
    public static class IsoMessageObject {
        private List<TagDesp> tagDespList;
        private String mxType;
        private String message;
        /**
         * MT格式化报文
         */
        private SWFMessageObject swfMessageObject;
    }

    public void showPlainFmtMessage(TextResultVo textResultVo) {
        String data = textResultVo.getData();
        String[] lines = data.split("\r?\n(?=:((\\d{2}[A-Z]?)|FXPS|II|IO):)");
        List<TagDesp> result = new ArrayList<>();
        for (int i = 0; i < lines.length; i++) {
            String line = lines[i];
            int idx = line.indexOf(':', 1);
            String key = line.substring(1, idx);
            String value = line.substring(idx + 1);
            String label = getLabel(xmlDocPropertiesForFmt, key);
            TagDesp tagDesp = new TagDesp();
            tagDesp.setTag(":" + key + ":");
            tagDesp.setDesp(label);
            tagDesp.setValue(value);
            result.add(tagDesp);
        }
        textResultVo.setFormatData(result);
    }


    public String cleanPath(String path) {
        if (!org.springframework.util.StringUtils.hasLength(path)) {
            return "";
        }
        if (path.startsWith("/") && path.length() > 1) {
            path = path.substring(1);
        }
        path = path.replace("\\", "/");
        return path;
    }

    private String getLabel(Properties xmlProperties, String key) {
        String tempKey = key;
        if (isNumber(key)) {
            tempKey = "[" + key + "]";
        }
        String property = "";
        if (xmlProperties != null) {
            property = xmlProperties.getProperty(tempKey);
        }
        if (com.brilliance.isc.utils.StringUtils.isNotBlank(property)) {
            return property;
        } else {
            return key;
        }
    }

    private boolean isNumber(String key) {
        String trim = key.trim();
        if (trim.length() > 0) {
            for (int i = 0; i < trim.length(); i++) {
                if (!Character.isDigit(trim.charAt(i))) {
                    return false;
                }
            }
            return true;
        }
        return false;
    }

}
