package com.ann.demo.utils;

import com.alibaba.fastjson.JSON;
import com.ann.demo.entity.constant.AliasName;
import com.ann.demo.entity.constant.InterfaceName;
import com.ann.demo.entity.filing.dto.MessageDto;
import com.ann.demo.entity.interfaceEntity.*;
import com.ann.demo.entity.normalEntity.*;
import org.dom4j.*;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.XMLFilterImpl;

import javax.annotation.PostConstruct;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @Description
 * @Date 2019/6/17 15:14
 * @Created by  ljx
 */
@Component
public class XMLUtils {

    static final Logger logger = LoggerFactory.getLogger(XMLUtils.class);

    private static final String DEFAULT_ENCODING = "UTF-8";

    private static final Map<String, String> map;

    static {
        map = new HashMap<String, String>();
        map.put("ns", "urn:hl7-org:v3");
    }

    public static String path;
    @Value("${file.path}")
    private String pathTemp;

    // 手麻系统
    public static String handNumbnessReportPath;
    @Value("${file.handNumbnessReportPath}")
    private String handNumbnessReportPathTemp;

    // 重症单
    public static String ICUReportPath;
    @Value("${file.ICUReportPath}")
    private String ICUReportPathTemp;

    // 输血单
    public static String bloodApplyPath;
    @Value("${file.bloodApplyPath}")
    private String bloodApplyPathTemp;

    @PostConstruct
    public void init() {
        path = pathTemp;
        handNumbnessReportPath = handNumbnessReportPathTemp;
        ICUReportPath = ICUReportPathTemp;
        bloodApplyPath = bloodApplyPathTemp;
    }

    private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddhhmmss");
    // 病案首页独享
    private static SimpleDateFormat simpleDateFormat1 = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss");

    /**
     * 生成普通xml字符串
     */
    public static String generateXmlStr(OutPara outPara) throws Exception{
        Field[] fields = outPara.getClass().getDeclaredFields();
        // 创建XML
        Document document = DocumentHelper.createDocument();
        Element response = document.addElement("RESPONSE");
        for (Field field : fields) {
            field.setAccessible(true);
            response
                    .addElement(field.getName())
                    .addText(field.get(outPara).toString());
        }
        return document.getRootElement().asXML();
    }


