package cn.edu.whu.boot.common.utils.xml;

import cn.edu.whu.boot.common.domain.CommonConstant;
import cn.edu.whu.boot.common.domain.ResultMessage;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;

import java.util.*;

/**
 * 项目使用 JDOM 和 dom4j 写 XML 文件!
 * JsonToElementUtil 工具类的作用就是将 Json 中数据写到 Element 中
 */
public class JsonToElementUtil{
    private final static Set<String> defaultTrue = new HashSet<>(Collections.singletonList("enable"));
    /**
     * String xmlConfigJsonString 是个 Json 字符串，该字符串封装了前端配置的 XML 文件内容!
     * 也就是 CommonConstant 类中提到的 "config":{...} ! 具体解释参考 CommonConstant 类!!!
     * @return ResultMessage 这里方便判断生成成功还是失败，如果失败，失败的原因返回给网页端
     */
    public static ResultMessage generateDocument(String xmlConfigJsonString){
        JSONObject obj = JSON.parseObject(xmlConfigJsonString);
        JSONObject configObj = (JSONObject) obj.get(CommonConstant.TASK_XML_CONFIG);
        JSONObject objectMeta = (JSONObject) configObj.get(CommonConstant.TAG_META);
        JSONArray objectTables = (JSONArray) configObj.get(CommonConstant.TAG_TABLES);
        // 定义根节点
        Element config = new Element(CommonConstant.TASK_XML_CONFIG);
        Document document = new Document(config);

        //定义根节点下的 meta 、 tables 一级节点
        Element meta = new Element(CommonConstant.TAG_META);
        Element tables = new Element(CommonConstant.TAG_TABLES);

        // 定义 meta 下的 drawtool、position、basemap 二级节点，并将接受到的 json 字符串属性传进节点中
        JsonToElementUtil.jsonToElementForDrawTool(meta, objectMeta);
        JsonToElementUtil.jsonToElementForType(meta, objectMeta);
        JsonToElementUtil.jsonToElementForLayer(meta, objectMeta);

        // 对传来的 objectTables 进行多项数据核对，确保数据不出错之后再生成xml文件，但同时也要给提示哪里出错
        ResultMessage checkMessage = CheckTableDataUtil.checkTables(objectTables);
        if(!checkMessage.isSuccess()){
            return checkMessage;
        }

        // 定义tables 下的 table 二级节点，并将接受到的json字符串属性传进节点中，此处有多个table节点
        JsonToElementUtil.jsonToElementForTable(tables, objectTables);
        config.addContent(meta);
        config.addContent(tables);
        return ResultMessage.success(document);
    }
    public static ResultMessage createTaskgenerateDocument(String xmlConfigJsonString){
        JSONObject obj = JSON.parseObject(xmlConfigJsonString);
        JSONObject objectMeta = (JSONObject) obj.get(CommonConstant.TAG_META);
        JSONArray objectTables = (JSONArray) obj.get(CommonConstant.TAG_TABLES);
        // 定义根节点
        Element config = new Element(CommonConstant.TASK_XML_CONFIG);
        Document document = new Document(config);

        //定义根节点下的 meta 、 tables 一级节点
        Element meta = new Element(CommonConstant.TAG_META);
        Element tables = new Element(CommonConstant.TAG_TABLES);

        // 定义 meta 下的 drawtool、position、basemap 二级节点，并将接受到的 json 字符串属性传进节点中
        JsonToElementUtil.jsonToElementForDrawTool(meta, objectMeta);
        JsonToElementUtil.jsonToElementForType(meta, objectMeta);
        JsonToElementUtil.jsonToElementForLayer(meta, objectMeta);

        // 对传来的 objectTables 进行多项数据核对，确保数据不出错之后再生成xml文件，但同时也要给提示哪里出错
        ResultMessage checkMessage = CheckTableDataUtil.checkTables(objectTables);
        if(!checkMessage.isSuccess()){
            return checkMessage;
        }

        // 定义tables 下的 table 二级节点，并将接受到的json字符串属性传进节点中，此处有多个table节点
        JsonToElementUtil.jsonToElementForTable(tables, objectTables);
        config.addContent(meta);
        config.addContent(tables);
        return ResultMessage.success(document);
    }
    /**
     * <drawtool> 新增了group分组 所以单独独立出一个方法
     */
    private static void jsonToElementForDrawTool(Element rootElement, JSONObject objectMeta){
        Element childElement = new Element(CommonConstant.TAG_DRAWTOOL);
        JSONObject jsonObject = objectMeta.getJSONObject(CommonConstant.TAG_DRAWTOOL);
        for(String group : jsonObject.keySet()){
            if(group.equalsIgnoreCase("default")){
                generateTypeElements(jsonObject, group, childElement);
            }else{
                Element groupElement = new Element(CommonConstant.TAG_DRAWTOOL_GROUP);
                groupElement.setAttribute(CommonConstant.KEY, group);
                generateTypeElements(jsonObject, group, groupElement);
                childElement.addContent(groupElement);
            }
        }
        rootElement.addContent(childElement);
    }

