package com.base.plan.trans;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.base.api.client.AntennaRpcService;
import com.base.api.client.PayloadRpcService;
import com.base.api.client.StationRpcService;
import com.base.api.client.TrbutDataRpcService;
import com.base.api.model.AntennaRpcDTO;
import com.base.api.model.PayloadRpcDTO;
import com.base.api.model.StationRpcDTO;
import com.base.api.model.TrbutDataRpcDTO;
import com.base.orbit.utils.IdGenerator;
import com.base.plan.mapper.WindowResourceMapper;
import com.base.plan.model.dto.StripDTO;
import com.base.plan.model.dto.WindowResourceDTO;
import com.base.plan.model.entity.WindowResource;
import com.base.plan.service.WindowResourceService;
import com.plan.api.client.StripRpcService;
import com.plan.api.model.StripRpcDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.*;
import java.util.stream.Collectors;

import org.springframework.util.CollectionUtils;


/**
 * 系统数传策略
 */
@Service
@Slf4j
@DataTransCore(dataTransTypeEnums = DataTransTypeEnums.SYSTEM_DATA_TRANS)
public class SystemDataTransMissHandler implements DataTransmissHandler {

    @Resource
    PayloadRpcService payloadRpcService;

    @Resource
    WindowResourceService windowResourceService;

    @Resource
    AntennaRpcService antennaRpcService;

    @Resource
    WindowResourceMapper windowResourceMapper;


    /**
     * 选择数传站
     *
     * @param stripDTOList 条带信息
     * @param resourceList 数传资源信息
     */
    @Override
    public List<WindowResourceDTO> trbutMake(List<StripDTO> stripDTOList, List<TrbutDataRpcDTO> resourceList) {

        //  做数传优选前，需判断所选条带占有总内存是否大于卫星总内存初始值
        List<WindowResourceDTO> windowResourceDTOS = new ArrayList<>();
        if (!CollectionUtils.isEmpty(stripDTOList)) {
            List<PayloadRpcDTO> payloadList = payloadRpcService.getAllList();
            List<AntennaRpcDTO> antennaRpcDTOS = antennaRpcService.selAllAntenaInfos();
            resourceList.stream().forEach(resource -> {
                resource.setDuration(Math.round(resource.getDuration() / 1000));
            });
            //  入库前判断此前存在相同的任务结果则删除
            Set<String> reqIdSet = stripDTOList.stream().map(StripDTO::getReqId).collect(Collectors.toSet());
            if (!CollectionUtils.isEmpty(reqIdSet)) {
                QueryWrapper queryWrapper = new QueryWrapper();
                queryWrapper.in("req_id", reqIdSet);
                windowResourceMapper.delete(queryWrapper);
            }

            stripDTOList.stream().forEach(dto -> {
                String satelliteCode = dto.getSatelliteCode();
                // 获取该星对应的资源信息
                List<TrbutDataRpcDTO> resourceListT = resourceList.stream().filter(t -> satelliteCode.equals(t.getSatelliteCode())).collect(Collectors.toList());
                if (!CollectionUtils.isEmpty(resourceListT)) {
                    // 选择资源：一个条带可能存在多个数传资源
                    List<WindowResourceDTO> windowResourceList = distributionResource(dto, resourceListT, payloadList, antennaRpcDTOS);
                    windowResourceDTOS.addAll(windowResourceList);
                } else {
                    //如果该条带所属卫星未查询到进出站则将该条带设为不满足时效性
                    WindowResourceDTO windowResourceDTO = DisContentRequire(dto);
                    windowResourceDTOS.add(windowResourceDTO);
                }
            });
        }
        List<WindowResourceDTO> windowResourceDTOList = windowResourceDTOS.stream().sorted(Comparator.comparing(WindowResourceDTO::getTimeRequire)).collect(Collectors.toList());
        return windowResourceDTOList;
    }