    //--------------------------------------------向外抛出异常
    public static MessageDto xpathToBean(String xmlStr, Class tClass, String source) throws Exception {
        MessageDto messageDto = null;
        Document doc = null;
        Object obj = null;
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddhhmmss");
        SimpleDateFormat simpleDateFormat1 = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss");
        xmlStr = xmlStr.replaceAll("&", "&amp;");
        SAXReader saxReader = new SAXReader();
        saxReader.setXMLFilter(new XMLFilterImpl() {
            @Override
            public void characters(char[] ch, int start, int length) throws SAXException {
                String text = new String(ch, start, length);
                if (length == 1) {
                    if ((int) ch[0] == 160) {
                        char[] escape = " ".toCharArray();
                        super.characters(escape, 0, escape.length);
                        return;
                    }
                }
                super.characters(ch, start, length);
            }
        });
        doc = saxReader.read(new ByteArrayInputStream(xmlStr.getBytes("UTF-8")), "UTF-8");
        if (tClass != null) {
            messageDto = getInstance(source);
            obj = tClass.newInstance();
        } else {
            // 是检查报告的接口
            String str = "/ns:ClinicalDocument/ns:component/ns:structuredBody/ns:component/ns:section/ns:entry/ns:observation/ns:code[@displayName='检查报告科室']/../ns:value/text()";
            //-----------------------存在一个错误 如果str找不到 就会向外抛出异常org.apache.cxf.interceptor.Fault: org/jaxen/JaxenException
            XPath x = doc.createXPath(str);
            x.setNamespaceURIs(map);

            Node node = x.selectSingleNode(doc);
            if (node != null) {
                messageDto = getInstance(node.getText());
                if (messageDto != null) {
                    obj = messageDto.getObject();
                }
            }
        }
        if (obj != null && messageDto != null) {
            if (messageDto.getPropertiesPath() != null) {
                // 加载配置文件
                InputStream inputStream = new BufferedInputStream(new FileInputStream(new File(messageDto.getPropertiesPath())));
                Properties properties = new Properties();
                //加载格式化后的流
                properties.load(new InputStreamReader(inputStream, DEFAULT_ENCODING));

                Enumeration<?> enumeration = properties.propertyNames();
                while (enumeration.hasMoreElements()) {
                    String key = (String) enumeration.nextElement();
                    String xpath = properties.getProperty(key);
                    XPath x = doc.createXPath(xpath);
                    x.setNamespaceURIs(map);
                    // 根据properties的value获取xml的value
                    String value = "";
                    Node node = x.selectSingleNode(doc);
                    if (node != null) {
                        value = node.getText();
                    }
                    // 判断属性是否存在
                    String objField = existsField(obj.getClass(), key);
                    if (objField != null) {
                        PropertyDescriptor descriptor = new PropertyDescriptor(key, obj.getClass());
                        if (("class java.util.Date").equals(objField)) {
                            if (value != null && value.length() > 0) {
                                Date parse = simpleDateFormat.parse(value, new ParsePosition(0));
                                descriptor.getWriteMethod().invoke(obj, parse);
                            }
                        } else {
                            descriptor.getWriteMethod().invoke(obj, value);
                        }
                    }
                    String messageField = existsField(messageDto.getClass(), key);
                    if (messageField != null) {
                        PropertyDescriptor propertyDescriptor = new PropertyDescriptor(key, messageDto.getClass());
                        // 给messageDto赋值
                        if (("class java.util.Date").equals(messageField)) {
                            if (value != null && value.length() > 0) {
                                Date parse = null;
                                parse = simpleDateFormat.parse(value, new ParsePosition(0));
                                if (parse == null) {
                                    parse = simpleDateFormat1.parse(value, new ParsePosition(0));
                                }
                                propertyDescriptor.getWriteMethod().invoke(messageDto, parse);
                            }
                        } else {
                            propertyDescriptor.getWriteMethod().invoke(messageDto, value);
                        }
                    }
                }
                // 将对象转成json
                messageDto.setObject(JSON.toJSONString(obj));
                return messageDto;
            }
        }
        return null;
    }

    public static MessageDto getInstance(String source) {
        MessageDto messageDto = null;
        // 根据source判断是哪个实体类
        switch (source) {
            case InterfaceName.HANDNUMBNESS_REPORT: // 手麻系统
                messageDto = new MessageDto(HandNumbnessReport.class.getName(), new HandNumbnessReport(), AliasName.HANDNUMBNESS_REPORT, handNumbnessReportPath, source);
                break;
            case InterfaceName.ICU_REPORT: // ICU重症单
                messageDto = new MessageDto(ICUReport.class.getName(), new ICUReport(), AliasName.ICU_REPORT, ICUReportPath, source);
                break;
            case InterfaceName.BLOODAPPLY_REPORT: // 输血单
                messageDto = new MessageDto(BloodApply.class.getName(), new BloodApply(), AliasName.BLOODAPPLY_REPORT, bloodApplyPath, source);
                break;
        }
        return messageDto;
    }

    public static String existsField(Class clz, String fieldName) {
        try {
            Field declaredField = clz.getDeclaredField(fieldName);
            if (declaredField != null) {
                return declaredField.getGenericType().toString();
            }
        } catch (Exception e) {
        }
        return null;
    }

