package com.yihu.jw.util.common;


import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import org.dom4j.*;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.dom4j.tree.DefaultAttribute;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.util.*;


public class XMLUtil {

    private static Logger logger = LoggerFactory.getLogger(XMLUtil.class);



    /**
     * 把xml字符串转换成 Document对象。
     *
     * @param xml 需要转换的xml字符串
     * @return 返回Document对象
     * @throws Exception 如果转换成Document对象异常的话抛出异常。
     */
    public static Document parseXml(String xml) throws Exception {
        try {
            return DocumentHelper.parseText(xml);
        } catch (DocumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            throw new Exception("传入的 xml 不是标准的xml字符串，请检查字符串是否合法。");
        }
    }

    /**
     * 转换成xml字符串
     *
     * @param xmlDoc 需要解析的xml对象
     * @throws Exception
     */
    public static String toXML_UTF_8(Document xmlDoc) throws Exception {
        return toXML(xmlDoc, "UTF-8", true);
    }

    /**
     * 转换成xml字符串
     *
     * @param xmlDoc 需要解析的xml对象
     * @throws Exception
     */
    public static String toXML_GBK(Document xmlDoc) throws Exception {
        return toXML(xmlDoc, "GBK", true);
    }

    /**
     * 转换成xml字符串
     *
     * @param xmlDoc   需要解析的xml对象
     * @param encoding 编码格式：UTF-8、GBK
     * @param iscom    是否为紧凑型格式
     * @return 修正完成后的xml字符串
     * @throws Exception
     */
    public static String toXML(Document xmlDoc, String encoding,
                               boolean iscom) throws Exception {
        ByteArrayOutputStream byteRep = new ByteArrayOutputStream();
        OutputFormat format = null;
        if (iscom) {
            format = OutputFormat.createCompactFormat();// 紧凑型格式
        } else {
            format = OutputFormat.createPrettyPrint();// 缩减型格式
        }
        format.setEncoding(encoding);// 设置编码
        format.setTrimText(false);// 设置text中是否要删除其中多余的空格
        XMLWriter xw;
        try {
            xw = new XMLWriter(byteRep, format);
            xw.write(xmlDoc);
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            throw new Exception("传入的编码格式错误，请传入正确的编码。");
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            throw new Exception("文档转换成xml字符串时出错。" + xmlDoc.asXML());
        }
        return byteRep.toString();
    }

    /**
     * 对节点Element 添加节点。
     *
     * @param e     需要添加的节点
     * @param name  添加的节点的名称
     * @param value 添加的内容
     *              <br/>
     *              Demo:
     *              < Root > aaa < /Root >
     *              <br/>
     *              call-->  addChildElement(root, "A", "a");
     *              <br/>
     *              result--> < Root >< A >a< /A >< /Root >
     */
    public static void addElement(Element e, String name, Object value) {
        if (isBlank(value)) {
            e.addElement(name).addText("");
        } else {
            e.addElement(name).addText(value.toString());
        }
    }

