package com.base.track.thread;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.base.api.model.SatelliteAndInstanceRpcDTO;
import com.base.api.model.YxInfoBasisInstanceRpcDTO;
import com.base.api.model.YxInfoBasisSatelliteRpcDTO;


import com.base.api.client.PayloadRpcService;
import com.base.api.model.PayloadRpcDTO;
import com.base.api.model.request.PayloadRpcReqDTO;
import com.base.orbit.utils.DateUtils;
import com.base.orbit.utils.FileUtil;
import com.base.orbit.utils.StringUtil;
import com.base.orbit.utils.TimeUtil;
import com.base.track.config.sharding.ShardingAlgorithmTool;
import com.base.track.config.sharding.enums.ShardingTableCacheEnum;
import com.base.track.mapper.SubPointDataMapper;
import com.base.track.model.entity.*;
import com.base.track.service.SubPointDataService;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.*;
import java.sql.*;
import java.text.SimpleDateFormat;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Service
@Slf4j
public class SubPointThread {

    private static String driver = "com.mysql.cj.jdbc.Driver";
    private static String url = "jdbc:mysql://192.168.1.5:3306/orbit_base_track?useUnicode=true&characterEncoding=UTF8&useSSL=false&serverTimezone=GMT%2b8&allowPublicKeyRetrieval=true";
    private static String userName = "root";
    private static String password = "123456";

    @Value("${error.lon.value:120.8}")
    private double errorLonValue;

    @Resource
    PayloadRpcService yxInfoSatellitePayloadService;

    @Resource
    SubPointDataService subPointDataService;
    @Resource
    SubPointDataMapper subPointDataMapper;

//    @Value("${spring.datasource.driver-class-name}")
//    public void setDriver(String driver) {
//        this.driver = driver;
//    }
//
//    @Value("${spring.datasource.url}")
//    public void setUrl(String url) {
//        this.url = url;
//    }
//
//    @Value("${spring.datasource.username}")
//    public void setUserName(String userName) {
//        this.userName = userName;
//    }
//
//    @Value("${spring.datasource.password}")
//    public void setPassword(String password) {
//        this.password = password;
//    }