    /**
     * xml字符串转成对象 有子节点的
     */
    public static <T> T xmlStrToObject1(String xmlStr, Class<T> type) throws Exception{
        if (StringUtils.isEmpty(xmlStr)) {
            return null;
        }
        Document doc = null; // 将xml格式的字符串转换成Document对象
        T obj = null; // 创建 JavaBean 对象 最外层对象
        obj = type.newInstance();
        doc = DocumentHelper.parseText(xmlStr);
        // 获取根节点
        Element root = doc.getRootElement();
        // 获取根节点下的所有元素
        List<Element> zeroElements = root.elements(); // 所有的节点 OperDetails
        // 循环所有子元素
        if (zeroElements != null && zeroElements.size() > 0) {
            CommonTable commonTable = null;
            CommonTable1 commonTable1 = null;
            List<OperDetail> operDetails = null;
            List<DiagDetail> diagDetails = null;

            //20200525 新增其他节点，用于校验
            OtherData otherData = null;
            if (Objects.equals(type.toString(), MedicalRecordHomepage.class.toString())) {
                commonTable = new CommonTable();
                commonTable1 = new CommonTable1();
                diagDetails = new ArrayList<>();
                operDetails = new ArrayList<>();
                otherData = new OtherData();
            }
            for (Element zeroElement : zeroElements) {
                // Element zeroElement = (Element) zeroElements.get(i);
                Field[] fields = obj.getClass().getDeclaredFields();  // MedicalRecordHomepage下的属性
                String value = zeroElement.getTextTrim();
                for (Field field : fields) {
                    field.setAccessible(true);
                    PropertyDescriptor descriptor = new PropertyDescriptor(field.getName(), type);
                    Class aClass = field.getType();
                    if (field.getName().toString().equals(zeroElement.getName()) && field.getName().equals("OperDetails")) {
                        List<Element> elements = zeroElement.elements();   // 如果节点下面有子节点 OperDetail1 OperDetail2
                        for (Element nextChild : elements) {
                            List<Element> finalElements = nextChild.elements(); // 子节点下面还有子节点 YZY201
                            OperDetail operDetail = new OperDetail();
                            Field[] operDetailFields = operDetail.getClass().getDeclaredFields();
                            for (Element finalElement : finalElements) {
                                value = finalElement.getTextTrim();
                                for (Field operDetailField : operDetailFields) {
                                    operDetailField.setAccessible(true);
                                    if (operDetailField.getName().equals(finalElement.getName())) {
                                        operDetail = evaluation(operDetail, operDetailField, OperDetail.class, value);
                                        break;
                                    }
                                }
                            }
                            operDetails.add(operDetail);
                        }
                        // 给 MedicalRecordHomepage 属性 赋值
                        descriptor.getWriteMethod().invoke(obj, operDetails);
                    } else if (field.getName().toString().equals(zeroElement.getName()) && field.getName().equals("DiagDetails")) {
                        List<Element> elements = zeroElement.elements();   // 如果节点下面有子节点 OperDetail1 OperDetail2
                        for (Element nextChild : elements) {
                            List<Element> finalElements = nextChild.elements(); // 子节点下面还有子节点 YZY201
                            DiagDetail diagDetail = new DiagDetail();
                            Field[] diagDetailFields = diagDetail.getClass().getDeclaredFields();
                            for (Element finalElement : finalElements) {
                                value = finalElement.getTextTrim();
                                for (Field operDetailField : diagDetailFields) {
                                    operDetailField.setAccessible(true);
                                    if (operDetailField.getName().equals(finalElement.getName())) {
                                        diagDetail = evaluation(diagDetail, operDetailField, DiagDetail.class, value);
                                        break;
                                    }
                                }
                            }
                            diagDetails.add(diagDetail);
                        }
                        // 给 MedicalRecordHomepage 属性 赋值
                        descriptor.getWriteMethod().invoke(obj, diagDetails);
                    } else {
                        if (field.getGenericType().toString().equals(CommonTable.class.toString())) {
                            Field[] commonTableFields = aClass.getDeclaredFields();
                            for (Field commonTableField : commonTableFields) {
                                commonTableField.setAccessible(true);
                                if (commonTableField.getName().equals(zeroElement.getName())) {
                                    commonTable = evaluation(commonTable, commonTableField, aClass, value);
                                    break;
                                }
                            }
                            descriptor.getWriteMethod().invoke(obj, commonTable);
                        } else if (field.getGenericType().toString().equals(CommonTable1.class.toString())) {
                            Field[] commonTable1Fields = aClass.getDeclaredFields();
                            for (Field commonTableField : commonTable1Fields) {
                                commonTableField.setAccessible(true);
                                if (commonTableField.getName().equals(zeroElement.getName())) {
                                    commonTable1 = evaluation(commonTable1, commonTableField, aClass, value);
                                    break;
                                }
                            }
                            descriptor.getWriteMethod().invoke(obj, commonTable1);
                        }else if (field.getGenericType().toString().equals(OtherData.class.toString())) {
                            Field[] commonTable1Fields = aClass.getDeclaredFields();
                            for (Field otherDataField : commonTable1Fields) {
                                otherDataField.setAccessible(true);
                                if (otherDataField.getName().equals(zeroElement.getName())) {
                                    otherData = evaluation(otherData, otherDataField, aClass, value);
                                    break;
                                }
                            }
                            descriptor.getWriteMethod().invoke(obj, otherData);
                        }
                    }
                }
            }
        }
        return obj;
    }