    private List<WindowResourceDTO> distributionResource(StripDTO stripDTO, List<TrbutDataRpcDTO> resourceList, List<PayloadRpcDTO> payloadList, List<AntennaRpcDTO> antennaRpcDTOS) {
        // 窗口与资源关联信息(分为两种情况:1.一个条带一个数传资源完成数传 2.一个条带需接力多个数传资源完成数传)
        List<WindowResourceDTO> windowResourceDTOS = new ArrayList<>();
        List<WindowResource> windowResourceList = new ArrayList<>();
        Date startTime = stripDTO.getObservationStartTime();
        Date endTime = stripDTO.getObservationEndTime();
        String satelliteCode = stripDTO.getSatelliteCode();
//        String reqId = stripDTO.getReqId();

        // 获取任务时间

        // 资源根据时间排序
        resourceList.stream().sorted(Comparator.comparing(TrbutDataRpcDTO::getReceiveStartTime)).collect(Collectors.toList());
        // 获取可用资源
        List<TrbutDataRpcDTO> resourceListT = resourceList.stream().filter(t -> !"0".equals(t.getOccupancyType())).collect(Collectors.toList());

        // 该卫星对应的载荷信息
        Optional<PayloadRpcDTO> payloadRpc = payloadList.stream().filter(t -> satelliteCode.equals(t.getSatelliteCode())).findFirst();
        if (payloadRpc.isPresent()) {
            PayloadRpcDTO payloadRpcDTO = payloadRpc.get();
            Objects.requireNonNull(payloadRpcDTO, "请检查卫星【" + satelliteCode + "】是否设置载荷参数！");
            //  载荷速率
            Double dataPassSpeed = Double.valueOf(payloadRpcDTO.getDataPassSpeed());
            Objects.requireNonNull(dataPassSpeed, "卫星【" + satelliteCode + "】载荷速率不能为空！");
            long duration = (endTime.getTime() - startTime.getTime()) / 1000;
            //  该条带星上内存占用
            Double memoryUsage = duration * dataPassSpeed;

            //找到条带拍摄结束后的所有可见数传资源
            List<TrbutDataRpcDTO> visibleStations = resourceListT.stream().filter(trbutDataRpcDTO ->
                    (trbutDataRpcDTO.getReceiveStartTime() != null && trbutDataRpcDTO.getReceiveStartTime().after(stripDTO.getObservationEndTime()))
            ).collect(Collectors.toList()).stream().sorted(Comparator.comparing(TrbutDataRpcDTO::getReceiveStartTime)).collect(Collectors.toList());



            Boolean tag = false;
            if (!CollectionUtils.isEmpty(visibleStations)) {
                for (int i = 0; i < visibleStations.size(); i++) {
                    //找到对应站的天线口径
                    TrbutDataRpcDTO trbutDataRpcDTO = visibleStations.get(i);
                    if (trbutDataRpcDTO.getDuration() == 0) {
                        //这里说明
                        throw new RuntimeException("数传资源【" + trbutDataRpcDTO.getStationName() + "】可见时长非法数值");
                    }
                    List<AntennaRpcDTO> antennas = antennaRpcDTOS.stream().filter(antennaRpcDTO -> antennaRpcDTO.getDmzdh().equals(trbutDataRpcDTO.getStationCode())).collect(Collectors.toList());
                    //  没天线口径和类型为1的则为中继星
                    if (CollectionUtils.isEmpty(antennas) && trbutDataRpcDTO.getStationType().equals(1)) {
                        //1.这里进行中继星数传方案的优选，首先计算星上数据数传需要多长的时间
                        Double dataTransferSpeed = 500.0;
                        WindowResourceDTO windowResourceDTO = new WindowResourceDTO();
                        Double transferTime = memoryUsage / dataTransferSpeed;
                        if(trbutDataRpcDTO.getDuration() > transferTime){
                            //2.说明在这个中继星的过境时间内能够将数据传输到中继星上
                            windowResourceDTO.setReceiveStartTime(trbutDataRpcDTO.getReceiveStartTime());
                            windowResourceDTO.setReceiveEndTime(trbutDataRpcDTO.getReceiveEndTime());
                            windowResourceDTO.setStripId(stripDTO.getId());
                            windowResourceDTO.setTargetName(stripDTO.getTargetName());
                            windowResourceDTO.setCircle(stripDTO.getCircle());
                            windowResourceDTO.setSatelliteCode(stripDTO.getSatelliteCode());
                            windowResourceDTO.setStationName(trbutDataRpcDTO.getStationName());
                            windowResourceDTO.setDuration(changeDouble(transferTime));
                            windowResourceDTO.setJssl(dataTransferSpeed);
                            windowResourceDTO.setTimeRequire(0);
                            windowResourceDTOS.add(windowResourceDTO);
                            windowResourceList.add(buildWindowResource(stripDTO.getReqId(), stripDTO.getId(), trbutDataRpcDTO.getId(), transferTime.intValue()));
                            tag = true;
                        }
                        if(tag){
                            //3.1 这里说明数据已经传输完成
                            break;
                        }else{
                            //3.2 这里说明在此次过境中已经没有办法将数据完全传输至中继星，只能进行接续传递
                            Double transmittedData = trbutDataRpcDTO.getDuration() * dataTransferSpeed;
                            windowResourceDTO.setReceiveStartTime(trbutDataRpcDTO.getReceiveStartTime());
                            windowResourceDTO.setReceiveEndTime(trbutDataRpcDTO.getReceiveEndTime());
                            windowResourceDTO.setStripId(stripDTO.getId());
                            windowResourceDTO.setTargetName(stripDTO.getTargetName());
                            windowResourceDTO.setCircle(stripDTO.getCircle());
                            windowResourceDTO.setSatelliteCode(stripDTO.getSatelliteCode());
                            windowResourceDTO.setStationName(trbutDataRpcDTO.getStationName());
                            windowResourceDTO.setDuration(Double.valueOf(trbutDataRpcDTO.getDuration()));
                            windowResourceDTO.setJssl(dataTransferSpeed);
                            windowResourceDTO.setTimeRequire(0);
                            windowResourceDTOS.add(windowResourceDTO);
                            windowResourceList.add(buildWindowResource(stripDTO.getReqId(), stripDTO.getId(), trbutDataRpcDTO.getId(), trbutDataRpcDTO.getDuration()));
                            memoryUsage = memoryUsage - transmittedData;
                        }
                    }else{
                        //将得到的当前站的天线按口径从小到大排序
                        List<AntennaRpcDTO> antennaSorts = antennas.stream().sorted(Comparator.comparing(AntennaRpcDTO::getTxkj, Comparator.comparingDouble(Double::parseDouble))).collect(Collectors.toList());
                        WindowResourceDTO windowResourceDTO = new WindowResourceDTO();
                        //判断是否能在可见时间内将星上数据传输下来
                        for (int j = 0; j < antennaSorts.size(); j++) {
                            //获取当前天线口径的接收速率
                            AntennaRpcDTO antenna = antennaSorts.get(j);
                            Double jssl = Double.parseDouble(antenna.getJssl());
                            //通过接收速率确定下放需要的时间
                            Double transferTime = memoryUsage / jssl;
                            if (trbutDataRpcDTO.getDuration() > transferTime) {
                                //说明能在这个口径下将数据传输下来
                                windowResourceDTO.setReceiveStartTime(trbutDataRpcDTO.getReceiveStartTime());
                                windowResourceDTO.setReceiveEndTime(trbutDataRpcDTO.getReceiveEndTime());
                                windowResourceDTO.setStripId(stripDTO.getId());
                                windowResourceDTO.setTargetName(stripDTO.getTargetName());
                                windowResourceDTO.setCircle(stripDTO.getCircle());
                                windowResourceDTO.setSatelliteCode(stripDTO.getSatelliteCode());
                                windowResourceDTO.setStationName(trbutDataRpcDTO.getStationName());
                                windowResourceDTO.setDuration(changeDouble(transferTime));
                                windowResourceDTO.setJssl(jssl);
                                windowResourceDTO.setTxkj(antenna.getTxkj());
                                windowResourceDTO.setTimeRequire(0);
                                windowResourceDTOS.add(windowResourceDTO);
                                windowResourceList.add(buildWindowResource(stripDTO.getReqId(), stripDTO.getId(), trbutDataRpcDTO.getId(), transferTime.intValue()));
                                tag = true;
                                break;
                            }
                        }
                        if (tag) {
                            //这里说明数据已经传输完成
                            break;
                        } else {
                            //执行到这里表示该站无法在可见时间范围内，完整接收星上数据，先计算上一个站最大天线口径传输后剩余数据量
                            double maxJssl = Double.parseDouble(antennaSorts.get(antennas.size() - 1).getJssl());
                            Double transmittedData = trbutDataRpcDTO.getDuration() * maxJssl;
                            windowResourceDTO.setReceiveStartTime(trbutDataRpcDTO.getReceiveStartTime());
                            windowResourceDTO.setReceiveEndTime(trbutDataRpcDTO.getReceiveEndTime());
                            windowResourceDTO.setStripId(stripDTO.getId());
                            windowResourceDTO.setTargetName(stripDTO.getTargetName());
                            windowResourceDTO.setCircle(stripDTO.getCircle());
                            windowResourceDTO.setSatelliteCode(stripDTO.getSatelliteCode());
                            windowResourceDTO.setStationName(trbutDataRpcDTO.getStationName());
                            windowResourceDTO.setDuration(Double.valueOf(trbutDataRpcDTO.getDuration()));
                            windowResourceDTO.setJssl(Double.parseDouble(antennaSorts.get(antennas.size() - 1).getJssl()));
                            windowResourceDTO.setTxkj(antennaSorts.get(antennas.size() - 1).getTxkj());
                            windowResourceDTO.setTimeRequire(0);
                            windowResourceDTOS.add(windowResourceDTO);
                            windowResourceList.add(buildWindowResource(stripDTO.getReqId(), stripDTO.getId(), trbutDataRpcDTO.getId(), trbutDataRpcDTO.getDuration()));
                            memoryUsage = memoryUsage - transmittedData;
                        }
                    }
                }
            } else {
                //  未找到合适的数传资源则标记为不满足
                WindowResourceDTO windowResourceDTO = DisContentRequire(stripDTO);
                windowResourceDTOS.add(windowResourceDTO);
            }
        }
        // 保存窗口对应的资源信息
        windowResourceService.saveWindowResource(windowResourceList);
        return windowResourceDTOS;
    }

