package com.base.core.service;



import com.base.core.config.ElectronImageLimitation;
import com.base.core.model.enity.AccessData;
import com.base.core.model.enity.EphData;
import com.base.core.model.enity.PositionLLA;
import com.base.core.model.enity.SphericalPoint;
import com.base.core.source.GlobeMath;
import com.base.core.utiles.AstroConst;
import com.base.core.utiles.GeoUtils;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;


public class ElectronAccessService extends AccessWindowsService {

    /**
     * 计算电子侦察卫星卫星访问信息，参数：目标数据结构Target、成像约束条件elimit
     */
    // public List<AccessData> ElectronAccess_point(List<EphData> subPointList,
    // double lat_targ, double lon_targ) {
    // SphericalPoint target = new SphericalPoint();
    // target.setLat(lat_targ);
    // target.setLon(lon_targ);
    //
    //
    // return ElectronAccess_point(subPointList, target);
    // }

    // public List<AccessData> ElectronAccess_point(List<EphData> subPointList,
    // SphericalPoint target) {
    // List<AccessData> electronAccessDatas = new ArrayList<>();
    //
    // if (subPointList.size() < 2) { // 如果星历列表过少
    // return null;
    // }
    //
    // AccessData accessMessage = new AccessData();
    //
    // int index = 0;
    //
    // while (index < subPointList.size() - 1) {
    // EphData subPoint = subPointList.get(index);
    //
    //
    // //x为星下点到目标点球面距离，单位：公里
    // double x = Math.cos((90.0 - subPoint.getLat()) * Math.PI / 180.0)
    // * Math.cos((90.0 - target.getLat()) * Math.PI / 180.0)
    // + Math.sin((90.0 - subPoint.getLat()) * Math.PI / 180.0)
    // * Math.sin((90.0 - target.getLat()) * Math.PI / 180.0)
    // * Math.cos(Math.abs(subPoint.getLon() - target.getLon()) * Math.PI / 180.0);
    // x = Math.acos(x) * 180.0 / Math.PI * 111.0;
    //
    // if (x <= elimit.getMaxCoverage()) {
    // accessMessage = imageComputeAll(target, this.imageLimitation, subPointList);
    // if ((accessMessage.getBeginConeangle() <= elimit.getMaxConeangle()) &&
    // (accessMessage.getEndConeangle() <= elimit.getMaxConeangle()) &&
    // (accessMessage.getBeginTime()) != (accessMessage.getEndTime())) {
    // AccessData accessData = new AccessData();
    //
    //// accessData.setSatName(subPoint.getSatName());
    //// accessData.setPass((long)subPoint.getPassNum());
    // accessData.setBeginTime(accessMessage.getBeginTime());
    // accessData.setEndTime(accessMessage.getEndTime());
    // accessData.setBeginConeangle(accessMessage.getBeginConeangle());
    // accessData.setEndConeangle(accessMessage.getEndConeangle());
    // electronAccessDatas.add(accessData);
    // index = (int) (index + elimit.getStepSpan());
    // }
    // // index=index+elimit.StepSpan;
    // }
    // index++;
    // }
    // return electronAccessDatas;
    //
    // }

    // public List<AccessData> computeAccessDatas(List<EphData> subPointList,
    // List<SphericalPoint> target) {
    // List<AccessData> windows_list = new ArrayList<>();
    // for (SphericalPoint sphericalPoint_one : target) {
    // windows_list.addAll(ElectronAccess_point(subPointList, sphericalPoint_one));
    // System.out.println(windows_list.size());
    //
    // }
    // return windows_list;
    // }

    public float getHalfAngleFOV() {
        return (float) (imageLimitation.getMaxConeAngle() / 2);
    }

