package com.ruoyi.wayline.waypoint;

/**
 * @Author zhouzhixiong
 * @Date 2023/12/2 17:04
 */


import com.ruoyi.wayline.domain.Action;
import com.ruoyi.wayline.domain.Placemark;
import com.ruoyi.wayline.domain.WaypointData;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

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.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;

public class WayPointKmlProcessorModify {

    /**
     * 根据提供的数据请求处理并生成航点飞行的 KML 文件
     *
     * @param dataRequest
     * @return
     */
    public byte[] processKml(WaypointData dataRequest, int templateId) {
        try {
            // 创建文件的编辑对象
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();

            // 从资源中读取 KML 模板文件
            InputStream inputStream = getClass().getClassLoader().getResourceAsStream("waypoint/template.kml");
            Document document = builder.parse(inputStream);

            // 使用提供的数据请求更新文档
            updateDocumentWithRequestData(document, dataRequest, templateId);

            // 将更新后的文档转换为字节数组
            return convertDocumentToByteArray(document);
        } catch (ParserConfigurationException | SAXException | IOException | TransformerException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 根据请求中的信息来更新文档内容
     *
     * @param document
     * @param dataRequest
     */
    private void updateDocumentWithRequestData(Document document, WaypointData dataRequest, int templateId) {
        // 更新安全起飞高度
        document.getElementsByTagName("wpml:takeOffSecurityHeight").item(0).setTextContent(String.valueOf(dataRequest.getTakeOffSecurityHeight()));

        // 更新参考起飞点
        document.getElementsByTagName("wpml:takeOffRefPoint").item(0).setTextContent(dataRequest.getTakeOffRefPoint());

        // 更新飞向首航点速度
        document.getElementsByTagName("wpml:globalTransitionalSpeed").item(0).setTextContent(String.valueOf(dataRequest.getGlobalTransitionalSpeed()));

        // 更新无人机的信息和负载的信息
        updateDroneAndPayloadInfo(document, dataRequest);

        // 生成并设置 templateId
        document.getElementsByTagName("wpml:templateId").item(0).setTextContent(String.valueOf(templateId));

        // 更新全局航线速度
        document.getElementsByTagName("wpml:autoFlightSpeed").item(0).setTextContent(String.valueOf(dataRequest.getAutoFlightSpeed()));

        // 更新相对起飞点高度
        document.getElementsByTagName("wpml:globalHeight").item(0).setTextContent(String.valueOf(dataRequest.getGlobalHeight()));

        // 更新飞行器离被摄面高度（相对地面高）
        document.getElementsByTagName("wpml:globalShootHeight").item(0).setTextContent(String.valueOf(dataRequest.getGlobalShootHeight()));

        // 更新图片格式列表
        document.getElementsByTagName("wpml:imageFormat").item(0).setTextContent(dataRequest.getImageFormat());

        // 删除模板中现有的所有 <Placemark> 节点
        removeExistingPlacemarks(document);

        // TODO 根据提供的位置列表添加若干新的 <Placemark> 节点
        Element folderElement = (Element) document.getElementsByTagName("Folder").item(0);
        for (int index = 0; index < dataRequest.getPlacemarkList().size(); index++) {
            Placemark location = dataRequest.getPlacemarkList().get(index);
            folderElement.appendChild(createPlacemarkElement(document, index, location, dataRequest));
        }

        // 更新文档中全部的负载位置索引标签
        updateAllPayloadPositionIndexes(document, dataRequest);
    }

    /**
     * 更新所有 <wpml:payloadPositionIndex> 标签
     *
     * @param document
     * @param dataRequest
     */
    private void updateAllPayloadPositionIndexes(Document document, WaypointData dataRequest) {
        NodeList payloadPositionIndexes = document.getElementsByTagName("wpml:payloadPositionIndex");
        for (int i = 0; i < payloadPositionIndexes.getLength(); i++) {
            payloadPositionIndexes.item(i).setTextContent(String.valueOf(dataRequest.getPayloadInfo().getPayloadPositionIndex()));
        }
    }

    /**
     * 创建一个新的 <Placemark> 元素
     *
     * @param document
     * @param index
     * @param location
     * @return
     */
    private Element createPlacemarkElement(Document document, int index, Placemark location, WaypointData dataRequest) {
        // 创建 <Placemark> 根节点
        Element placemark = document.createElement("Placemark");

        // 设置 <Point> 元素及其坐标
        Element point = document.createElement("Point");
        Element coordinatesElement = document.createElement("coordinates");
        String coordinates = location.getLongitude() + "," + location.getLatitude();
        coordinatesElement.setTextContent(coordinates);
        point.appendChild(coordinatesElement);
        placemark.appendChild(point);

        // 设置 <wpml:index> 元素
        placemark.appendChild(createElementWithTextContent(document, "wpml:index", String.valueOf(index)));

        // 设置其他 <Placemark> 元素的属性
        placemark.appendChild(createElementWithTextContent(document, "wpml:ellipsoidHeight", String.valueOf(location.getAltitude())));
        placemark.appendChild(createElementWithTextContent(document, "wpml:height", String.valueOf(location.getAltitude())));
        placemark.appendChild(createElementWithTextContent(document, "wpml:useGlobalHeight", "0"));
        placemark.appendChild(createElementWithTextContent(document, "wpml:useGlobalSpeed", "1"));
        placemark.appendChild(createElementWithTextContent(document, "wpml:useGlobalHeadingParam", "1"));
        placemark.appendChild(createElementWithTextContent(document, "wpml:useGlobalTurnParam", "1"));
        placemark.appendChild(createElementWithTextContent(document, "wpml:useStraightLine", "1"));
        placemark.appendChild(createElementWithTextContent(document, "wpml:gimbalPitchAngle", "0.0"));
        placemark.appendChild(createElementWithTextContent(document, "wpml:waypointSpeed", String.valueOf(dataRequest.getAutoFlightSpeed())));

        // 根据每个 Placemark 的动作列表创建动作组
        createActionGroup(document, location, placemark, index);

        return placemark;
    }


    /**
     * 更新无人机的信息和负载信息
     *
     * @param document
     * @param dataRequest
     */
    public void updateDroneAndPayloadInfo(Document document, WaypointData dataRequest) {
        // 更新飞行器的信息
        NodeList droneInfoNodes = document.getElementsByTagName("wpml:droneInfo").item(0).getChildNodes();
        for (int i = 0; i < droneInfoNodes.getLength(); i++) {
            Node node = droneInfoNodes.item(i);
            if (node.getNodeName().equals("wpml:droneEnumValue")) {
                node.setTextContent(String.valueOf(dataRequest.getDroneInfo().getDroneEnumValue()));
            } else if (node.getNodeName().equals("wpml:droneSubEnumValue")) {
                node.setTextContent(String.valueOf(dataRequest.getDroneInfo().getDroneSubEnumValue()));
            }
        }

        // 更新负载的信息
        NodeList payloadInfoNodes = document.getElementsByTagName("wpml:payloadInfo").item(0).getChildNodes();
        for (int i = 0; i < payloadInfoNodes.getLength(); i++) {
            Node node = payloadInfoNodes.item(i);
            if (node.getNodeName().equals("wpml:payloadEnumValue")) {
                node.setTextContent(String.valueOf(dataRequest.getPayloadInfo().getPayloadEnumValue()));
            } else if (node.getNodeName().equals("wpml:payloadSubEnumValue")) {
                node.setTextContent(String.valueOf(dataRequest.getPayloadInfo().getPayloadSubEnumValue()));
            } else if (node.getNodeName().equals("wpml:payloadPositionIndex")) {
                node.setTextContent(String.valueOf(dataRequest.getPayloadInfo().getPayloadPositionIndex()));
            }
        }
    }

    /**
     * 从文档中移除所有现有的 <Placemark> 节点
     *
     * @param document
     */
    public void removeExistingPlacemarks(Document document) {
        NodeList placemarks = document.getElementsByTagName("Placemark");
        int length = placemarks.getLength();
        for (int i = length - 1; i >= 0; i--) {
            Node placemark = placemarks.item(i);
            placemark.getParentNode().removeChild(placemark);
        }
    }

    /**
     * 创建带文本内容的新元素
     *
     * @param document
     * @param tagName
     * @param textContent
     * @return
     */
    public Element createElementWithTextContent(Document document, String tagName, String textContent) {
        Element element = document.createElement(tagName);
        element.setTextContent(textContent);
        return element;
    }

    /**
     * 创建动作组
     *
     * @param document
     * @param placemark
     */
    private void createActionGroup(Document document, Placemark placemark, Element placemarkElement, int index) {
        // 创建 <wpml:actionGroup> 节点
        Element actionGroup = document.createElement("wpml:actionGroup");
        actionGroup.appendChild(createElementWithTextContent(document, "wpml:actionGroupId", "0"));
        actionGroup.appendChild(createElementWithTextContent(document, "wpml:actionGroupStartIndex", String.valueOf(index)));
        actionGroup.appendChild(createElementWithTextContent(document, "wpml:actionGroupEndIndex", String.valueOf(index)));
        actionGroup.appendChild(createElementWithTextContent(document, "wpml:actionGroupMode", "sequence"));

        Element actionTrigger = document.createElement("wpml:actionTrigger");
        actionTrigger.appendChild(createElementWithTextContent(document, "wpml:actionTriggerType", "reachPoint"));
        actionGroup.appendChild(actionTrigger);

        // 添加每个动作到动作组中
        List<Action> actions = placemark.getActions();
        if (actions == null || actions.isEmpty()) {
            return;
        }
        for (int i = 0; i < actions.size(); i++) {
            Action action = actions.get(i);
            Element actionElement = createActionElement(document, i, action.getType(), action.getParams());
            actionGroup.appendChild(actionElement);
        }

        // 将动作组添加到 Placemark
        placemarkElement.appendChild(actionGroup);
    }

    private Element createActionElement(Document document, int actionId, String actionActuatorFunc, Map<String, String> actionParams) {
        Element actionElement = document.createElement("wpml:action");

        // 设置动作ID
        actionElement.appendChild(createElementWithTextContent(document, "wpml:actionId", String.valueOf(actionId)));

        // 设置动作类型
        actionElement.appendChild(createElementWithTextContent(document, "wpml:actionActuatorFunc", actionActuatorFunc));

        // 创建并设置动作参数
        Element actionParamsElement = document.createElement("wpml:actionActuatorFuncParam");
        for (Map.Entry<String, String> param : actionParams.entrySet()) {
            actionParamsElement.appendChild(createElementWithTextContent(document, "wpml:" + param.getKey(), param.getValue()));
        }
        actionElement.appendChild(actionParamsElement);

        return actionElement;
    }

    /**
     * 将文档转换为字节数组
     *
     * @param document 要转换的文档
     * @return 转换后的字节数组
     * @throws TransformerException
     */
    public byte[] convertDocumentToByteArray(Document document) throws TransformerException {
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
        DOMSource source = new DOMSource(document);
        StringWriter stringWriter = new StringWriter();
        StreamResult result = new StreamResult(stringWriter);
        transformer.transform(source, result);
        String xmlString = stringWriter.toString();
        // 使用正则表达式移除空白行
        xmlString = xmlString.replaceAll("(?m)^[ \t]*\r?\n", "");
        return xmlString.getBytes(StandardCharsets.UTF_8);
    }
}