    private static <T> T evaluation(T obj, Field field, Class<T> type, String value)  throws Exception {
        // 赋值
        String fieldName = field.getName();
        Type genericType = field.getGenericType();

        PropertyDescriptor descriptor = new PropertyDescriptor(fieldName, type);
        if (("java.lang.Integer").equals(genericType.getTypeName()) || genericType.getTypeName().equals("int")) {
            if (value != null && value.length() > 0) {
                descriptor.getWriteMethod().invoke(obj, Integer.parseInt(value + ""));
            }
        } else if ((Date.class.getName()).equals(genericType.getTypeName())) {
            if (value != null && value.length() > 0) {
                Date parse = null;
                parse = simpleDateFormat.parse(value, new ParsePosition(0));
                if (parse == null) {
                    parse = simpleDateFormat1.parse(value, new ParsePosition(0));
                }
                descriptor.getWriteMethod().invoke(obj, parse);
            }
        } else if (("java.lang.Long").equalsIgnoreCase(genericType.getTypeName()) || genericType.getTypeName().equals("long")) {
            if (value != null && value.length() > 0) {
                descriptor.getWriteMethod().invoke(obj, Long.parseLong(value + ""));
            }
        } else if (("java.lang.Boolean").equalsIgnoreCase(genericType.getTypeName()) || genericType.getTypeName().equals("boolean")) {
            if (value != null && value.length() > 0) {
                descriptor.getWriteMethod().invoke(obj, Boolean.valueOf(value + ""));
            }
        } else if (("java.lang.Double").equalsIgnoreCase(genericType.getTypeName()) || genericType.getTypeName().equals("double")) {
            if (value != null && value.length() > 0) {
                if(value.indexOf("-") != -1){
                    descriptor.getWriteMethod().invoke(obj, Double.parseDouble("0.00"));
                }else{
                    descriptor.getWriteMethod().invoke(obj, Double.parseDouble(value + ""));
                }
            }
        } else if (("java.lang.Float").equalsIgnoreCase(genericType.getTypeName()) || genericType.getTypeName().equals("float")) {
            if (value != null && value.length() > 0) {
                if(value.indexOf("-") != -1){
                    descriptor.getWriteMethod().invoke(obj, Float.parseFloat("0.00"));
                }else{
                    descriptor.getWriteMethod().invoke(obj, Float.parseFloat(value + ""));
                }

            }
        } else {
            descriptor.getWriteMethod().invoke(obj, value);
        }
        return obj;
    }
}