package cn.estsh.i3plus.pojo.base.common;

import cn.estsh.i3plus.pojo.base.annotation.XStreamCDATA;
import com.alibaba.fastjson.JSON;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.naming.NameCoder;
import com.thoughtworks.xstream.io.naming.NoNameCoder;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XppDomDriver;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.io.Writer;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import static cn.estsh.i3plus.pojo.base.common.XStreamFactory.toJavaBean;
import static cn.estsh.i3plus.pojo.base.common.XStreamFactory.toXml;

/**
 * @Description :   Xml 工厂
 * 官方DOC: https://www.tutorialspoint.com/xstream/xstream_discussion.htm
 * @Reference :
 * @Author : wei.peng
 * @CreateDate : 19-7-23 下午5:58
 * @Modify:
 **/
public class XStreamFactory {

    private static final Logger LOGGER = LoggerFactory.getLogger(XStreamFactory.class);

    private static final XStream xStream = XStreamFactory.getXStream();

    /* 转换重试次数 */
    private static final int RETRY_NUM = 3;

    public static final String CDATA_PREFIX = "<![CDATA[";
    public static final String CDATA_SUFFIX = "]]>";

    private XStreamFactory() {
    }

    public static XStream getXStream() {
        final NameCoder nameCoder = new NoNameCoder();
        XStream xStream = new XStream(new XppDomDriver(nameCoder) {
            @Override
            public HierarchicalStreamWriter createWriter(Writer out) {
                return new PrettyPrintWriter(out, nameCoder) {
                    boolean cdataFlag = false;
                    Class<?> targetClass = null;

                    @Override
                    public void startNode(String name, Class clazz) {
                        super.startNode(name, clazz);
                        if (targetClass == null) {
                            targetClass = clazz;
                        }
                        cdataFlag = isCDATA(targetClass, name);
                    }

                    @Override
                    public void writeText(QuickWriter writer, String text) {
                        if (cdataFlag) {
                            writer.write(CDATA_PREFIX);
                            writer.write(text);
                            writer.write(CDATA_SUFFIX);
                        } else {
                            writer.write(text);
                        }
                    }
                };
            }
        });

//        xStream.setMode(XStream.NO_REFERENCES);
        xStream.autodetectAnnotations(true);
//        XStream.setupDefaultSecurity(xStream);

        return xStream;
    }


    private static boolean isCDATA(Class<?> clazz, String fieldAlias) {
        //检查类本身
        boolean cdataFlag = isExistCDATA(clazz, fieldAlias);
        if (cdataFlag) {
            return cdataFlag;
        }
        //继续检查父类
        Class<?> superClazz = clazz.getSuperclass();
        while (!superClazz.equals(Object.class)) {
            cdataFlag = isExistCDATA(superClazz, fieldAlias);
            if (cdataFlag) {
                return cdataFlag;
            }
            superClazz = superClazz.getClass().getSuperclass();
        }
        return false;
    }

    /**
     * 检查是否有 @XStreamCDATA 注解
     *
     * @param clazz      clazz
     * @param fieldAlias fieldAlias
     * @return
     */
    private static boolean isExistCDATA(Class<?> clazz, String fieldAlias) {
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if (field.getAnnotation(XStreamCDATA.class) != null) {
                XStreamAlias xStreamAlias = field.getAnnotation(XStreamAlias.class);
                if (xStreamAlias != null && fieldAlias.equals(xStreamAlias.value())) {
                    return true;
                } else {
                    if (fieldAlias.equals(field.getName())) {
                        return true;
                    }
                }
            }
        }
        return false;
    }


    /**
     * Javabean 转XML
     *
     * @param <T>
     * @return xml字符串
     */
    public static <T> String toXml(T t) {
        xStream.processAnnotations(t.getClass());
        String headLine = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
        for (int i = 1; i < RETRY_NUM; i++) {
            try {
                return headLine + xStream.toXML(t);
            } catch (Exception e) {
                LOGGER.error("Bean To Xml Error Message:{} Number:{}", e.getMessage(), i);
            }
        }
        return null;
    }

    /**
     * Javabean 转XML
     *
     * @param <T>
     * @return xml字符串
     */
    public static <T> String toXmlNoHeadLine(T t) {
        xStream.processAnnotations(t.getClass());
        return xStream.toXML(t);
    }

    /**
     * XML字符串转javabean
     *
     * @param xmlStr xml字符串
     * @param <T>
     * @return Java对象
     */
    public static <T> T toJavaBean(String xmlStr) {
        return (T) xStream.fromXML(xmlStr);
    }
}

//
class Test {
    public static void main(String[] args) {
        User u = new User(0, "zhangsan0", "zhangsanpwd0");
        User u1 = new User(1, "zhangsan1", "zhangsanpwd");
        User u2 = new User(2, "zhangsan2", "zhangsanpwd");
        User u3 = new User(3, "zhangsan3", "zhangsanpwd");

        Role r1 = new Role(1, "Admin", "Admin1");
        Role r2 = new Role(2, "Admin", "Admin2");
        Role r3 = new Role(3, "Admin", "Admin3");

        DataAdapter ad = new DataAdapter(10086L, UUID.randomUUID().toString(), "sssfwef", u1);
        u1.setRole(r1);

        u2.getRoleList().add(r1);
        u2.getRoleList().add(r2);
        u2.getRoleList().add(r3);

        u3.setRole(r1);
        u3.getRoleList().add(r2);
        u3.getRoleList().add(r3);

        System.out.println(toXml(u) + "\n\n ");
        System.out.println(toXml(u1) + "\n\n ");
        System.out.println(toXml(u2) + "\n\n ");
        System.out.println(toXml(u3) + "\n\n ");
        System.out.println(toXml(ad) + "\n\n ");

        System.out.println(JSON.toJSONString(toJavaBean(toXml(ad))));

    }


    @Data
    @XStreamAlias("DataAdapter")
    static
//对应carInfos元素
    class DataAdapter implements Serializable {

        @XStreamAsAttribute
        private Long scId;
        private String key;
        @XStreamCDATA
        private String resultData;

        private Object auth;

        public DataAdapter(Long scId, String key, String resultData, Object auth) {
            this.scId = scId;
            this.key = key;
            this.resultData = resultData;
            this.auth = auth;
        }
    }

    @Data
    @XStreamAlias("user")
    static
//对应carInfos元素
    class User implements Serializable {
        private static final long serialVersionUID = -7554548655397869156L;

        @XStreamAsAttribute
        private Integer id;
        private String loginName;
        private String loginPwd;

        private Role role;
        private List<Role> roleList = new ArrayList<Role>();
        private List<Role> roleArray;

        public User() {
        }

        public User(Integer id, String loginName, String loginPwd) {
            this.id = id;
            this.loginName = loginName;
            this.loginPwd = loginPwd;
        }
    }

    @Data
    @XStreamAlias("role")
    static
//对应carInfos元素
    class Role implements Serializable {
        private static final long serialVersionUID = -3134157833696958743L;
        @XStreamAsAttribute
        private Integer id;
        private String roleName;
        private String roleCode;

        public Role() {
        }

        public Role(Integer id, String roleName, String roleCode) {
            this.id = id;
            this.roleName = roleName;
            this.roleCode = roleCode;
        }
    }
}