package com.artfess.base.util;

import java.io.ByteArrayOutputStream;
import java.io.OutputStreamWriter;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;

/**
 * xml操作工具类
 * 
 * @company 阿特菲斯信息技术有限公司
 * @author heyifan
 * @email heyf@jee-soft.cn
 * @date 2018年4月11日
 */
public class XmlUtil {
	private static XmlMapper xmlMapper = new XmlMapper();
	/**
	 * 根据Element获取这个对应的元素的xml。
	 * @param element
	 * @return 
	 * String
	 * @exception 
	 * @since  1.0.0
	 */
	public static String getXML(Element element) {
		TransformerFactory transFactory = TransformerFactory.newInstance();
		try {
			Transformer transformer = transFactory.newTransformer();
			transformer.setOutputProperty("encoding", "UTF-8");
			transformer.setOutputProperty("indent", "yes");

			DOMSource source = new DOMSource();
			source.setNode(element);
			StreamResult result = new StreamResult();

			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			result.setOutputStream(baos);
			transformer.transform(source, result);
			return baos.toString("UTF-8");



		} catch (Exception e) {
			return "";
		}
	}

	/**
	 * 在当前节点下，获取指定名称的子元素。
	 * 
	 * @param ruleNode
	 * @param name
	 * @return Element
	 * @exception
	 * @since 1.0.0
	 */
	public static Element getChildNodeByName(Element ruleNode, String name) {
		NodeList nodeList = ruleNode.getChildNodes();
		for (int i = 0; i < nodeList.getLength(); i++) {			
			Element node = getElement(nodeList.item(i));				
			if (node!=null && node.getTagName()!=null && node.getTagName().equalsIgnoreCase(name)) {
				return node;
			}			
		}
		return null;
	}

	private static Element getElement(Object object){
		if(object!=null && object instanceof Element){
			Element node = (Element) object;
			return node;
		}
		return null;
	}

	public static Document getDocument(String xmlFilePath){
		Document doc = null;
		try {			
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			DocumentBuilder db = dbf.newDocumentBuilder();		
			doc = db.parse(xmlFilePath);					
		} catch (Exception e) {
			e.printStackTrace();
		}
		return doc;
	}

	public static NodeList selectNodes(String express, Object source) {// 查找节点，返回符合条件的节点集。
		NodeList result = null;
		XPathFactory xpathFactory = XPathFactory.newInstance();
		XPath xpath = xpathFactory.newXPath();
		try {
			result = (NodeList) xpath.evaluate(express, source,
					XPathConstants.NODESET);
		} catch (XPathExpressionException e) {
			e.printStackTrace();
		}

		return result;
	}

	public static Element selectFirstElement(String express,Object source){
		NodeList list = selectNodes(express, source);
		for(int i=0;i<list.getLength();i++){
			Object obj = list.item(i);
			if(obj instanceof Element){
				return (Element)obj;
			}
		}
		return null;
	}