    private static void generateTypeElements(JSONObject jsonObject, String group, Element groupElement) {
        Object value = jsonObject.get(group);
        if (value instanceof JSONArray) {
            JSONArray jsonArray = (JSONArray) value;
            for (Object obj : jsonArray) {
                if (obj instanceof JSONObject) {
                    generateTypeElement(groupElement, (JSONObject) obj);
                }
            }
        } else if (value instanceof JSONObject) {
            // 如果是 JSONObject，也可以根据需要进行处理
            generateTypeElement(groupElement, (JSONObject) value);
        } else {
            // 处理其他类型，这里可以根据实际情况添加日志或者其他处理逻辑
            System.err.println("Unexpected type for group: " + group + ", value: " + value);
        }
    }

    private static void generateTypeElement(Element groupElement, JSONObject obj) {
        Element grandchildElement = new Element(CommonConstant.TAG_TYPE);
        Set<String> keySet = obj.keySet();
        for (String key : keySet) {
            grandchildElement.setAttribute(key, obj.get(key).toString());
        }
        groupElement.addContent(grandchildElement);
    }

    /**
     * 从 XML 配置文件中可以看出 <position> 都只有 <type name = ""/> 这样的标签，
     * 所以该方法的目的就是将 Json 中数据生成 <type> 标签
     */
    private static void jsonToElementForType(Element rootElement, JSONObject objectMeta){
        Element childElement = new Element(CommonConstant.TAG_POSITIOON);
        JSONArray jsonArray = (JSONArray) objectMeta.get(CommonConstant.TAG_POSITIOON);
        for(Object obj : jsonArray){
            generateTypeElement(childElement, (JSONObject) obj);
        }
        rootElement.addContent(childElement);
    }

    /**
     * 从 XML 配置文件中可以看出 <basemap> 与 <drawtool> 和 <position> 的标签不一样，
     * <basemap> 中的标签样式如下：
     * <layer group = "" name = "" type = "" subType = "" index = ""/>
     * 所以该方法的目的就是将 Json 中数据生成 <layer> 标签
     */
    private static void jsonToElementForLayer(Element rootElement, JSONObject objectMeta){
        Element childElement = new Element(CommonConstant.TAG_BASEMAP);
        JSONArray jsonArray = (JSONArray) objectMeta.get(CommonConstant.TAG_BASEMAP);
        for(Object obj : jsonArray){
            JSONObject jsonObj = (JSONObject) obj;

            // 针对 basemap节点的 标签属性进行判断,若为空则不加入 attributes 中
            Set<String> keySet = jsonObj.keySet();
            List<Attribute> attributes = new ArrayList<>();
            for(String key : keySet){
                if(!"".equals(jsonObj.get(key))){
                    Attribute attribute = new Attribute(key, jsonObj.get(key).toString());
                    attributes.add(attribute);
                }
            }
            Element grandchildElement = new Element(CommonConstant.TAG_LAYER);
            grandchildElement.setAttributes(attributes);
            childElement.addContent(grandchildElement);
        }
        rootElement.addContent(childElement);
    }