    /**
     * 判断对象是否为空！(null,"", "null")
     *
     * @param value
     * @return
     */
    private static boolean isBlank(String value) {
        if (value == null || value.length() == 0) {
            return true;
        } else if (StringUtils.isEmpty(value)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 判断对象是否非空！(null,"", "null")
     *
     * @param obj
     * @return
     */
    public static boolean isNotBlank(Object obj) {
        return !isBlank(obj);
    }

    /**
     * 判断对象是否为空！(null,"", "null")
     *
     * @param obj
     * @return
     */
    public static boolean isBlank(Object obj) {
        if (obj == null) {
            return true;
        }
        if (obj instanceof String) {
            return isBlank((String) obj);
        } else {
            return isBlank(obj.toString());
        }
    }

    public static void addElement(Element e, String name, Integer value) {
        Element current = e.addElement(name);
        if (value != null) {
            current.setText(Integer.toString(value));
        }
    }

    /**
     * 添加CDATA 类型节点
     *
     * @param e
     * @param name
     * @param value
     */
    public static void addCDATAElement(Element e, String name, String value) {
        Element current = e.addElement(name);
        if (value != null) {
            current.addCDATA(value.trim());
        }
    }

    /**
     * 添加CDATA 类型节点
     *
     * @param e
     * @param name
     * @param value
     */
    public static void addCDATAElement(Element e, String name, Integer value) {
        Element current = e.addElement(name);
        if (value != null) {
            current.addCDATA(value.toString());
        }
    }

    /**
     * 获取节点中的整数
     *
     * @throws Exception
     */
    public static int getInt(Element e, String name, boolean isMust) throws Exception {
        Element current = e.element(name);


        if (current == null || current.getText() == null || "".equals(current.getText().trim()) || current.getText().length() <= 0) {
            if (isMust) {
                throw new Exception("在 $" + e.asXML() + "$中获取节点：" + name + " 的值为空。");
            }
            return 0;
        }
        Integer i = 0;
        try {
            i = Integer.parseInt(current.getTextTrim());
        } catch (NumberFormatException e1) {
            i = 0;
            if (isMust) {
                throw new Exception("在 $" + e.asXML() + "$中获取节点：" + name + " 的值不是整形。");
            }
        }
        return i;
    }

    /**
     * 获取节点中的字符串
     *
     * @throws Exception
     */
    public static String getString(Element e, String name, boolean isMust) throws Exception {
        return getString(e, name, isMust, null);
    }

    /**
     * 获取节点中的字符串
     *
     * @param e
     * @param name
     * @param isMust 是否必填 true 必填  false非必填
     * @param defVal 默认值
     * @return
     * @throws Exception
     */
    public static String getString(Element e, String name, boolean isMust, String defVal) throws Exception {
        Element current = e.element(name);
        if (current == null || current.getText() == null || StringUtils.isEmpty(current.getText().trim())) {
            if (isMust) {
                throw new Exception("在 $" + e.asXML() + "$中获取节点：" + name + " 的值为空。");
            }
            return defVal;
        }
        return current.getTextTrim();
    }

    public static String ElementStringValue(String str) {
        if (str != null) {
            return str;
        } else {
            return "";
        }
    }

    public static Integer ElementIntegerValue(Integer str) {
        if (str != null) {
            return str;
        } else {
            return 0;
        }
    }

    public static String ElementValue(String str) {
        if (str != null && !str.equalsIgnoreCase("null")) {
            return str;
        } else {
            return "";
        }
    }

    public static String xml2json(String xml){
        StringWriter w = new StringWriter();
        ObjectMapper objectMapper = new ObjectMapper();
        XmlMapper xmlMapper = new XmlMapper();
        JsonParser jp;
        try {

            jp = xmlMapper.getFactory().createParser(xml);
            JsonGenerator jg = objectMapper.getFactory().createGenerator(w);
            while (jp.nextToken() != null) {
                String name = jp.getParsingContext().getCurrentName();
                if ("".equals(name)){
                    jp.overrideCurrentName("text");
                }
                jg.copyCurrentEvent(jp);
            }
            jp.close();
            jg.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return w.toString();
    }

    public static String formatXML(Document doc) throws Exception {
        StringWriter out = null;
        try {
            OutputFormat formate = OutputFormat.createPrettyPrint();
            out = new StringWriter();
            XMLWriter writer = new XMLWriter(out, formate);
            writer.write(doc);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            out.close();
        }
        return out.toString();
    }


    // ---------- map转XML--------------
    public static String map2xml(Map<String, String> dataMap) {
        synchronized (XMLUtil.class) {
            StringBuilder strBuilder = new StringBuilder();
            strBuilder.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
            strBuilder.append("<QUERY_FORM>");
            Set<String> objSet = dataMap.keySet();
            for (Object key : objSet) {
                if (key == null) {
                    continue;
                }
                strBuilder.append("<").append(key.toString()).append(">");
                Object value = dataMap.get(key);
                strBuilder.append(coverter(value));
                strBuilder.append("</").append(key.toString()).append(">");
            }
            strBuilder.append("</QUERY_FORM>");
            return strBuilder.toString();
        }
    }

    public static String map2xmlForWx(Map<String, String> dataMap) {
        synchronized (XMLUtil.class) {
            StringBuilder strBuilder = new StringBuilder();
            strBuilder.append("<xml>");
            Set<String> objSet = dataMap.keySet();
            for (Object key : objSet) {
                if (key == null) {
                    continue;
                }
                strBuilder.append("<").append(key.toString()).append(">");
                Object value = dataMap.get(key);
                strBuilder.append(coverter(value));
                strBuilder.append("</").append(key.toString()).append(">");
            }
            strBuilder.append("</xml>");
            return strBuilder.toString();
        }
    }

    public static String coverter(Object[] objects) {
        StringBuilder strBuilder = new StringBuilder();
        for (Object obj : objects) {
            strBuilder.append("<item className=").append(obj.getClass().getName()).append(">\n");
            strBuilder.append(coverter(obj));
            strBuilder.append("</item>\n");
        }
        return strBuilder.toString();
    }

    public static String coverter(Collection<?> objects) {
        StringBuilder strBuilder = new StringBuilder();
        for (Object obj : objects) {
            strBuilder.append("<item className=").append(obj.getClass().getName()).append(">\n");
            strBuilder.append(coverter(obj));
            strBuilder.append("</item>\n");
        }
        return strBuilder.toString();
    }

    public static String coverter(Object object) {
        if (object instanceof Object[]) {
            return coverter((Object[]) object);
        }
        if (object instanceof Collection) {
            return coverter((Collection<?>) object);
        }
        StringBuilder strBuilder = new StringBuilder();
        if (isObject(object)) {
            Class<? extends Object> clz = object.getClass();
            Field[] fields = clz.getDeclaredFields();

            for (Field field : fields) {
                field.setAccessible(true);
                if (field == null) {
                    continue;
                }
                String fieldName = field.getName();
                Object value = null;
                try {
                    value = field.get(object);
                } catch (IllegalArgumentException e) {
                    continue;
                } catch (IllegalAccessException e) {
                    continue;
                }
                strBuilder.append("<").append(fieldName)
                        .append(" className=\"").append(
                        value.getClass().getName()).append("\">");
                if (isObject(value)) {
                    strBuilder.append(coverter(value));
                } else if (value == null) {
                    strBuilder.append("null");
                } else {
                    strBuilder.append(value.toString() + "");
                }
                strBuilder.append("</").append(fieldName).append(">");
            }
        } else if (object == null) {
            strBuilder.append("null");
        } else {
            strBuilder.append(object.toString());
        }
        return strBuilder.toString();
    }

    private static boolean isObject(Object obj) {
        if (obj == null) {
            return false;
        }
        if (obj instanceof String) {
            return false;
        }
        if (obj instanceof Integer) {
            return false;
        }
        if (obj instanceof Double) {
            return false;
        }
        if (obj instanceof Float) {
            return false;
        }
        if (obj instanceof Byte) {
            return false;
        }
        if (obj instanceof Long) {
            return false;
        }
        if (obj instanceof Character) {
            return false;
        }
        if (obj instanceof Short) {
            return false;
        }
        if (obj instanceof Boolean) {
            return false;
        }
        return true;
    }

    // --------------------xml转map---------------

    private static void ele2map(Map map, Element ele)  {
        // 获得当前节点的子节点
        List<Element> elements = ele.elements();
        if (elements.size() == 0) {
            // 没有子节点说明当前节点是叶子节点，直接取值即可
            map.put(ele.getName(), ele.getText());
        } else if (elements.size() == 1) {
            // 只有一个子节点说明不用考虑list的情况，直接继续递归即可
            Map<String, Object> tempMap = new HashMap<String, Object>();
            ele2map(tempMap, elements.get(0));
            //设置标签属性
            setAttributes(tempMap,elements.get(0));
            if (tempMap.size()==1){
                map.put(ele.getName(), ele.getText());
            }else {
                map.put(ele.getName(), tempMap);
            }
        } else {
            // 多个子节点的话就得考虑list的情况了，比如多个子节点有节点名称相同的
            // 构造一个map用来去重
            Map<String, Object> tempMap = new HashMap<String, Object>();
            for (Element element : elements) {
                tempMap.put(element.getName(), null);
            }
            Set<String> keySet = tempMap.keySet();
            for (String string : keySet) {
                Namespace namespace = elements.get(0).getNamespace();
                List<Element> elements2 = ele.elements(new QName(string, namespace));
                // 如果同名的数目大于1则表示要构建list
                if (elements2.size() > 1) {
                    List<Map> list = new ArrayList<Map>();
                    for (Element element : elements2) {
                        Map<String, Object> tempMap1 = new HashMap<String, Object>();
                        ele2map(tempMap1, element);
                        setAttributes(tempMap1,element);//属性值设置
                        list.add(tempMap1);

                    }
                    map.put(string, list);
                } else {
                    // 同名的数量不大于1则直接递归去
                    Map<String, Object> tempMap1 = new HashMap<String, Object>();
                    ele2map(tempMap1, elements2.get(0));
                    setAttributes(tempMap1,elements2.get(0));//属性值设置
                    if (tempMap1.containsKey(string)) {
                        map.put(string, tempMap1.get(string));
                    }else {
                        map.put(string, tempMap1);
                    }

                }
            }
        }
    }

    /**
     *
     * 设置属性值（xml转map）
     * @param map
     * @param element
     */
    public static void setAttributes(Map map,Element element){
        List list = element.attributes();
        Map<String,Object> attrMap = null;
        DefaultAttribute e = null;
        if (!list.isEmpty()) {
            attrMap = new HashMap<>();
            for (int i = 0; i < list.size(); i++) {
                e = (DefaultAttribute) list.get(i);
                attrMap.put(e.getName(),e.getText());
            }
            attrMap.put("text",element.getText());
        }

        if (attrMap!=null && !attrMap.isEmpty()){
            map.put(element.getName(),attrMap);
        }
    }

//============================================
    public static Object xml2map(Element element) {
        System.out.println(element);
        Map<String, Object> map = new HashMap<String, Object>();
        List<Element> elements = element.elements();
        if (elements.size() == 0) {
            map.put(element.getName(), element.getText());
            if (!element.isRootElement()) {
                return element.getText();
            }
        } else if (elements.size() == 1) {
            map.put(elements.get(0).getName(), xml2map(elements.get(0)));
        } else if (elements.size() > 1) {
            // 多个子节点的话就得考虑list的情况了，比如多个子节点有节点名称相同的
            // 构造一个map用来去重
            Map<String, Element> tempMap = new HashMap<String, Element>();
            for (Element ele : elements) {
                tempMap.put(ele.getName(), ele);
            }
            Set<String> keySet = tempMap.keySet();
            for (String string : keySet) {
                Namespace namespace = tempMap.get(string).getNamespace();
                List<Element> elements2 = element.elements(new QName(string, namespace));
                // 如果同名的数目大于1则表示要构建list
                if (elements2.size() > 1) {
                    List<Object> list = new ArrayList<Object>();
                    for (Element ele : elements2) {
                        list.add(xml2map(ele));
                    }
                    map.put(string, list);
                } else {
                    // 同名的数量不大于1则直接递归去
                    map.put(string, xml2map(elements2.get(0)));
                }
            }
        }

        return map;
    }
//    ============================================


    public static Map xmltoMap(String xml) {
        if (!StringUtils.isEmpty(xml)) {
            try {
                Map map = new HashMap();
                Document document = DocumentHelper.parseText(xml);
                Element nodeElement = document.getRootElement();
                List node = nodeElement.elements();
                for (Iterator it = node.iterator(); it.hasNext(); ) {
                    Element elm = (Element) it.next();
                    List list = elm.elements();
                    if(list.size() > 0){
                        Map xmltoMap = xmltoMap(elm.asXML());
                        map.putAll(xmltoMap);
                    }else{
                        map.put(elm.getName(), elm.getText());
                    }
                    elm = null;
                }
                node = null;
                nodeElement = null;
                document = null;
                return map;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    public static Map xmltoMap2(String xml) {
        Map<String,Object> map = new HashMap<String,Object>();
        Document doc;
        try {
            doc = DocumentHelper.parseText(xml);
            Element el = doc.getRootElement();
            map.put(el.getName(), DiGui(el));
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        return map;
    }


    public static void main(String[] args) {

        String aa="<HisTrans>\n" +
                "  <TranCode>GetHealthYE</TranCode>\n" +
                "  <RespMsg />\n" +
                "  <RespCode>0</RespCode>\n" +
                "  <Order>\n" +
                "    <HealthYE>3.94</HealthYE>\n" +
                "    <HospitalizationYE />\n" +
                "  </Order>\n" +
                "</HisTrans>";

//        String aa= "<HisTrans>\n" +
//                "  <TranCode>GetHealthYE</TranCode>\n" +
//                "  <RespMsg />\n" +
//                "  <RespCode>0</RespCode>\n" +
//                "  <Order>\n" +
//                "    <HealthYE>3.94</HealthYE>\n" +
//                "    <HospitalizationYE></HospitalizationYE>\n" +
//                "  </Order>\n" +
//                "</HisTrans>";
        Map map = xmltoMap(aa);
        System.out.println(map);

    }

    public static List xmltoList(String xml) {
        if (!StringUtils.isEmpty(xml)) {
            try {
                List<Map> list = new ArrayList<Map>();
                Document document = DocumentHelper.parseText(xml);
                Element nodesElement = document.getRootElement();
                List nodes = nodesElement.elements();
                for (Iterator its = nodes.iterator(); its.hasNext(); ) {
                    Element nodeElement = (Element) its.next();
                    Map map = xmltoMap(nodeElement.asXML());
                    list.add(map);
                    map = null;
                }
                nodes = null;
                nodesElement = null;
                document = null;
                return list;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }


    public static Map DiGui(Element rootElement) {
        // 对节点进行判断
        int flag = hasGradeChrid(rootElement);
        // 存储本层的map,采用LinkedHashMap,保证的顺序
        Map<String, Object> map_this = new LinkedHashMap<String, Object>();
        // 存储子节点的map，采用LinkedHashMap,保证的顺序
        Map<String, Object> map_children = new LinkedHashMap<String, Object>();
        // 获取节点迭代器
        Iterator<Element> iterator = rootElement.elementIterator();
        if (flag == 0) {// 说明该节点所有子节点均有子节点,进入递归
            int num = 0;
            while (iterator.hasNext()) {// 依次继续对节点进行操作
                Element childelement = iterator.next();
                map_children = DiGui(childelement);
                map_this.put(childelement.getName() + "_" + num, map_children);
                num++;
            }
        }
        if (flag == 1) {// 说明该节点的所有子节点均无子节点,封装数据
            while (iterator.hasNext()) {
                Element childelement = iterator.next();
                map_this.put(childelement.getName(),
                        (String) childelement.getData());
            }
        }
        if (flag == 2) {// 说明了该节点的子节点有些拥有子节点，有些不拥有
            int nodes = rootElement.elements().size();// 获取子节点个数
            while (nodes >= 1) {
                nodes--;
                int num = 0;//为了让循环重复的节点，避免了key的冲突
                Element element = iterator.next();
                flag = hasGradeChrid(element);//对节点进行判断
                if (flag == 1) {                          //对于子节点，如果只是普通的子节点，那么直接将数进行封装
                    // 封装如map,String,String
                    map_this.put(element.getName(), element.getData());
                }
                else{                                     //非普通子节点，那么进行递归
                    map_children = DiGui(element);
                    map_this.put(element.getName() + "_" + num, map_children);//为了让循环重复的节点，避免了key的冲突
                }
            }
        }
        return map_this;
    }

    /**
     * 用于判断该节点的类型 0：说明该节点所有子节点均有子节点 1：说明该节点的所有子节点均无子节点 2：说明了该节点的子节点有些拥有子节点，有些不拥有
     *
     * @param rootelement
     * @return
     */
    public static int hasGradeChrid(Element rootelement) {
        int flag = 1;// 初始为1，用与处理对没有子节点的节点进行判断
        StringBuffer flag_arr = new StringBuffer();
        Iterator<Element> iterator = rootelement.elementIterator();
        while (iterator.hasNext()) {
            Element element = iterator.next();// 获取入参rootelement节点的子节点
            // Iterator<Element> iterator_chirld = element.elementIterator();
            if (element.elements().size() > 0) {// 判断是否有子节点
                flag_arr.append("0");
            } else {
                flag_arr.append("1");
            }
        }
        // 如果只包含0，说明该节点所有子节点均有子节点
        if (flag_arr.toString().contains("0")) {
            flag = 0;
        }
        // 如果只包含1，说明该节点的所有子节点均无子节点
        if (flag_arr.toString().contains("1")) {
            flag = 1;
        }
        // 如果同时包含了,0,1,说明了该节点的子节点有些拥有子节点，有些不拥有
        if (flag_arr.toString().contains("0")
                && flag_arr.toString().contains("1")) {
            flag = 2;
        }
        return flag;
    }

    /* ============================== 以下暂时添加，后面可删除 ========================================= */
//    public static String maptoXml(Map map) {
//        Document document = DocumentHelper.createDocument();
//        Element nodeElement = document.addElement("node");
//        for (Object obj : map.keySet()) {
//            Element keyElement = nodeElement.addElement("key");
//            keyElement.addAttribute("label", String.valueOf(obj));
//            keyElement.setText(String.valueOf(map.get(obj)));
//        }
//        return doc2String(document);
//    }
//
//    public static String listtoXml(List list) throws Exception {
//        Document document = DocumentHelper.createDocument();
//        Element nodesElement = document.addElement("nodes");
//        int i = 0;
//        for (Object o : list) {
//            Element nodeElement = nodesElement.addElement("node");
//            if (o instanceof Map) {
//                for (Object obj : ((Map) o).keySet()) {
//                    Element keyElement = nodeElement.addElement("key");
//                    keyElement.addAttribute("label", String.valueOf(obj));
//                    keyElement.setText(String.valueOf(((Map) o).get(obj)));
//                }
//            } else {
//                Element keyElement = nodeElement.addElement("key");
//                keyElement.addAttribute("label", String.valueOf(i));
//                keyElement.setText(String.valueOf(o));
//            }
//            i++;
//        }
//        return doc2String(document);
//    }
//
//    public static String doc2String(Document document) {
//        String s = "";
//        try {
//            // 使用输出流来进行转化
//            ByteArrayOutputStream out = new ByteArrayOutputStream();
//            // 使用UTF-8编码
//            OutputFormat format = new OutputFormat("   ", true, "UTF-8");
//            XMLWriter writer = new XMLWriter(out, format);
//            writer.write(document);
//            s = out.toString("UTF-8");
//        } catch (Exception ex) {
//            ex.printStackTrace();
//        }
//        return s;
//    }

}