    public AccessData imageComputeAll(SphericalPoint target, ElectronImageLimitation limit, List<EphData> ephSourceList,
                                      int imageTime) {
        AccessData message = null;

        int id1 = index;
        int id2 = -1;
        int startIdx = -1;
        int endIdx = -1;

        if (id1 >= ephSourceList.size() - 1) {
            return null;
        } else {
        }

        while (id1 < ephSourceList.size() - 1) {
            if (!checkEphContinueBeforeIDX(ephSourceList, id1)) {
                index++;
                break;
            }
            EphData subPoint = ephSourceList.get(id1);

            float coneangle1 = (float) coneangleBetweenEphAndTarget(subPoint, target);
            double x = GlobeMath.distanceIngeoLatLon(subPoint, target);//

            double maxConeAngle = Math.min(calculateMaxConeAngleByEphAtEarthCutLine(subPoint),imageLimitation.getMaxConeAngle());
            // System.out.println("ConeAnlge:"+coneangle1);
            double maxCoverageDistance = GeoUtils.computeDistanceRadBySatFovAngle(subPoint.getHigh(), maxConeAngle)
                    * AstroConst.R_Earth / 1000;
            ;// 最大覆盖距离
            if (coneangle1 <= imageLimitation.getMaxConeAngle() && x <= imageLimitation.getMaxDistance_to_shot()) {
                id2 = id1;
                startIdx = id1;
                break;
            }
            id1++;
        }

        // 如果找到了起始点,开始找终点
        if (id2 > 0) {
            while (id2 <= ephSourceList.size() - 1) {

                // 如果未找到终点已经发现不连续，则直接跳出，重新找起点。
                if (!checkEphContinueBeforeIDX(ephSourceList, id2)) {
                    index = id2;
                    break;
                }
                EphData subPoint2 = ephSourceList.get(id2);
                float coneangle2 = (float) coneangleBetweenEphAndTarget(subPoint2, target);
                double x = GlobeMath.distanceIngeoLatLon(subPoint2, target);//

                double maxConeAngle = Math.min(calculateMaxConeAngleByEphAtEarthCutLine(subPoint2),
                        imageLimitation.getMaxConeAngle());
                // double maxCoverageDistance =
                // GeoUtils.computeDistanceRadBySatFovAngle(subPoint2.getHigh(), maxConeAngle)*
                // AstroConst.R_Earth;

                // 找到超出边界点,或者结尾点，设置endIdx
                if (coneangle2> imageLimitation.getMaxConeAngle() || x > imageLimitation.getMaxDistance_to_shot() || (id2 == ephSourceList.size() - 1)) {
                    endIdx = id2 - 1;
                    index = id2;
                    break;
                } else {
                    // 未超出边界，继续向下搜索
                    id2++;
                }
            }

            // 设置开始和结束信息
            {

                // 找到的成像窗口时间
                int found_timeLength = (int) ((endIdx - startIdx) * imageLimitation.getStepLength());

                // 如果找到的时间块小于最小的成像时间要求，则直接返回null
                if (found_timeLength < imageLimitation.getMinAccessDuration()) {
                    return null;
                }

                // 如果成像要求时间大于最大约束时间，直接将imagetime改为最大约束时间。（或者直接返回空）
                if (imageTime >= imageLimitation.getMaxAccessDuration()
                        || found_timeLength >= imageLimitation.getMaxAccessDuration())
                    imageTime = imageLimitation.getMaxAccessDuration();
                else if (imageTime <= DEFAULT_MAXPOSSABLE_TIME_LENGTH || imageTime< imageLimitation.getMaxAccessDuration()) {
                    imageTime = found_timeLength;
                }

                if (startIdx > -1 && endIdx > -1) {

                    // 找到中心点
                    int nearestIndex = findNearestIdxBetweenSE(ephSourceList, startIdx, endIdx, target);
                    double coneangle_nearest = coneangleBetweenEphAndTarget(ephSourceList.get(nearestIndex), target);
                    // 中心点扩展指定时间进行。
                    if(imageTime!=found_timeLength){
	                    // 如果输入的成像为具体数值，则检查已找到窗口时长与要求的时长之差，如果该差大于等于0则认为可以安排拍摄。
	                    int time_distance_step_to_extend = imageTime / 2;
	                    
	                    startIdx = nearestIndex - (int) (time_distance_step_to_extend / imageLimitation.getStepLength());
	                    endIdx = startIdx + (int) (imageTime / imageLimitation.getStepLength());
	                    if (startIdx < 0)
	                        startIdx = 0;
	                    if (endIdx > ephSourceList.size() - 1)
	                        endIdx = ephSourceList.size() - 1;
                    }

                    List<EphData> accessEphDataList = new ArrayList<>();
                    for (int i = startIdx; i <= endIdx; i++) {
                        accessEphDataList.add(ephSourceList.get(i));
                    }
                    //检查是否连续，如果不连续，直接认为不满足拍摄时长要求，返回空
                    if (!checkEphContinue(accessEphDataList)) {
                        return null;
                    } else {
                        EphData subPoint_start = ephSourceList.get(startIdx);
                        double coneangle_start = coneangleBetweenEphAndTarget(subPoint_start, target);
                        message = new AccessData();
                        message.setBeginTime(subPoint_start.getEpochTime());
                        message.setBeginConeangle((float) coneangle_start);

                        EphData subPoint_end = ephSourceList.get(endIdx);
                        double coneangel_end = coneangleBetweenEphAndTarget(subPoint_end, target);
                        message.setEndTime(subPoint_end.getEpochTime());
                        message.setEndConeangle((float) coneangel_end);

                        System.out.println(ephSourceList.get(nearestIndex).toString());
                        message.setCenterConeangle((float) coneangle_nearest);//
                        message.setAccessPosition_ephdata(ephSourceList.get(nearestIndex));
                        message.setAccessTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(message.getAccessPosition_ephdata().getEpochTime()));

                        message.setCenter_position(target);
                        message.setPositionEphdataList(accessEphDataList);
                        return message;
                    }

                } else {
                    return null;
                }
            }

        } else {
            // index++;
            return null;
        }
    }

    double coneangleBetweenEphAndTarget(EphData ephData, SphericalPoint target) {
        PositionLLA pos1 = new PositionLLA();
        pos1.setLon(ephData.getLon());
        pos1.setLat(ephData.getLat());
        pos1.setHigh(ephData.getHigh());
        double coneangle = Math.abs(GeoUtils.accessLLA(pos1, target));
        return coneangle;
    }

    int findNearestIdxBetweenSE(List<EphData> ephSourceList, int start, int end, SphericalPoint target) {
        EphData subPoint4 = ephSourceList.get(start);
        int nearestIndex = start;
        double distance_shortest = GlobeMath.distanceIngeoLatLon(subPoint4, target);// distanceIngeoLatLon(subPoint4,target);
        for (int j = start; j <= end; j++) {
            EphData subPoint_x = ephSourceList.get(j);
            double distance_x = GlobeMath.distanceIngeoLatLon(subPoint_x, target);// distanceIngeoLatLon(subPoint_x,
                                                                                    // target);
            // System.out.println("distance_x:" + distance_x + "   " + subPoint_x.getTimeStr());
            if (distance_shortest > distance_x) {
                distance_shortest = distance_x;
                nearestIndex = j;
            }
        }
        return nearestIndex;
    }

    double calculateMaxConeAngleByEphAtEarthCutLine(EphData ephData) {
        double R = AstroConst.R_Earth;
        double altitude = ephData.getHigh();
        double fov = Math.asin(R / (R + altitude));
        return Math.toDegrees(fov);
    }

}