    /**
     * 该方法的目的就是将 Json 中数据生成 <table> 标签
     */
    private static void jsonToElementForTable(Element rootElement, JSONArray tableArray){
        for(Object obj : tableArray){
            JSONObject tableObj = (JSONObject) obj;
            Set<String> keySet = tableObj.keySet();
            List<Attribute> tableAttributes = new ArrayList<>();

            // 针对于 <table> 标签添加属性
            for(String key : keySet){
                if(!CommonConstant.TABLES_FIELDS.equals(key)){
                    Attribute attribute = new Attribute(key, tableObj.getString(key));
                    tableAttributes.add(attribute);
                }
            }
            Element childElement = new Element(CommonConstant.TAG_TABLE);
            childElement.setAttributes(tableAttributes);

            // 针对于 <table> 节点 添加 <Field>子节点
            JSONArray fieldsArray = (JSONArray) tableObj.get(CommonConstant.TABLES_FIELDS);

            for(Object fieldObj : fieldsArray){
                Element grandchildElement = new Element(CommonConstant.TAG_FIELD);

                // 定义变量，为后面存储 "selector" 数据使用(如果存在的话)
                JSONArray selectorJsonArray = null;
                JSONObject fieldJsonObj = (JSONObject) fieldObj;
                Set<String> fieldKeySet = fieldJsonObj.keySet();
                List<Attribute> fieldAttributes = new ArrayList<>();

                // 针对于 <field> 标签添加属性 , 特别是针对于 type = "checkbox" 的属性
                for(String key : fieldKeySet){
                    boolean defaultContains = defaultTrue.contains(key);
                    if(CommonConstant.FIELD_SELECTOR.equals(key)){
                        selectorJsonArray = (JSONArray) fieldJsonObj.get(key);
                    }
                    else if(CommonConstant.KEY.equals(key)){
                        Attribute attribute = new Attribute(key, fieldJsonObj.getString(key));
                        fieldAttributes.add(attribute);
                    }
                    // 如果属性值为空字符串，直接跳过
                    // 如果该属性的默认值是 true，则只有当该属性的值为 false 的时候才记录
                    // 对于其他属性，只有当该属性的值不为 false 的时候才记录
                    else if(!"".equals(fieldJsonObj.get(key)) &&
                            ((defaultContains && "false".equals(fieldJsonObj.getString(key)))||
                             (!defaultContains && !"false".equals(fieldJsonObj.getString(key))))){
                        Attribute attribute = new Attribute(key, fieldJsonObj.getString(key));
                        fieldAttributes.add(attribute);
                    }
                }
                try{
                    if(selectorJsonArray != null && selectorJsonArray.size() != 0){
                        List<String> groupNumList = new ArrayList<>();
                        for(Object groupObj : selectorJsonArray){
                            JSONObject groupJsonObj = (JSONObject) groupObj;
                            String groupNum = groupJsonObj.getString("group");
                            if("".equals(groupNum)){
                                Element selectElement = genSelectElement(groupJsonObj);
                                grandchildElement.addContent(selectElement);
                            }
                            else{
                                groupNumList.add(groupNum);
                            }
                        }
                        Set<String> groupNumSet = new HashSet<>(groupNumList);
                        for(String groupNum : groupNumSet){
                            Element groupElement = new Element("group");
                            Attribute attribute = new Attribute("key", groupNum);
                            groupElement.setAttribute(attribute);
                            for(Object groupObj : selectorJsonArray){
                                JSONObject groupJsonObj = (JSONObject) groupObj;
                                if(groupNum.equals(groupJsonObj.getString("group"))){
                                    Element selectElement = genSelectElement(groupJsonObj);
                                    groupElement.addContent(selectElement);
                                }
                            }
                            grandchildElement.addContent(groupElement);
                        }
                    }
                } catch(Exception e){
                    e.printStackTrace();
                }
                grandchildElement.setAttributes(fieldAttributes);
                childElement.addContent(grandchildElement);
            }
            rootElement.addContent(childElement);
        }
    }

    /**
     * 根据传入的groupJsonObj生成 <select>
     */
    private static Element genSelectElement(JSONObject groupJsonObj){
        Element selectElement = new Element(CommonConstant.FIELD_SELECT);
        List<Attribute> selectKeyAttributes = new ArrayList<>();
        selectKeyAttributes.add(new Attribute("key", groupJsonObj.getString("key")));
        selectKeyAttributes.add(new Attribute("value", groupJsonObj.getString("value")));
        selectElement.setAttributes(selectKeyAttributes);
        return selectElement;
    }


}