    @Async("subPointTaskExecutor")
    public void run(String filePath, SatelliteAndInstanceRpcDTO satelliteAndInstance, String startTime, String endTime) {
        Date newstart=new Date(DateUtils.StringToDate(startTime).getTime()-8*3600*1000);
        Date newend=new Date(DateUtils.StringToDate(endTime).getTime()-8*3600*1000);
        startTime=DateUtils.DateToString(newstart);
        endTime=DateUtils.DateToString(newend);
        try {
            String code = satelliteAndInstance.getYxInfoBasisSatelliteDTO().getSatelliteCode();
            log.info("卫星" + code + "开始星下点计算");
            boolean isFileWrite = writeDependFile(filePath, startTime, endTime, satelliteAndInstance);
            if (isFileWrite) {
                //文件写入成功，开始执行exe文件
                runExeFile(filePath, satelliteAndInstance.getYxInfoBasisSatelliteDTO(), startTime, endTime);
            } else {
                //文件写入失败
                log.error("卫星" + code + "写入星下点计算条件文件失败");
            }
            log.info("卫星" + code + "结束星下点计算");
        } catch (Exception e) {
            log.error(e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 生成计算依赖文件
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return boolean
     */
    private boolean writeDependFile(String filePath, String startTime, String endTime, SatelliteAndInstanceRpcDTO satelliteAndInstance) throws IOException {
        YxInfoBasisSatelliteRpcDTO infoBasisSatelliteDO = satelliteAndInstance.getYxInfoBasisSatelliteDTO();
        YxInfoBasisInstanceRpcDTO infoBasisInstanceDO = satelliteAndInstance.getYxInfoBasisInstanceDTO();
        StringBuilder sb = new StringBuilder();
        //卫星详细信息 卫星质量 迎风面 光面 地球重力场系数1 地球重力场系数2
        sb.append("1 ").append(infoBasisSatelliteDO.getSatelliteName()).append(" ").append(infoBasisSatelliteDO.getMass()).append(" ").append(infoBasisSatelliteDO.getWindArea()).append(" ").append(infoBasisSatelliteDO.getShineArea()).append(" ").append(infoBasisSatelliteDO.getEarthGravityFieldModeOrde1() != null ? infoBasisSatelliteDO.getEarthGravityFieldModeOrde1() : "32.0").append(" ").append(infoBasisSatelliteDO.getEarthGravityFieldModeOrde2() != null ? infoBasisSatelliteDO.getEarthGravityFieldModeOrde2() : "32.0").append("\n");
        //预报开始时间
        sb.append("2 ").append(startTime).append(" ").append(endTime).append(" 1.0\n");
        //地面站信息 地面站经度 地面站纬度 地面站高度 天线最小跟踪仰角（度）默认1度 天线最小接收仰角（度）默认1度 日凌角（度）默认1度
        sb.append("3 0 0 0 0 0 0\n");
        //星历类型
        sb.append("4 3\n");
        //星历文件目录
        sb.append("5 ").append(filePath + Thread.currentThread().getName().split("-")[2]).append("\n");
        //星历预报时间
        sb.append("6 ").append(startTime).append(" ").append(endTime).append(" 1.0 60.0\n");
        //星历轨道类型 输入轨道类型，输出轨道类型
        sb.append("7 1 1\n");
        //六根数对应的时间
        sb.append("8 ").append(infoBasisInstanceDO.getOrbitNum()).append(" ").append(TimeUtil.format(new Date(infoBasisInstanceDO.getEpochTime().getTime()-8*3600*1000))).append(" ").append(TimeUtil.format(new Date(infoBasisInstanceDO.getEpochTime().getTime()-8*3600*1000))).append("\n");
        //瞬根数据 半长轴（m） 偏心率 轨道倾角（度） 升交点赤经（度） 近地点俯角（度） 平近点角（度）
        sb.append("9 ").append(infoBasisInstanceDO.getSemiMajorAxis()).append(" ").append(infoBasisInstanceDO.getEccentricity()).append(" ").append(infoBasisInstanceDO.getInclination()).append(" ").append(infoBasisInstanceDO.getRaan()).append(" ").append(infoBasisInstanceDO.getArgumentOfPerigee()).append(" ").append(infoBasisInstanceDO.getMeanAnoMaly()).append("\n");
        //GPS数据
        sb.append("10 0 0 0 0 0 0\n");
        //两行数据
        sb.append("11 1 0 0 0 0 0 0 0 0\n");
        //两行数据
        sb.append("12 2 0 0 0 0 0 0 0 0");
        File file = new File(filePath + Thread.currentThread().getName().split("-")[2] + "/orbdata/subsp.inp");
        boolean isDelete = true;
        if (file.exists()) {
            isDelete = file.delete();
        }
        if (isDelete) {
            BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, true)));
            bufferedWriter.write(sb.toString());
            bufferedWriter.flush();
            bufferedWriter.close();
        } else {
            return false;
        }
        return true;
    }

