package com.css.simulation.resource.video.service;

import api.common.pojo.po.scene.VehicleTypePO;
import api.common.util.FileUtil;
import api.common.util.LinuxUtil;
import api.common.util.OsUtil;
import api.common.util.StringUtil;
import com.css.simulation.resource.video.configuration.constant.ConstantConfiguration;
import com.css.simulation.resource.video.configuration.minio.MinioConfiguration;
import com.css.simulation.resource.video.configuration.redis.CustomRedisClient;
import com.css.simulation.resource.video.data.entity.VehicleEntity;
import com.css.simulation.resource.video.mapper.ModelConfigMapper;
import com.css.simulation.resource.video.mapper.ModelVehicleMapper;
import com.css.simulation.resource.video.mapper.SimulationAutomaticProjectMapper;
import com.css.simulation.resource.video.util.MinioUtil;
import io.minio.MinioClient;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.File;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Iterator;

@Service
@Slf4j
public class VideoService {
    @Resource
    private ModelVehicleMapper modelVehicleMapper;
    @Resource
    private ModelConfigMapper modelConfigMapper;
    @Resource
    private SimulationAutomaticProjectMapper simulationAutomaticProjectMapper;
    @Resource
    private MinioConfiguration minioConfiguration;
    @Resource
    private MinioClient minioClient;
    @Resource
    private ConstantConfiguration constantConfiguration;
    @Resource
    private CustomRedisClient customRedisClient;

    //* -------------------------------- Comment --------------------------------

    public static final String oldXoscName = "simulation_my0.xosc";
    public static final String newXoscName = "simulation_my05.xosc";
    public static final String oldXoscRelativePath = "xosc/simulation_my0.xosc";
    public static final String csv1Name = "Ego.csv";
    public static final String csv2Name = "evaluation.csv";
    public static final String videoName = "simulation_output.mp4";

    //* -------------------------------- Comment --------------------------------


