package xyz.wbsite.dbtool.javafx.manger;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.StringProperty;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import xyz.wbsite.dbtool.javafx.annotation.Property;
import xyz.wbsite.dbtool.javafx.po.DataBase;
import xyz.wbsite.dbtool.javafx.po.DictItem;
import xyz.wbsite.dbtool.javafx.po.Field;
import xyz.wbsite.dbtool.javafx.po.Frame;
import xyz.wbsite.dbtool.javafx.po.Module;
import xyz.wbsite.dbtool.javafx.po.Project;
import xyz.wbsite.dbtool.javafx.po.SelectItem;
import xyz.wbsite.dbtool.javafx.po.Table;
import xyz.wbsite.dbtool.web.frame.utils.ClassUtil;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class XmlManager {

    public Project inflate(File xmlFile) {
        Project project = new Project();

        if (xmlFile == null) {
            return project;
        } else if (!xmlFile.exists()) {
            System.err.println("文件不存在!");
        } else {
            //开始解析
            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
            FileInputStream inputStream = null;
            try {
                inputStream = new FileInputStream(xmlFile);
                Document document = documentBuilderFactory.newDocumentBuilder().parse(inputStream);
                Element projectElement = document.getDocumentElement();

                // 获取Project信息
                if ("project".endsWith(projectElement.getNodeName())) {
                    System.out.println("加载模型[" + xmlFile.getName() + "]");

                    // 映射属性
                    map(projectElement, project);

                    //获取所有Module
                    NodeList modules = projectElement.getElementsByTagName("module");
                    if (modules.getLength() > 0) {
                        for (int i = 0; i < modules.getLength(); i++) {
                            Element moduleElement = (Element) modules.item(i);
                            Module module = new Module();
                            // 映射属性
                            map(moduleElement, module);
                            project.putModule(module);

                            NodeList tables = moduleElement.getElementsByTagName("table");
                            if (tables.getLength() > 0) {
                                for (int j = 0; j < tables.getLength(); j++) {
                                    Element tableElement = (Element) tables.item(j);
                                    Table table = new Table();
                                    // 映射属性
                                    map(tableElement, table);

                                    NodeList fields = tableElement.getElementsByTagName("field");
                                    if (fields.getLength() > 0) {
                                        for (int k = 0; k < fields.getLength(); k++) {
                                            Element fieldElement = (Element) fields.item(k);
                                            Field field = new Field();
                                            // 映射属性
                                            map(fieldElement, field);
                                            table.putField(field);

                                            NodeList dictItems = fieldElement.getElementsByTagName("dictItem");
                                            for (int l = 0; l < dictItems.getLength(); l++) {
                                                Element di = (Element) dictItems.item(l);
                                                DictItem dictItem = new DictItem();
                                                map(di, dictItem);
                                                field.getDictItems().add(dictItem);
                                            }

                                            NodeList selectItems = fieldElement.getElementsByTagName("selectItem");
                                            for (int l = 0; l < selectItems.getLength(); l++) {
                                                Element di = (Element) selectItems.item(l);
                                                SelectItem selectItem = new SelectItem();
                                                map(di, selectItem);
                                                field.getSelectItems().add(selectItem);
                                            }
                                        }
                                    }
                                    table.setdBhandle(module);
                                    module.putTable(table);
                                }
                            }
                        }
                    }
                    System.out.println("模型初始化成功...");
                    return project;
                }
            } catch (Exception e) {
                System.out.println("文件：'" + xmlFile.getName() + "'解析失败!请检查语法是否正确!");
                e.printStackTrace();
            } finally {
                if (inputStream != null) {
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return project;
    }

    public boolean saveAs(File path, Project project) {
        DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder;
        //生成DOM模型
        try {
            builder = dbFactory.newDocumentBuilder();
            Document doc = builder.newDocument();

            // 写入Project信息
            Element projectElement = doc.createElement("project");
            doc.appendChild(projectElement);
            map(project, projectElement);


            Element modules = doc.createElement("modules");
            projectElement.appendChild(modules);
            for (Module md : project.getModules()) {
                // 写入Module信息
                Element module = doc.createElement("module");
                modules.appendChild(module);
                map(md, module);

                for (Table t : md.getTables()) {
                    // 写入table信息
                    Element table = doc.createElement("table");
                    module.appendChild(table);
                    map(t, table);
                    for (Field f : t.getFields()) {
                        // 写入field信息
                        Element field = doc.createElement("field");
                        map(f, field);

                        if (f.getDictItems().size() > 0) {
                            for (DictItem dictItem : f.getDictItems()) {
                                // 写入DictItem信息
                                Element dict = doc.createElement("dictItem");
                                field.appendChild(dict);
                                map(dictItem, dict);
                            }
                        }

                        if (f.getSelectItems().size() > 0) {
                            for (SelectItem dictItem : f.getSelectItems()) {
                                // 写入SelectItem信息
                                Element dict = doc.createElement("selectItem");
                                field.appendChild(dict);
                                map(dictItem, dict);
                            }
                        }
                        table.appendChild(field);
                    }
                }
            }
            //endregion

            //输出文件
            FileOutputStream stream = null;
            try {
                Transformer t = TransformerFactory.newInstance().newTransformer();

                //设置换行和缩进
                t.setOutputProperty(OutputKeys.INDENT, "yes");
                t.setOutputProperty(OutputKeys.METHOD, "xml");

                if (!path.exists()) {
                    path.mkdirs();
                }
                File file = new File(path, project.getName() + ".xml");
                if (!file.exists()) {
                    file.createNewFile();
                }
                stream = new FileOutputStream(file);
                t.transform(new DOMSource(doc), new StreamResult(stream));
                System.out.println("[" + file.getName() + "]保存成功!");
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            } finally {
                if (stream != null) {
                    try {
                        stream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
            e.printStackTrace();
            return false;
        }
        return true;
    }

    private void map(Object o, Element element) {
        List<java.lang.reflect.Field> fields = getProperties(o.getClass());
        try {
            for (java.lang.reflect.Field field : fields) {
                Property annotation = field.getAnnotation(Property.class);
                String value = annotation.value();
                Method method = ClassUtil.getMethod(field.getName(), o.getClass());
                Object invoke = method.invoke(o);
                element.setAttribute(value, invoke.toString());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void map(Element element, Object o) {
        List<java.lang.reflect.Field> fields = getProperties(o.getClass());
        try {
            for (java.lang.reflect.Field field : fields) {
                Property annotation = field.getAnnotation(Property.class);
                String value = annotation.value();
                String attribute = element.getAttribute(value);
                field.setAccessible(true);
                if (field.getType() == boolean.class || field.getType() == Boolean.class) {
                    Method method = ClassUtil.setMethod(field.getName(), o.getClass(), field.getType());
                    method.setAccessible(true);
                    method.invoke(o, getBoolean(attribute));
                } else if (field.getType() == String.class) {
                    Method method = ClassUtil.setMethod(field.getName(), o.getClass(), field.getType());
                    method.setAccessible(true);
                    method.invoke(o, attribute);
                } else if (field.getType() == DataBase.class) {
                    Method method = ClassUtil.setMethod(field.getName(), o.getClass(), field.getType());
                    method.setAccessible(true);
                    method.invoke(o, getDataBase(attribute));
                }else if (field.getType() == Frame.class) {
                    Method method = ClassUtil.setMethod(field.getName(), o.getClass(), field.getType());
                    method.setAccessible(true);
                    method.invoke(o, getFrame(attribute));
                } else if (field.getType() == StringProperty.class) {
                    Method method = ClassUtil.setMethod(field.getName(), o.getClass(), String.class);
                    method.setAccessible(true);
                    method.invoke(o, attribute);
                } else if (field.getType() == IntegerProperty.class) {
                    Method method = ClassUtil.setMethod(field.getName(), o.getClass(), int.class);
                    method.setAccessible(true);
                    method.invoke(o, getInt(attribute));
                } else if (field.getType() == BooleanProperty.class) {
                    Method method = ClassUtil.setMethod(field.getName(), o.getClass(), boolean.class);
                    method.setAccessible(true);
                    method.invoke(o, getBoolean(attribute));
                }
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    private List<java.lang.reflect.Field> getProperties(Class clazz) {
        List<java.lang.reflect.Field> list = new ArrayList();

        for (java.lang.reflect.Field declaredField : clazz.getDeclaredFields()) {
            if (declaredField.isAnnotationPresent(Property.class)) {
                list.add(declaredField);
            }
        }
        return list;
    }

    private int getInt(String str) {
        if (str == null || "".equals(str)) {
            return 0;
        }
        return Integer.parseInt(str);
    }

    private DataBase getDataBase(String str) {
        if (str == null || "".equals(str)) {
            return DataBase.MySQL;
        }
        return DataBase.valueOf(str);
    }

    private Frame getFrame(String str) {
        if (str == null || "".equals(str)) {
            return Frame.网页框架;
        }
        return Frame.valueOf(str);
    }

    private boolean getBoolean(String str) {
        if (str != null && str.matches("true|1")) {
            return true;
        }
        return false;
    }
}