    /**
     * 写完依赖文件之后运行执行exe程序
     */
    private void runExeFile(String filePath, YxInfoBasisSatelliteRpcDTO yxInfoBasisSatellite, String startTime, String endTime) throws Exception {
        Date newstart=new Date(DateUtils.StringToDate(startTime).getTime()+8*3600*1000);
        Date newend=new Date(DateUtils.StringToDate(endTime).getTime()+8*3600*1000);
        startTime=DateUtils.DateToString(newstart);
        endTime=DateUtils.DateToString(newend);
        System.out.println(new Date());
        // 星下点的处理方法 根据ID获取卫星的数据和卫星的载荷
        SatellitePara satellitePara = findThisEphData(yxInfoBasisSatellite);
        if (satellitePara != null) {
            String port = Thread.currentThread().getName().split("-")[2];
            File oldFile = new File(filePath + port + "/orbdata/subsp.dat");
            boolean isOldFileDelete = true;
            if (oldFile.exists()) {
                isOldFileDelete = oldFile.delete();
            }
            File okFile = new File(filePath + port + "/orbdata/subsp.ok");
            boolean isOkFileDelete = true;
            if (okFile.exists()) {
                isOkFileDelete = okFile.delete();
            }
            if (isOldFileDelete && isOkFileDelete) {
                Process exec = Runtime.getRuntime().exec(filePath + port + "/bin/subsp.exe", null, new File(filePath + port + "/bin/"));
                if (FileUtil.checkFileStatus(okFile, "卫星" + yxInfoBasisSatellite.getSatelliteCode() + "星下点计算", log)) {
                    DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

                    // 关闭可执行程序
                    exec.destroy();

                    FileInputStream fileInputStream = new FileInputStream(oldFile);
                    InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream);
                    BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

                    // 每行数据
                    String result;
                    // 执行的行数
                    int line = 0;
                    // 初始数据
                    List<SubPointData> subPointDataList = new ArrayList<>();
                    // 遍历结果文件数据
                    String code = StringUtil.change(yxInfoBasisSatellite.getSatelliteCode());
                    while ((result = bufferedReader.readLine()) != null) {
                        if (line != 0 && line != 1) {
                            // 数据转换
                            List<String> r = FileUtil.getLineArray(result);
                            String dateStr = r.get(0) + " " + r.get(1);

                            Date date = sdf.parse(dateStr);
                            SubPointData subpointData = new SubPointData();
                            subpointData.setId(yxInfoBasisSatellite.getSatelliteCode() + date.getTime());
                            subpointData.setSatelliteId(yxInfoBasisSatellite.getId());
                            subpointData.setCode(code);
                            subpointData.setSatelliteCode(yxInfoBasisSatellite.getSatelliteCode());
                            subpointData.setDate( new Date(date.getTime()+8*3600*1000));
                            subpointData.setLon(Double.parseDouble(r.get(2)));
                            subpointData.setLat(Double.parseDouble(r.get(3)));
                            subpointData.setAlt(Double.parseDouble(r.get(4)));
                            subpointData.setOrbit(r.get(5));
                            subpointData.setUpdown(r.get(6));
                            subPointDataList.add(subpointData);
                        }
                        line++;
                    }
                   if (ShardingAlgorithmTool.getShardingTable(ShardingTableCacheEnum.SUBPOINT,"subpoint_"+code)){
                        // 删除表中该时间内的信息
                    QueryWrapper<SubPointData> wrapper=new QueryWrapper<>();
                    wrapper.ge("date",startTime);
                    wrapper.le("date",endTime);
                    wrapper.eq("code",code);
                    subPointDataService.remove(wrapper);
                    }
                    // 遍历星下点信息
                    for (int i = 0; i < subPointDataList.size() - 1; i++) {
                        SubPointData subPointThis = subPointDataList.get(i);
                        SubPointData subPointNext = subPointDataList.get(i + 1);
                        Double latThis = subPointThis.getLat();
                        Double latNext = subPointNext.getLat();

                        // 获取前一颗星的Position
                        LatLon latLon = LatLon.fromDegrees(subPointThis.getLat(), subPointThis.getLon());
                        Position positionThis = new Position(latLon, subPointThis.getAlt());
                        //获取下一颗星的position
                        LatLon latLon1 = LatLon.fromDegrees(subPointNext.getLat(), subPointNext.getLon());
                        Position positionNext = new Position(latLon1, subPointNext.getAlt());

                        // 星下点,下个星下点,幅宽,length重叠区域,theta侧摆角
                        FSideAngleSurfaceGrid fSideAngleSurfaceGridR = new FSideAngleSurfaceGrid(positionThis, positionNext, satellitePara.getSWATH_WIDTH() / 2, satellitePara.getOVER_LAP(), satellitePara.getMaxrollangle());
                        FSideAngleSurfaceGrid fSideAngleSurfaceGridStrip = new FSideAngleSurfaceGrid(positionThis, positionNext, satellitePara.getSWATH_WIDTH() / 2, satellitePara.getOVER_LAP(), 0);

                        LatLon data0, data1, dataSt0, dataSt1;
                        if (latThis < latNext) {
                            // 这儿是升轨的时候
                            data1 = fSideAngleSurfaceGridR.getPointSec();
                            data0 = fSideAngleSurfaceGridR.getPointThr();
                            dataSt1 = fSideAngleSurfaceGridStrip.getPointSec();
                            dataSt0 = fSideAngleSurfaceGridStrip.getPointThr();
                        } else {
                            // 不是升轨的时候
                            data0 = fSideAngleSurfaceGridR.getPointSec();
                            data1 = fSideAngleSurfaceGridR.getPointThr();
                            dataSt0 = fSideAngleSurfaceGridStrip.getPointSec();
                            dataSt1 = fSideAngleSurfaceGridStrip.getPointThr();
                        }

                        // 侧摆左点经纬度
                        Double lr_lon = data0.longitude.getDegrees();
                        Double lr_lat = data0.latitude.getDegrees();
                        // 侧摆右点经度
                        Double rr_lon = data1.longitude.getDegrees();
                        Double rr_lat = data1.latitude.getDegrees();
                        // 幅宽左点经度
                        Double ls_lon = dataSt0.longitude.getDegrees();
                        Double ls_lat = dataSt0.latitude.getDegrees();
                        // 幅宽左点经度
                        Double rs_lon = dataSt1.longitude.getDegrees();
                        Double rs_lat = dataSt1.latitude.getDegrees();
                        subPointThis.setLrLon(lr_lon);
                        subPointThis.setLrLat(lr_lat);
                        subPointThis.setRrLon(rr_lon);
                        subPointThis.setRrLat(rr_lat);
                        subPointThis.setLsLon(ls_lon);
                        subPointThis.setLsLat(ls_lat);
                        subPointThis.setRsLon(rs_lon);
                        subPointThis.setRsLat(rs_lat);
                    }
                    subPointDataService.saveBatch(subPointDataList);
                    log.info("计算完成");
                }
            }
        }
    }

    private double formatLon(double lon) {
        return lon + errorLonValue > 180 ?  lon + errorLonValue -360 : lon + errorLonValue;
    }


    public SatellitePara findThisEphData(YxInfoBasisSatelliteRpcDTO satelliteDO) {
        SatellitePara satellitePara = new SatellitePara();
        //寻找到了卫星ID
        //开始判断侧摆角
        String maxRollAngle = satelliteDO.getMaxRollAngle();
        String satelliteDOId = satelliteDO.getId();
        if (!maxRollAngle.equalsIgnoreCase("")) {
            satellitePara.setMaxrollangle(Float.parseFloat(maxRollAngle));
            PayloadRpcReqDTO rpcReqDTO=new PayloadRpcReqDTO();
            rpcReqDTO.setSatelliteIds(Lists.newArrayList(satelliteDOId));
            List<PayloadRpcDTO> payloadDOList = yxInfoSatellitePayloadService.getPayloadRpcDTOList(rpcReqDTO);
            if (payloadDOList != null) {
                float max_USUAL_BANDWIDTH = -1;
                float max_OVER_LAP = -1;
                for (PayloadRpcDTO payloadDO : payloadDOList) {
                    float tempSWATH_WIDTH = payloadDO.getSwathWidth().floatValue();
                    float temp_OVER_LAP = payloadDO.getOverLap().floatValue();
                    if (tempSWATH_WIDTH > max_USUAL_BANDWIDTH) {
                        max_USUAL_BANDWIDTH = tempSWATH_WIDTH;
                        max_OVER_LAP = temp_OVER_LAP;
                    }
                }
                satellitePara.setSWATH_WIDTH(max_USUAL_BANDWIDTH);
                satellitePara.setOVER_LAP(max_OVER_LAP);
            } else {
                return null;
            }
        } else {
            return null;
        }
        return satellitePara;
    }
}