    /**
     * 生成视频
     */
    public void generateVideo(String generateVideoKey, String nodeName, String projectId, String projectType, String maxSimulationTime, String taskId) {
        String lockName = "video-lock-" + taskId;
        customRedisClient.lock(lockName, 1000L, (Long.parseLong(maxSimulationTime) + 10) * 1000L);
        try {
            String rootDirectoryPathOfMinio = minioConfiguration.getProjectResultDirectory() + projectId + "/" + taskId + "/";
            String xodrPathOfMinio = rootDirectoryPathOfMinio + taskId + ".xodr";
            String osgbPathOfMinio = rootDirectoryPathOfMinio + taskId + ".osgb";
            String csv1PathOfMinio = rootDirectoryPathOfMinio + csv1Name;
            String csv2PathOfMinio = rootDirectoryPathOfMinio + csv2Name;
            String rootDirectoryPathOfLinux = constantConfiguration.getLinuxTempDirectory() + "video/" + projectId + "/" + taskId + "/";
            String xodrPathOfLinux = rootDirectoryPathOfLinux + taskId + ".xodr";
            String osgbPathOfLinux = rootDirectoryPathOfLinux + taskId + ".osgb";
            String csv1PathOfLinux = rootDirectoryPathOfLinux + csv1Name;
            String csv2PathOfLinux = rootDirectoryPathOfLinux + csv2Name;
            log.info("下载 csv、xodr、osgb。");
            MinioUtil.downloadToFile(minioClient, minioConfiguration.getBucketName(), xodrPathOfMinio, xodrPathOfLinux);
            MinioUtil.downloadToFile(minioClient, minioConfiguration.getBucketName(), osgbPathOfMinio, osgbPathOfLinux);
            MinioUtil.downloadToFile(minioClient, minioConfiguration.getBucketName(), csv1PathOfMinio, csv1PathOfLinux);
            MinioUtil.downloadToFile(minioClient, minioConfiguration.getBucketName(), csv2PathOfMinio, csv2PathOfLinux);
            log.info("生成 xosc 文件。");
            String xoscPath = generateXosc(rootDirectoryPathOfLinux, xodrPathOfLinux, osgbPathOfLinux, projectId, projectType);
            log.info("启动虚拟窗口并通过 esmini 截图。");
            String pictureDirectoryPath = rootDirectoryPathOfLinux + "picture";
            FileUtil.createDirectory(pictureDirectoryPath);
            // 获取屏幕号创建新的虚拟屏幕
//            final long newScreenNum = customRedisClient.increment("screen-number", 1);
//            log.info("获取新的屏幕号：" + newScreenNum);
//            final String xvfbCommand = constantConfiguration.getXvfbCommand().replaceAll("screen-num", String.valueOf(newScreenNum));
//            final String esminiCommand = constantConfiguration.getEsminiCommand().replaceAll("screen-num", String.valueOf(newScreenNum)) + " " + xoscPath + " " + pictureDirectoryPath + "/screenshot " + StringUtil.doubleToString(Double.parseDouble(maxSimulationTime), 2);
            final String esminiCommand = constantConfiguration.getEsminiCommand() + " " + xoscPath + " " + pictureDirectoryPath + "/screenshot " + StringUtil.doubleToString(Double.parseDouble(maxSimulationTime), 2);
//            OsUtil.exec(xvfbCommand);
            OsUtil.exec(esminiCommand);
            log.info("删除 esmini 进程。");
            OsUtil.kill(esminiCommand);
            StringBuilder removeCommand = new StringBuilder();
            removeCommand.append("rm -f ");
            int num = 14;
            for (int i = 0; i < num; i++) {
                removeCommand.append(pictureDirectoryPath).append("/screenshot_0000").append(i).append(".tga ");
            }
            OsUtil.kill(removeCommand.toString());
            log.info("生成视频。");
            String videoTargetPathOfLinux = rootDirectoryPathOfLinux + "video/" + videoName;
            FileUtil.createParentDirectory(videoTargetPathOfLinux);
            String videoTargetPathOfMinio = rootDirectoryPathOfMinio + videoName;
            OsUtil.exec("ffmpeg -f image2 -framerate 30 -start_number " + num + " -i " + pictureDirectoryPath + "/screenshot_%05d.tga" + " -c:v libx264 -vf format=yuv420p -crf 20 " + videoTargetPathOfLinux);
            //删除生成的临时文件
            String removeAll = "rm -rf " + pictureDirectoryPath;
            log.info("删除全部图片：" + removeAll);
            OsUtil.exec(removeAll);
            //6 将视频上传到 minio
            MinioUtil.uploadFromFile(minioClient, videoTargetPathOfLinux, minioConfiguration.getBucketName(), videoTargetPathOfMinio);
            log.info("上传成功：" + videoTargetPathOfMinio);
//            OsUtil.kill(xvfbCommand);
            //* -------------------------------- 删除临时文件 --------------------------------
        } catch (Exception e) {
            throw new RuntimeException("视频生成失败。", e);
        } finally {
            customRedisClient.unlock(lockName);
            customRedisClient.set(generateVideoKey, "1");
        }
    }

    /**
     * Xosc
     */
    @SneakyThrows
    private String generateXosc(String csvDirectoryPath, String xodrPath, String osgbPath, String projectId, String projectType) {
        //1 生成 xosc 文件
        String command = constantConfiguration.getXoscGenerateCommand() + " " + csvDirectoryPath;
        String execute = LinuxUtil.execute(command);    // 执行命令后生成 xosc 到目录下
        String oldXoscPath = csvDirectoryPath + oldXoscRelativePath;  // xosc结果文件的路径
        //2 调用修改xosc
        return modifyXosc(oldXoscPath, xodrPath, osgbPath, projectId, projectType);  // 返回最新的 xosc 文件全路径
    }


