package com.ruoyi.wayline.waypoint;

import com.ruoyi.wayline.domain.Placemark;
import com.ruoyi.wayline.domain.WaypointData;
import com.ruoyi.wayline.domain.Action;
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 WayPointWpmlProcessorModify {

    public byte[] processWpml(WaypointData dataRequest, int templateId) {
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            InputStream inputStream = getClass().getClassLoader().getResourceAsStream("waypoint/waylines.wpml");
            Document document = builder.parse(inputStream);

            updateDocumentWithRequest(document, dataRequest, templateId);

            return convertDocumentToByteArray(document);
        } catch (ParserConfigurationException | SAXException | IOException | TransformerException e) {
            throw new RuntimeException(e);
        }
    }

    private void updateDocumentWithRequest(Document document, WaypointData dataRequest, int templateId) {
        document.getElementsByTagName("wpml:takeOffSecurityHeight").item(0).setTextContent(String.valueOf(dataRequest.getTakeOffSecurityHeight()));
        document.getElementsByTagName("wpml:globalTransitionalSpeed").item(0).setTextContent(String.valueOf(dataRequest.getGlobalTransitionalSpeed()));
        updateDroneAndPayloadInfo(document, dataRequest);
        document.getElementsByTagName("wpml:templateId").item(0).setTextContent(String.valueOf(templateId));
        document.getElementsByTagName("wpml:autoFlightSpeed").item(0).setTextContent(String.valueOf(dataRequest.getAutoFlightSpeed()));

        removeExistingPlacemarks(document);

        Element folderElement = (Element) document.getElementsByTagName("Folder").item(0);
        List<Placemark> placemarkList = dataRequest.getPlacemarkList();
        for (int index = 0; index < placemarkList.size(); index++) {
            Placemark location = placemarkList.get(index);
            String coordinates = location.getLongitude() + "," + location.getLatitude();
            String height = String.valueOf(dataRequest.getGlobalHeight());
            folderElement.appendChild(createPlacemarkElement(document, coordinates, location, dataRequest, index, height));
        }

        updateAllPayloadPositionIndexes(document, 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()));
        }
    }

    private Element createPlacemarkElement(Document document, String coordinates, Placemark placemarkData, WaypointData dataRequest, int index, String height) {
        Element placemark = document.createElement("Placemark");

        Element point = document.createElement("Point");
        Element coordinatesElement = document.createElement("coordinates");
        coordinatesElement.setTextContent(coordinates);
        point.appendChild(coordinatesElement);
        placemark.appendChild(point);

        placemark.appendChild(createElementWithTextContent(document, "wpml:index", String.valueOf(index)));
        placemark.appendChild(createElementWithTextContent(document, "wpml:executeHeight", placemarkData.getAltitude()));
        placemark.appendChild(createElementWithTextContent(document, "wpml:waypointSpeed", String.valueOf(dataRequest.getAutoFlightSpeed())));

        addWaypointHeadingParam(placemark, document);
        addWaypointTurnParam(placemark, document);
        addWaypointGimbalHeadingParam(placemark, document);

        placemark.appendChild(createElementWithTextContent(document, "wpml:useStraightLine", "1"));

        createActionGroup(document, placemark, placemarkData, index);

        return placemark;
    }

    private void createActionGroup(Document document, Element placemarkElement, Placemark placemarkData, int index) {
        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 = placemarkData.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);
        }

        placemarkElement.appendChild(actionGroup);
    }

    private Element createActionElement(Document document, int actionId, String actionActuatorFunc, Map<String, String> actionParams) {
        Element actionElement = document.createElement("wpml:action");
        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;
    }

    private void addWaypointHeadingParam(Element placemark, Document document) {
        Element waypointHeadingParam = document.createElement("wpml:waypointHeadingParam");
        waypointHeadingParam.appendChild(createElementWithTextContent(document, "wpml:waypointHeadingMode", "followWayline"));
        waypointHeadingParam.appendChild(createElementWithTextContent(document, "wpml:waypointHeadingAngle", "0"));
        waypointHeadingParam.appendChild(createElementWithTextContent(document, "wpml:waypointHeadingPathMode", "followBadArc"));
        placemark.appendChild(waypointHeadingParam);
    }

    private void addWaypointTurnParam(Element placemark, Document document) {
        Element waypointTurnParam = document.createElement("wpml:waypointTurnParam");
        waypointTurnParam.appendChild(createElementWithTextContent(document, "wpml:waypointTurnMode", "toPointAndStopWithDiscontinuityCurvature"));
        waypointTurnParam.appendChild(createElementWithTextContent(document, "wpml:waypointTurnDampingDist", "0.0"));
        placemark.appendChild(waypointTurnParam);
    }

    private void addWaypointGimbalHeadingParam(Element placemark, Document document) {
        Element waypointGimbalHeadingParam = document.createElement("wpml:waypointGimbalHeadingParam");
        waypointGimbalHeadingParam.appendChild(createElementWithTextContent(document, "wpml:waypointGimbalPitchAngle", "0.0"));
        placemark.appendChild(waypointGimbalHeadingParam);
    }

    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()));
            }
        }
    }

    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);
        }
    }

    public Element createElementWithTextContent(Document document, String tagName, String textContent) {
        Element element = document.createElement(tagName);
        element.setTextContent(textContent);
        return element;
    }

    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);
    }
}