	/**
	 * 验证格式是否正确
	 * @param root
	 * @param firstName 第一个节点名
	 * @param nextName 第二个节点名
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public static void checkXmlFormat(org.dom4j.Element root,String firstName,String nextName) throws Exception {
		String msg = "导入文件格式不对";
		if (!root.getName().equals(firstName))
			throw new Exception(msg);
		List<org.dom4j.Element> itemLists = root.elements();
		for (org.dom4j.Element elm : itemLists) {
			if (!elm.getName().equals(nextName))
				throw new Exception(msg);
		}

	}
	
	/**
	 * Xml格式转为Json格式
	 * @param xml	xml格式字符串
	 * @return		json格式字符串
	 */
	public static String toJson(String xml) {
		StringWriter w = new StringWriter();
		try (JsonParser jp = xmlMapper.getFactory().createParser(xml);JsonGenerator jg = JsonUtil.getMapper().getFactory().createGenerator(w);){
			
			while (jp.nextToken() != null) {
				jg.copyCurrentEvent(jp);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return w.toString();
	}
	
	/**
	 * 将一个Xml的Node转换为对象
	 * @param node
	 * @param clazz
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> T transformToObject(Node node, Class<T> clazz) throws Exception{
		PipedOutputStream output = new PipedOutputStream();
		OutputStreamWriter writer = new OutputStreamWriter(output);
		PipedInputStream input = new PipedInputStream(output);
		TransformerFactory.newInstance().newTransformer().transform( new DOMSource(node), new StreamResult(writer));
		Object object = JAXBUtil.unmarshall(input, clazz);
		return (T) object;
	}
	
	/**
	 * 根据类的Xml配置转为bean，只能是对象不能是集合
	 * @param data
	 * @return obj
	 * @throws IllegalAccessException 
	 * @throws InstantiationException 
	 */
	@SuppressWarnings("unchecked")
	public static <T> T mapToXMLBean(Map<String, Object> data, Class<T> beanClass) throws Exception {
		Object object = beanClass.newInstance();
		List<Field> fields = ReflectUtil.getAllField(beanClass);
		return (T) setDataToObject(data, object, fields);
	}
	
	/**
	 * 根据属性列表设置map属性到bean对象
	 * @param data
	 * @param object
	 * @param fields
	 * @return
	 * @throws Exception 
	 */
	@SuppressWarnings("unchecked")
	private static Object setDataToObject(Map<String, Object> data, Object object, List<Field> fields) throws Exception {
		for (Field field : fields) {
			Map<String, Object> fromMap = data;
			String propName = field.getName();
			XmlElement element = field.getAnnotation(XmlElement.class);
			if (element != null && !"##default".equals(element.name())) {
				propName = element.name();
			}
			XmlElementWrapper wapper = field.getAnnotation(XmlElementWrapper.class);
			if (wapper != null) {
				fromMap = (Map<String, Object>) data.get(wapper.name());
			}
			Object propValue = fromMap.get(propName);
			if (propValue != null) {
				Class<?> fieldClazz = field.getType();
				if (ReflectUtil.isBasicType(fieldClazz)) {
					ReflectUtil.setFieldValue(object, field, StringUtil.parserObject(propValue.toString(), fieldClazz));
				} else if (fieldClazz.isAssignableFrom(List.class)) {
					Class<?> propClazz = ReflectUtil.getGenericType(field.getGenericType(), 0);
					List<Object> listObjs = new ArrayList<>();
					if (List.class.isInstance(propValue)) {
						List<Map<String, Object>> list = (List<Map<String, Object>>) propValue;
						for (Map<String, Object> propMap: list) {
							listObjs.add(mapToXMLBean(propMap, propClazz));
						}
					} else if (Map.class.isInstance(propValue)) {
						listObjs.add(mapToXMLBean((Map<String, Object>) propValue, propClazz));
					}
					ReflectUtil.setFieldValue(object, field, listObjs);
				} else if (Map.class.isInstance(propValue)) {
					ReflectUtil.setFieldValue(object, field, mapToXMLBean((Map<String, Object>) propValue, fieldClazz));
				}
			}
		}
		return object;
	}
	
	/**
	 * 将Xml的Dom转为map，转换后类型不一定准确，特别是对于list只有一个时
	 * @param n	dom节点
	 * @return map
	 */
	@SuppressWarnings("unchecked")
	public static void transformToMap(Map<String, Object> result, Node n) {

		if (n.hasChildNodes()) {
			NodeList nl = n.getChildNodes();
			Map<String, Integer> nodeNum = new HashMap<>();
			for (int i = 0; i < nl.getLength(); i++) {
				Node child = nl.item(i);
				if (child.getNodeType() == Node.ELEMENT_NODE) {
					String nodeName = child.getLocalName();
					nodeNum.put(nodeName, nodeNum.containsKey(nodeName) ? nodeNum.get(nodeName) + 1 : 1);
				}
			}
			for (int i = 0; i < nl.getLength(); i++) {
				Node child = nl.item(i);
				if (child.getNodeType() == Node.ELEMENT_NODE) {
					String nodeName = child.getLocalName();
					if (nodeNum.get(nodeName) > 1) {
						List<Map<String, Object>> arrayNode = (List<Map<String, Object>>) result.get(nodeName);
						if (arrayNode == null) {
							arrayNode = new ArrayList<Map<String, Object>>();
							result.put(nodeName, arrayNode);
						}
						Map<String, Object> childNodeMap = new HashMap<>();
						transformToMap(childNodeMap, child);
						arrayNode.add(childNodeMap);
					} else {
						if (child.getChildNodes().getLength() == 1 && child.getFirstChild().getNodeType() == Node.TEXT_NODE) {
							Node text = child.getFirstChild();
							result.put(child.getLocalName(), text.getNodeValue());
						} else {
							Map<String, Object> childNodeMap = new HashMap<>();
							transformToMap(childNodeMap, child);
							result.put(child.getLocalName(), childNodeMap);
						}
					}
				}
			}
		}
	}
	
}