    /**
     * 修改 xosc 文件
     */
    @SneakyThrows
    public String modifyXosc(String oldXoscPath, String xodrPath, String osgbPath, String projectId, String projectType) {
        String newXoscPath = oldXoscPath.replace(oldXoscName, newXoscName);
        //获取三维自车对象
        VehicleTypePO po = vehicleById(projectId, projectType);
        log.info("修改 xosc 文件----==" + po.getModel3d());

        //1 创建Reader对象
        SAXReader reader = new SAXReader();
        //2 加载xml
        // Document document = reader.read(new File("D:\\simulation_my0.xosc"));
        Document document = reader.read(new File(oldXoscPath));
        //3 获取根节点
        Element root = document.getRootElement();
        Iterator<Element> iterator0 = root.elementIterator();
        while (iterator0.hasNext()) {   // 第一层遍历
            Element node1 = iterator0.next();
            if ("Entities".equals(node1.getName())) { // 找到第一层节点后进入第二层遍历
                Iterator<Element> iterator1 = node1.elementIterator();
                while (iterator1.hasNext()) {
                    Element node2 = iterator1.next();
                    // 添加节点
                    if ("ScenarioObject".equals(node2.getName())) {
                        String scenarioObjectName = node2.attribute("name").getValue();
                        if (scenarioObjectName.equals("Ego")) {
                            Iterator<Element> iterator2 = node2.elementIterator();
                            while (iterator2.hasNext()) {
                                Element node3 = iterator2.next();
                                if ("Vehicle".equals(node3.getName())) {
                                    Attribute name = node3.attribute("name");
                                    name.setText("Audi_A3_2009_red");
                                    Attribute vehicleCategory = node3.attribute("vehicleCategory");
                                    vehicleCategory.setText(po.getVehicleCategory());
                                    //node3.addAttribute("model3d", po.getModel3d());//自车渲染
                                    if (null == po.getModel3d() || po.getModel3d().equals("")) {
                                        node3.addAttribute("model3d", "/root/disk1/simulation-cloud/esmini/resources/models/car_red.osgb");//自车渲染
                                    } else {
                                        log.info(" 车辆类型为 为：" + po.getVehicleCategory() + " 渲染文件为：" + po.getModel3d());
                                        node3.addAttribute("model3d", po.getModel3d());
                                    }
                                    Iterator<Element> iterator4 = node3.elementIterator();
                                    while (iterator4.hasNext()) {
                                        Element node4 = iterator4.next();
                                        if ("Performance".equals(node4.getName())) {
                                            Attribute maxSpeed = node4.attribute("maxSpeed");
                                            maxSpeed.setText(String.valueOf(po.getPerformanceMaxSpeed()));
                                            Attribute maxDeceleration = node4.attribute("maxDeceleration");
                                            maxDeceleration.setText(String.valueOf(po.getPerformanceMaxDeceleration()));
                                            Attribute maxAcceleration = node4.attribute("maxAcceleration");
                                            maxAcceleration.setText(po.getPerformanceMaxAcceleration());
                                        } else if ("BoundingBox".equals(node4.getName())) {
                                            Iterator<Element> iterator5 = node4.elementIterator();
                                            while (iterator5.hasNext()) {
                                                Element node5 = iterator5.next();
                                                if ("Center".equals(node5.getName())) {
                                                    Attribute x = node5.attribute("x");
                                                    x.setText(String.valueOf(po.getCenterX()));
                                                    Attribute y = node5.attribute("y");
                                                    y.setText(String.valueOf(po.getCenterY()));
                                                    Attribute z = node5.attribute("z");
                                                    z.setText(String.valueOf(po.getCenterZ()));
                                                }
                                                if ("Dimensions".equals(node5.getName())) {
                                                    Attribute width = node5.attribute("width");
                                                    width.setText(String.valueOf(po.getDimensionsWidth()));
                                                    Attribute length = node5.attribute("length");
                                                    length.setText(String.valueOf(po.getDimensionsHeight()));
                                                    Attribute height = node5.attribute("height");
                                                    height.setText(String.valueOf(po.getDimensionsHeight()));
                                                }
                                            }
                                        } else if ("Axles".equals(node4.getName())) {
                                            Iterator<Element> iterator5 = node4.elementIterator();
                                            while (iterator5.hasNext()) {
                                                Element node5 = iterator5.next();
                                                if ("FrontAxle".equals(node5.getName())) {
                                                    Attribute maxSteering = node5.attribute("maxSteering");
                                                    maxSteering.setText(String.valueOf(po.getFrontAxleMaxSteering()));
                                                    Attribute wheelDiameter = node5.attribute("wheelDiameter");
                                                    wheelDiameter.setText(String.valueOf(po.getFrontAxleWheelDiameter()));
                                                    Attribute trackWidth = node5.attribute("trackWidth");
                                                    trackWidth.setText(String.valueOf(po.getFrontAxleTrackWidth()));
                                                    Attribute positionX = node5.attribute("positionX");
                                                    positionX.setText(String.valueOf(po.getFrontAxlePositionX()));
                                                    Attribute positionZ = node5.attribute("positionZ");
                                                    positionZ.setText(String.valueOf(po.getFrontAxlePositionZ()));
                                                }
                                                if ("RearAxle".equals(node5.getName())) {
                                                    Attribute maxSteering = node5.attribute("maxSteering");
                                                    maxSteering.setText(po.getRearAxleMaxSteering());
                                                    Attribute wheelDiameter = node5.attribute("wheelDiameter");
                                                    wheelDiameter.setText(String.valueOf(po.getRearAxleWheelDiameter()));
                                                    Attribute trackWidth = node5.attribute("trackWidth");
                                                    trackWidth.setText(String.valueOf(po.getRearAxleTrackWidth()));
                                                    Attribute positionX = node5.attribute("positionX");
                                                    positionX.setText(String.valueOf(po.getRearAxlePositionX()));
                                                    Attribute positionZ = node5.attribute("positionZ");
                                                    positionZ.setText(String.valueOf(po.getRearAxlePositionZ()));
                                                }
                                            }
                                        }

                                    }
                                }

                            }

                            // 保存文件
                            // Writer osWrite = new OutputStreamWriter(Files.newOutputStream(Paths.get("D:\\simulation_my0000.xosc")));// 创建输出流

                        }
                    }
                }
            }

            if ("RoadNetwork".equals(node1.getName())) { // 找到第一层节点后进入第二层遍历
                Iterator<Element> iterator1 = node1.elementIterator();
                while (iterator1.hasNext()) {
                    Element node2 = iterator1.next();
                    // 添加节点
                    if ("LogicFile".equals(node2.getName())) {
                        Attribute name = node2.attribute("filepath");
                        name.setText(xodrPath);
                    }
                    if ("SceneGraphFile".equals(node2.getName())) {
                        Attribute name = node2.attribute("filepath");
//                         name.setText(osgbPath);
                        name.setText("osgbPath");
                    }
                }
            }

            Writer osWrite = new OutputStreamWriter(Files.newOutputStream(Paths.get(newXoscPath)));
            OutputFormat format = OutputFormat.createPrettyPrint(); // 获取输出的指定格式
            format.setEncoding("UTF-8");// 设置编码 ，确保解析的xml为UTF-8格式
            XMLWriter writer = new XMLWriter(osWrite, format);// XMLWriter
            writer.write(document);// 把document写入xmlFile指定的文件(可以为被解析的文件或者新创建的文件)
            writer.flush();
            writer.close();
        }
        return newXoscPath;
    }