    public static double changeDouble(Double dou){
        NumberFormat nf = new DecimalFormat("0.0");
        dou = Double.parseDouble(nf.format(dou));
        return dou;
    }

    public static void main(String[] args) {
        System.out.println(changeDouble(12.3232));
    }

    public WindowResourceDTO DisContentRequire(StripDTO stripDTO) {
        WindowResourceDTO windowResourceDTO = new WindowResourceDTO();
        windowResourceDTO.setStripId(stripDTO.getId());
        windowResourceDTO.setTargetName(stripDTO.getTargetName());
        windowResourceDTO.setCircle(stripDTO.getCircle());
        windowResourceDTO.setSatelliteCode(stripDTO.getSatelliteCode());
        windowResourceDTO.setTimeRequire(1);
        return windowResourceDTO;
    }


    private WindowResource buildWindowResource(String reqId, String windowId, String resourceId, Integer dataTransferDuration) {
        WindowResource windowResource = new WindowResource();
        windowResource.setId(IdGenerator.getStrId());
        windowResource.setReqId(reqId);
        windowResource.setWindowId(windowId);
        windowResource.setResourceId(resourceId);
        windowResource.setCreateTime(new Date());
        windowResource.setUpdateTime(new Date());
        windowResource.setDataTransferDuration(dataTransferDuration);
        return windowResource;
    }

}