    //获取三维自车对象
    @SneakyThrows
    public VehicleTypePO vehicleById(String projectId, String projectType) {
        VehicleTypePO po = new VehicleTypePO();
        //项目类型 1.手动；2.自动
        String vehicle = "";
        if (projectType.equals("1")) {
            vehicle = simulationAutomaticProjectMapper.vehicleBySdId(projectId);
        } else if (projectType.equals("2")) {
            vehicle = simulationAutomaticProjectMapper.vehicleByZdId(projectId);
        }
        log.info("车辆配置 Id 为：" + vehicle);
        String vehicleId = modelConfigMapper.getVehicleId(vehicle);

        log.info("车辆 Id 为：" + vehicleId);
        VehicleEntity vehicleInfo = modelVehicleMapper.getVehicleInfo(vehicleId);
        log.info("车辆信息为：" + vehicleInfo);

        //自车模型
    /*    String car = vehicleInfo.getVehicleType().substring(0, vehicleInfo.getVehicleType().indexOf(","));
        car = car.substring(0, 1).toLowerCase() + car.substring(1);
        if (car.contains("truck")) {
            po.setVehicleCategory("truck");
        } else if (car.contains("trailer")) {
            po.setVehicleCategory("trailer");
        } else if (car.contains("van")) {
            po.setVehicleCategory("van");
        } else if (car.contains("semitrailer")) {
            po.setVehicleCategory("semitrailer");
        } else if (car.contains("bus")) {
            po.setVehicleCategory("bus");
        } else if (car.contains("motorbike")) {
            po.setVehicleCategory("motorbike");
        } else if (car.contains("bicycle")) {
            po.setVehicleCategory("bicycle");
        } else if (car.contains("train")) {
            po.setVehicleCategory("train");
        } else if(car.equalsIgnoreCase("suv")) {
            po.setVehicleCategory("suv");
           // po.setModel3d("/root/disk1/simulation-cloud/esmini/resources/models/audiq5.osgb");
        }else {
            po.setVehicleCategory("car");
           // po.setModel3d("/root/disk1/simulation-cloud/esmini/resources/models/audia8.osgb");
        }*/
        //从字段取自车渲染osgb
      /*  String vehicleType=vehicleInfo.getVehicleType();
        String car = vehicleType.substring( vehicleType.indexOf(",")+1,vehicleType.lastIndexOf(","));
        log.info("VideoService--vehicleById 车辆类型为：" + car);
    */
        String car = vehicleInfo.getModelLabel();
       /*String carPath=dictMapper.getCarPath(car);
        if(null!=carPath&&!carPath.equals("")){
            po.setVehicleCategory(car);
            po.setModel3d(carPath);
        }else{
            po.setVehicleCategory("car");
        }*/
        po.setVehicleCategory(car);
        po.setModel3d(vehicleInfo.getVehicleTopView());
        //车前距
        BigDecimal frontDistance = vehicleInfo.getFrontDistance();
        //车后距
        BigDecimal mo2 = new BigDecimal("2");
        BigDecimal rearDistance = vehicleInfo.getRearDistance();
        BigDecimal CenterX;
        BigDecimal divide = (frontDistance.add(rearDistance)).divide(mo2, 6, RoundingMode.CEILING);
        if (frontDistance.compareTo(rearDistance) > 0) {
            CenterX = divide.subtract(rearDistance);
        } else {
            CenterX = divide.subtract(frontDistance);
        }
        po.setCenterX(CenterX);
        //车高
        BigDecimal mo3 = new BigDecimal("3");
        po.setCenterZ(vehicleInfo.getHeightDistance().divide(mo3, 6, RoundingMode.CEILING));
        //车左距  车右距
        BigDecimal LeftDistance = vehicleInfo.getLeftDistance();
        BigDecimal RightDistance = vehicleInfo.getRightDistance();
        po.setDimensionsWidth(LeftDistance.add(RightDistance));
        po.setDimensionsHeight(vehicleInfo.getHeightDistance());
        po.setDimensionsLength(frontDistance.add(rearDistance));

        po.setPerformanceMaxSpeed(vehicleInfo.getMaxSpeed());
        po.setPerformanceMaxDeceleration(vehicleInfo.getMaxDeceleration());
        BigDecimal moPI = new BigDecimal("3.141516");
        BigDecimal mo180 = new BigDecimal("180");
        po.setFrontAxleMaxSteering(vehicleInfo.getMaxSteeringAngle().multiply(moPI).divide(mo180, 6, RoundingMode.CEILING));
        po.setFrontAxleWheelDiameter(vehicleInfo.getWheelDiameter());
        BigDecimal mo4 = new BigDecimal("4");
        BigDecimal mo5 = new BigDecimal("5");
        po.setFrontAxleTrackWidth(LeftDistance.add(RightDistance).multiply(mo4).divide(mo5, 6, RoundingMode.CEILING));
        po.setFrontAxlePositionX(po.getCenterX());
        po.setFrontAxlePositionZ(po.getCenterZ());

        po.setRearAxleWheelDiameter(vehicleInfo.getWheelDiameter());
        po.setRearAxleTrackWidth(po.getFrontAxleTrackWidth());
        po.setRearAxlePositionX(po.getFrontAxlePositionX());
        po.setRearAxlePositionZ(po.getFrontAxlePositionZ());
        return po;

    }


}
