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.SphericalPoint;
import com.base.core.source.Angle;
import com.base.core.source.GlobeMath;
import com.base.core.source.LatLon;
import com.base.core.utiles.AstroConst;
import com.base.core.utiles.ListUtils;

import java.util.*;


/*
使用说明
1、创建AccessWindowsService实例，_8和_18独立，分别获取服务实例。分别使用。
2、输入信息：
    List<SubPoint> 星下点数据列表，List<SphericalPoint>由点组成的列表。单点为点目标，多于一点为区域目标。
    a、区域目标会自动转换为点阵，点阵间距默认为3度，最大点间距大概300公里，对于大尺度区域和普查卫星应该满足要求，对于精细计算选择何种尺度还需进一步研究。
    b、对点阵中的点进行逐个计算访问窗口，形成访问窗口队列
    c、对访问窗进行合并处理，当且仅当窗口间有交叉时进行合并处理。最大合并窗口是30分钟，电子星8号。
    d、返回合并后的访问窗口
3、输出信息：访问窗口队列，List<ElectronAccessData>

   AccessWindowsService accessWindowsService = AccessWindowsService.getAccessWindows_8();


   List<ElectronAccessData> test_result  = accessWindowsService.electronComputeAndCombine(subPointList,point_inarea);

   模块内部没有使用全局或临界资源，应该不存在并发冲突问题。
   模块内部没有进行并行化处理，可以考虑在外部框架安排。

 */
abstract public class AccessWindowsService {





    protected ElectronImageLimitation imageLimitation;

    public final int DEFAULT_MAXPOSSABLE_TIME_LENGTH =0;

//    public abstract float getDefaultPointDistance();
//    public abstract float getMaxWindonwLimit();
//    public abstract List<AccessData> computeAccessDatas(List<EphData> ephDataList, List<SphericalPoint> target);

    public void setup(ElectronImageLimitation imageLimitation){
        this.imageLimitation = imageLimitation;
    }

    public ElectronImageLimitation getImageLimitation() {
        return imageLimitation;
    }

    public void setImageLimitation(ElectronImageLimitation imageLimitation) {
        this.imageLimitation = imageLimitation;
    }

    public List<SphericalPoint> convertAreaToPoints(List<SphericalPoint> polygonAreaPoints) {
        return convertAreaToPoints(polygonAreaPoints, imageLimitation.getDefault_point_distance());
    }

/*20190615 YHL修改，用新函数替代
 *     static public List<SphericalPoint> convertAreaToPoints(List<SphericalPoint> polygonAreaPoints, double distance) {
        if (polygonAreaPoints == null)
            return null;
        if (polygonAreaPoints.size() <= 2)
            return polygonAreaPoints;
        SphericalPoint firstAreaPoints = null;
        SphericalPoint previousAreaPoints = null;
        List<SphericalPoint> resultPoints = new ArrayList<>();
        //将多边形转为外接矩形
        List<Double> rec_XY = convertPolygonToRectangleXY(polygonAreaPoints);
        //将外接矩形转为点阵
        List<SphericalPoint> rec_pointsArray = convertRectangleToPointsArray(rec_XY.get(0), rec_XY.get(1), rec_XY.get(2), rec_XY.get(3), distance);
        //对点阵中的逐点进行判断，是否在多边形中,在多边形中的，放入结果列表中。
        List<SphericalPoint> polygon_pointsArray = new ArrayList();
        for (SphericalPoint point : rec_pointsArray) {
            if (IsInside(polygonAreaPoints, point))
                polygon_pointsArray.add(point);
        }
        polygon_pointsArray.addAll(polygonAreaPoints);
        return polygon_pointsArray;

    }
*/
    /*20190615 YHL增加*/
    static public List<SphericalPoint> convertAreaToPoints(List<SphericalPoint> polygonAreaPoints, double distance) {
        if (polygonAreaPoints == null)
            return null;
        if (polygonAreaPoints.size() <= 2)
            return polygonAreaPoints;
        SphericalPoint firstAreaPoints = null;
        SphericalPoint previousAreaPoints = null;
        List<SphericalPoint> resultPoints = new ArrayList<>();
        // 将多边形转为外接矩形
        List<Double> rec_XY = convertPolygonToRectangleXY(polygonAreaPoints);
        // 将外接矩形转为点阵
        List<SphericalPoint> rec_pointsArray = convertRectangleToPointsArray(rec_XY.get(0), rec_XY.get(1),
                rec_XY.get(2), rec_XY.get(3), distance);
        // 对点阵中的逐点进行判断，是否在多边形中,在多边形中的，放入结果列表中。
        List<SphericalPoint> polygon_pointsArray = new ArrayList();
        List<SphericalPoint> points_outside_Array = new ArrayList();

        for (SphericalPoint point : rec_pointsArray) {
            if (!containsPoint(polygonAreaPoints, point)) {
                points_outside_Array.add(point);
                // 如果没有与顶点重和，添加进入，否则不加入
                // boolean repeated = false;
                // for (SphericalPoint vertexPoint : polygonAreaPoints) {
                //     if (point.getLat() == vertexPoint.getLat() && point.getLon() == vertexPoint.getLon()) {
                //         repeated = true;
                //         continue;
                //     }
                // }

                // if (!repeated)
                //     polygon_pointsArray.add(point);
            }
        }
        rec_pointsArray.removeAll(points_outside_Array);
        polygon_pointsArray.addAll(rec_pointsArray);
        return polygon_pointsArray;

    }
    /*20190615 YHL增加*/
    static public boolean containsPoint(List<SphericalPoint> polygPoints, SphericalPoint p) {
        double[] point = new double[2];
        double[][] vertices = new double[polygPoints.size()][2];
        
        //gaoyue edit 20190625
        double x_min = 0, x_max = 0, y_min = 0, y_max = 0;
        int count = 0;
        for (SphericalPoint pp : polygPoints) {
            if (count == 0) {
                x_min = pp.getLon();
                y_min = pp.getLat();
                x_max = pp.getLon();
                y_max = pp.getLat();
                count++;
                continue;
            } else {
                if (x_min > pp.getLon())
                    x_min = pp.getLon();
                if (x_max < pp.getLon())
                    x_max = pp.getLon();
                if (y_min > pp.getLat())
                    y_min = pp.getLat();
                if (y_max < pp.getLat())
                    y_max = pp.getLat();
            }
        }
        if (Math.abs(x_min)+Math.abs(x_max) > 180)
        {
        	point[0] = p.getLat() + 90;
        	point[1] = p.getLon() < 0 ? p.getLon() + 360 : p.getLon();
        	for (int i = 0; i < polygPoints.size(); i++) {
                vertices[i][0] = polygPoints.get(i).getLat() + 90;
                vertices[i][1] = polygPoints.get(i).getLon() < 0 ? polygPoints.get(i).getLon() + 360 : polygPoints.get(i).getLon();
            }
        }
        else
        {
        	point[0] = p.getLat() + 90;
        	point[1] = p.getLon();
        	for (int i = 0; i < polygPoints.size(); i++) {
        		vertices[i][0] = polygPoints.get(i).getLat() + 90;
        		vertices[i][1] = polygPoints.get(i).getLon();
            }
        }
        // edit end 20190625
        /*
        point[0] = p.getLat() + 90;
        point[1] = p.getLon() +180 < 0 ? p.getLon() + 360 : p.getLon();

        for (int i = 0; i < polygPoints.size(); i++) {
            vertices[i][0] = polygPoints.get(i).getLat() + 90;
            vertices[i][1] = polygPoints.get(i).getLon() < 0 ? polygPoints.get(i).getLon() + 360: polygPoints.get(i).getLon();
        }
        */
        int verticesCount = vertices.length;
        int nCross = 0;
        for (int i = 0; i < verticesCount; ++i) {
            double[] p1 = vertices[i];
            double[] p2 = vertices[(i + 1) % verticesCount];

            // 求解 y=p.y 与 p1 p2 的交点
            if (p1[0] == p2[0]) { // p1p2 与 y=p0.y平行
                continue;
            }
            if (point[0] < Math.min(p1[0], p2[0])) { // 交点在p1p2延长线上
                continue;
            }
            if (point[0] >= Math.max(p1[0], p2[0])) { // 交点在p1p2延长线上
                continue;
            }
            // 求交点的 X 坐标
            double x = (point[0] - p1[0]) * (p2[1] - p1[1]) / (p2[0] - p1[0]) + p1[1];
            if (x > point[1]) { // 只统计单边交点
                nCross++;
            }
        }
        // 单边交点为偶数，点在多边形之外
        return (nCross % 2 == 1);
    }
    
    static public List<SphericalPoint> convertPolygonToRectangle(List<SphericalPoint> polygonPoints) {
        double x_min = 0, x_max = 0, y_min = 0, y_max = 0;
        int count = 0;
        for (SphericalPoint point : polygonPoints) {
            if (count == 0) {
                x_min = point.getLon();
                y_min = point.getLat();
                x_max = point.getLon();
                y_max = point.getLat();
                count++;
                continue;
            } else {
                if (x_min > point.getLon())
                    x_min = point.getLon();
                if (x_max < point.getLon())
                    x_max = point.getLon();
                if (y_min > point.getLat())
                    y_min = point.getLat();
                if (y_max < point.getLat())
                    y_max = point.getLat();
            }
        }
        List<SphericalPoint> rectangle_pointslist = new ArrayList<>();
        rectangle_pointslist.add(new SphericalPoint(x_min, y_min));
        rectangle_pointslist.add(new SphericalPoint(x_min, y_max));
        rectangle_pointslist.add(new SphericalPoint(x_max, y_max));
        rectangle_pointslist.add(new SphericalPoint(x_max, y_min));
        return rectangle_pointslist;
    }

    static public List<Double> convertPolygonToRectangleXY(List<SphericalPoint> polygonPoints) {
        double x_min = 0, x_max = 0, y_min = 0, y_max = 0;
        int count = 0;
        for (SphericalPoint point : polygonPoints) {
            if (count == 0) {
                x_min = point.getLon();
                y_min = point.getLat();
                x_max = point.getLon();
                y_max = point.getLat();
                count++;
                continue;
            } else {
                if (x_min > point.getLon())
                    x_min = point.getLon();
                if (x_max < point.getLon())
                    x_max = point.getLon();
                if (y_min > point.getLat())
                    y_min = point.getLat();
                if (y_max < point.getLat())
                    y_max = point.getLat();
            }
        }

//        if(Math.abs(x_max-x_min)>180){
//            double temp = x_max;
//            x_max = x_min;
//            x_min = temp;
//        }

        List<Double> rectangle_xyminmaxlist = new ArrayList<>();
        rectangle_xyminmaxlist.add(new Double(x_min));
        rectangle_xyminmaxlist.add(new Double(x_max));
        rectangle_xyminmaxlist.add(new Double(y_min));
        rectangle_xyminmaxlist.add(new Double(y_max));
        return rectangle_xyminmaxlist;
    }


/*20190615 YHL修改，用新函数替代
    static public List<SphericalPoint> convertRectangleToPointsArray(double x_min, double x_max, double y_min, double y_max, double distance) {
//        double x_min=rectangle.get(0),x_max=rectangle.get(1),y_min=rectangle.get(2),y_max=rectangle.get(3);
        List<SphericalPoint> list = new ArrayList<>();


        List<SphericalPoint> points_array = new ArrayList<>();
        points_array.add(new SphericalPoint(x_min, y_min));
        points_array.add(new SphericalPoint(x_min, y_max));
        points_array.add(new SphericalPoint(x_max, y_max));
        points_array.add(new SphericalPoint(x_max, y_min));
        for (double x = x_min + distance; x < x_max; x += distance) {
            if(x>180){
                x=-180+(180-x);
            }
            for (double y = y_min + distance; y < y_max; y += distance) {
                points_array.add(new SphericalPoint(x, y));
            }
        }
        return points_array;
    }
*/
    /*20190615 YHL增加*/
    static public List<SphericalPoint> convertRectangleToPointsArray(double x_min, double x_max, double y_min,
            double y_max, double distance) {
        List<List<SphericalPoint>> points_array = convertRectangleToPointsArray_rows(x_min, x_max,y_min,y_max,distance);
        List<SphericalPoint> points_output = new ArrayList<>();
        if(points_array!=null){
            for(List<SphericalPoint> points_one_row: points_array){
                if(points_one_row!=null)
                    points_output.addAll(points_one_row);
            }
        }
        return points_output;
    }
    /*20190615 YHL增加*/
    static public List<List<SphericalPoint>> convertRectangleToPointsArray_rows(double x_min, double x_max,
            double y_min, double y_max, double distance) {
        // double
        // x_min=rectangle.get(0),x_max=rectangle.get(1),y_min=rectangle.get(2),y_max=rectangle.get(3);
        List<SphericalPoint> list = new ArrayList<>();

        List<List<SphericalPoint>> points_array = new ArrayList<>();
        // points_array.add(new SphericalPoint(x_min, y_min));
        // points_array.add(new SphericalPoint(x_max, y_min));
        // points_array.add(new SphericalPoint(x_min, y_max));
        // points_array.add(new SphericalPoint(x_max, y_max));
        boolean x_tail, y_tail;
        boolean cross180 = (180 - Math.abs(x_min) + (180 - Math.abs(x_max)) < (x_max - x_min)) ? true : false;

        for (double y = y_min; y < y_max + distance; y += distance) {
            List<SphericalPoint> points_row = new ArrayList<>();
            boolean flag_end = false;
            if (y >= y_max) {
                flag_end = true;
                y = y_max;
            }
            double coord_y;
            if (y > 90) {
                coord_y = 90 - (y - 90);
            } else {
                coord_y = y;
            }

            if (cross180) {
                double x;
                for (x = x_min; x > -180; x += -distance) {
                    double coord_x;

                    // -175至175
                    if (x < -180) {
                        coord_x = 180 + (x + 180);
                    } else if (x > 180) {
                        coord_x = -180 + (x - 180);
                    } else {
                        coord_x = x;
                    }

                    points_row.add(new SphericalPoint(coord_x, coord_y));

                }
                for (x = 180 + (180 + x); x > x_max; x += -distance) {
                    double coord_x;

                    // -175至175
                    if (x < -180) {
                        coord_x = 180 + (x + 180);
                    } else if (x > 180) {
                        coord_x = -180 + (x - 180);
                    } else {
                        coord_x = x;
                    }

                    points_row.add(new SphericalPoint(coord_x, coord_y));

                }
            } else {
                for (double x = x_min; x < x_max; x += distance) {
                    double coord_x;

                    // -175至175
                    if (x < -180) {
                        coord_x = 180 + (x + 180);
                    } else if (x > 180) {
                        coord_x = -180 + (x - 180);
                    } else {
                        coord_x = x;
                    }

                    points_row.add(new SphericalPoint(coord_x, coord_y));

                }
            }
            // 每行最后一点
            points_row.add(new SphericalPoint(x_max, y));

            points_array.add(points_row);
            if (flag_end) {
                break;
            }
        }
        // //添加最后一行
        // List<SphericalPoint> points_row_last = new ArrayList<>();
        // for (double x = x_min ; x < x_max; x += distance) {
        // double coord_x;
        // x_tail = false;

        // if (x > 180) {
        // coord_x = -180 + (x-180 );
        // }else{
        // coord_x=x;
        // }

        // points_row_last.add(new SphericalPoint(coord_x, y_max));

        // }
        // //添加最后一行，最后一个点
        // points_row_last.add(new SphericalPoint(x_max, y_max));

        return points_array;
    }
    
    static public boolean IsInside(List<SphericalPoint> points, SphericalPoint targ_point) {
        int count = points.size();
        SphericalPoint p = targ_point;
        if (count < 3) {
            return false;
        }

        boolean result = false;

        for (int i = 0, j = count - 1; i < count; i++) {
            SphericalPoint p1 = points.get(i);
            SphericalPoint p2 = points.get(j);

            if (p1.getLat() <= p.getLat() && p2.getLat() >= p.getLat() || p2.getLat() <= p.getLat() && p1.getLat() >= p.getLat()) {
                if (p1.getLon() + (p.getLat() - p1.getLat()) / (p2.getLat() - p1.getLat()) * (p2.getLon() - p1.getLon()) <= p.getLon()) {
                    result = !result;
                }
            }
            j = i;
        }
        return result;
    }



    public List<AccessData> accessWindowCombine(List<AccessData> accessDataList) {
        List<AccessData> working_accessDatas = new LinkedList<>();
        working_accessDatas.addAll(accessDataList);
        List<AccessData> combined_result_List = new LinkedList<>();

        System.out.println("Combining AccessWindows!\n  num of all accessData:"+ accessDataList.size());

        while (!working_accessDatas.isEmpty()) {
            AccessData current = working_accessDatas.get(0);
            AccessData target = null;
            boolean combineflag = false;
            for (int i = 1; i < working_accessDatas.size(); i++) {
                //如果有相交，进行合并，TBD:或其他合并条件
                target = working_accessDatas.get(i);
                if (isAccessDataIntersect(current, target)) {
                    //可以检查如果需要合并，合并后的时长是否超出了最大连续侦察时长。防止无限合并
                    long windowSize = combineTwoAccessData(current, target).getEndTime().getTime() - combineTwoAccessData(current, target).getBeginTime().getTime();
                    windowSize /= 1000;//to seconds

                    if (windowSize <= imageLimitation.getMaxAccessDuration()) {
                        combineflag = true;
                        break;
                    }
                }
            }
            //如果经过比较,发现不需要合并，说明该时间段已经为最长时间段，从工作列表中移至结果列表中。
            if (!combineflag) {
                working_accessDatas.remove(current);
                combined_result_List.add(current);

            } else {
                //如果发现需要进行合并，将发现目标窗口与工作窗口合并，并将合并后的新窗口放在列表第一个，等待再次访问
                AccessData combined_single = combineTwoAccessData(current, target);
                working_accessDatas.remove(current);
                working_accessDatas.remove(target);
                working_accessDatas.add(0, combined_single);
            }
        }
        
        //此处有缺陷 ，对于8号 
        for(AccessData accessData :combined_result_List){
            List<SphericalPoint> targetAreaPointsArray = accessData.getPointsArrayOfTarget();
            int counter=0;
            Set<Integer> gridCodeSet = new HashSet<>();
            Set<Integer> targetAreaCodeSet = new HashSet<>();
            for(SphericalPoint sphericalPoint:targetAreaPointsArray) {
//                List<SphericalPoint> area =  accessData.getVertex_Points();
//                if(IsInside(area,sphericalPoint)){
//                    gridCodeSet.add(counter);
//                }
                targetAreaCodeSet.add(counter);
                counter++;
            }
//            if(!gridCodeSet.isEmpty()){
//            	accessData.setAccessDataGridsCodeSet(gridCodeSet);
//            }
            accessData.setTargetAreaGridsCodeSet(targetAreaCodeSet);
        }
        System.out.println("Combining AccessWindows completed!\n num of combined accessData:"+ combined_result_List.size());

        return combined_result_List;
    }
    //获取半视场角
    private boolean isAccessDataIntersect(AccessData timeWindow_A, AccessData timeWindow_B) {
        boolean flag_timeIntersect = timeWindow_A.getBeginTime().getTime() <= timeWindow_B.getEndTime().getTime() && timeWindow_A.getEndTime().getTime() >= timeWindow_B.getBeginTime().getTime();
        if(imageLimitation.getMaxRollDegree()!=0){
        	//进行逐点视场角判断
        	//当前AccessData A与另一AccessData B进行合并时，
        	// 首先检查A与B的测摆角之差是否小于半视场角HAFOV，如果小于则认为可以合并，生成C，合并后以A的测摆角作为新的测摆角，将C中放置合并后的所有ACData
        	//电子星rollAngle都是0，则不受此判断影响
        	float fragment_rollangle = (float) Math.abs(timeWindow_A.getRollAngle()-timeWindow_B.getRollAngle());
        	float fragment_pitchangle = (float) Math.abs(timeWindow_A.getPitchAngle()-timeWindow_B.getPitchAngle());

        	boolean flag_rollAngleInterSect = (imageLimitation.getHAFOV()/2>fragment_rollangle);
        	boolean flag_pitchlAngleInterSect = (imageLimitation.getHAFOV()/2>fragment_pitchangle);

        	return flag_timeIntersect && flag_rollAngleInterSect && flag_pitchlAngleInterSect;
        }else
        	return flag_timeIntersect;
    }

    public AccessData combineTwoAccessData(AccessData timeWindow_A, AccessData timeWindow_B) {
        Date ealiestBegin, latestEnd;
        float coneangleBegin, coneangleEnd;
        ealiestBegin = timeWindow_A.getBeginTime().before(timeWindow_B.getBeginTime()) ? (timeWindow_A.getBeginTime()) : timeWindow_B.getBeginTime();
        coneangleBegin = timeWindow_A.getBeginTime().before(timeWindow_B.getBeginTime()) ? timeWindow_A.getBeginConeangle() : timeWindow_A.getBeginConeangle();
        latestEnd = timeWindow_A.getEndTime().after(timeWindow_B.getEndTime()) ? timeWindow_A.getEndTime() : timeWindow_B.getEndTime();
        coneangleEnd = timeWindow_A.getBeginTime().before(timeWindow_B.getBeginTime()) ? timeWindow_A.getEndConeangle() : timeWindow_A.getEndConeangle();
        AccessData combined_singleAccessData = new AccessData();
        combined_singleAccessData.setAccessTime(timeWindow_A.getAccessTime());
        combined_singleAccessData.setBeginTime(ealiestBegin);
        combined_singleAccessData.setEndTime(latestEnd);
        combined_singleAccessData.setBeginConeangle(coneangleBegin);
        combined_singleAccessData.setEndConeangle(coneangleEnd);
        combined_singleAccessData.combineToAccessDataList(timeWindow_A);
        combined_singleAccessData.combineToAccessDataList(timeWindow_B);
        combined_singleAccessData.setRollAngle(timeWindow_A.getRollAngle());
        combined_singleAccessData.setPitchAngle(timeWindow_A.getPitchAngle());
        combined_singleAccessData.setSunElevation((timeWindow_A.getSunElevation()+timeWindow_B.getSunElevation())/2);
        combined_singleAccessData.setCenter_position(timeWindow_A.getCenter_position());
        combined_singleAccessData.setAccessPosition_ephdata(timeWindow_A.getAccessPosition_ephdata());
        if(timeWindow_A.getBase_AccessData()!=null&&timeWindow_B.getBase_AccessData()!=null){
            AccessData baseAccessData = combineTwoAccessData(timeWindow_A.getBase_AccessData(),timeWindow_B.getBase_AccessData());
            combined_singleAccessData.setBase_AccessData(baseAccessData);
        }

        computeVertexPointsOfAccessData(combined_singleAccessData);

        combined_singleAccessData.combineGridSet(timeWindow_A);
        combined_singleAccessData.combineGridSet(timeWindow_B);
        combined_singleAccessData.setPointsArrayOfTarget(timeWindow_A.getPointsArrayOfTarget());

//        System.out.println("Combined:"+timeWindow_A+" "+timeWindow_B);
        return combined_singleAccessData;
    }

    public List<AccessData> electronComputeAndCombine(List<EphData> subPointList, SphericalPoint target) {
        return accessWindowCombine(computeAccessData(subPointList, target, DEFAULT_MAXPOSSABLE_TIME_LENGTH));
    }
    public boolean is_eph_continue = false;
    public List<AccessData> computeAccessData(List<EphData> subPointList, SphericalPoint target, int imageTime) {
        is_eph_continue = checkEphContinue(subPointList);
        List<AccessData> accessDataList = imageAccessAll(target,imageLimitation,subPointList, imageTime);
        for(AccessData accessData:accessDataList){
        	computeVertexPointsOfAccessData(accessData);
        }
        return accessDataList;
    }

    protected boolean checkEphContinue(List<EphData> subPointList){
        if(subPointList==null)
            return false;

        int size = subPointList.size();
        if(size<1)
            return false;
        Date date_start = subPointList.get(0).getEpochTime();
        Date date_end = subPointList.get(size-1).getEpochTime();
        if(size==(date_end.getTime()/1000-date_start.getTime()/1000+1)){
            return true;
        }else{
            return false;
        }
    }

    protected boolean checkEphContinueBeforeIDX(List<EphData> subPointList, int idx){
        if(subPointList==null)
            return false;
        int size = subPointList.size();
        if(size<1 || size < idx+1)
            return false;
        else if(size==1)
            return true;
        else if(idx==0)
            return true;

        Date date_start = subPointList.get(idx).getEpochTime();
        Date date_end = subPointList.get(idx-1).getEpochTime();
        long distance = (date_end.getTime()-date_start.getTime());
        if(distance<=imageLimitation.getStepLength()*1000){
            return true;
        }else{
            return false;
        }
    }




    public void computeVertexPointsOfAccessData(AccessData accessData) {

        AccessData imageAccessData = accessData;
        if(accessData.getBase_AccessData()!=null)
            imageAccessData  = accessData.getBase_AccessData();

        LatLon target_latlon = LatLon.fromDegrees(imageAccessData.getCenter_position().getLat(), imageAccessData.getCenter_position().getLon());
        LatLon accessEPH_latlon = LatLon.fromDegrees(imageAccessData.getAccessPosition_ephdata().getLat(), imageAccessData.getAccessPosition_ephdata().getLon());
        Angle width_Angle = accessData.getRollAngle()!=0?LatLon.greatCircleDistance(target_latlon, accessEPH_latlon):Angle.ZERO;

        List<EphData> accessEphList = imageAccessData.getPositionEphdataList();
        if(accessEphList.size()<2)
            return;
        int ephData_earliest_index = 0;
        int ephData_latest_index = 0;

        //访问星历可能非有序.
        for(int i=1;i<accessEphList.size();i++){
            EphData earliest_ephData = accessEphList.get(ephData_earliest_index);
            EphData latest_ephData = accessEphList.get(ephData_latest_index);
            EphData current = accessEphList.get(i);
            if(current.getEpochTime().before(earliest_ephData.getEpochTime())){
                ephData_earliest_index = i;
            }else if(current.getEpochTime().after(latest_ephData.getEpochTime())){
                ephData_latest_index = i;
            }

        }

        int top_pos1,top_pos2;
        int bottom_pos1,bottom_pos2;
        if((ephData_earliest_index+1)>(accessEphList.size()-1)){
            //特例分支
            top_pos1 = ephData_earliest_index-1;
            top_pos2 = ephData_earliest_index;
        }else{

            //常规分支
            top_pos1 = ephData_earliest_index;
            top_pos2 = ephData_earliest_index+1;
        }
        if(ephData_latest_index-1<0){
            bottom_pos1 = ephData_latest_index;
            bottom_pos2 = ephData_latest_index+1;
        }else{

            //常规分支
            bottom_pos1 = ephData_latest_index-1;
            bottom_pos2 = ephData_latest_index;
        }



        LatLon position_subpoint_top_1 = new LatLon(Angle.fromDegrees(accessEphList.get(top_pos1).getLat()),Angle.fromDegrees(accessEphList.get(top_pos1).getLon()));
        LatLon position_subpoint_top_2 = new LatLon(Angle.fromDegrees(accessEphList.get(top_pos2).getLat()),Angle.fromDegrees(accessEphList.get(top_pos2).getLon()));


        LatLon position_subpoint_bottom_1 = new LatLon(Angle.fromDegrees(accessEphList.get(bottom_pos1).getLat()),Angle.fromDegrees(accessEphList.get(bottom_pos1).getLon()));
        LatLon position_subpoint_bottom_2 = new LatLon(Angle.fromDegrees(accessEphList.get(bottom_pos2).getLat()),Angle.fromDegrees(accessEphList.get(bottom_pos2).getLon()));
        double roll = accessData.getRollAngle();
        boolean isDesent = accessEphList.get(ephData_earliest_index).getLon()>accessEphList.get(ephData_latest_index).getLon()
        		?true:false;

        boolean isLeftByEph = (roll < 0  && isDesent) ||(roll >0 && !isDesent);


        boolean isRightCenterLeft = true;
        boolean isLeftCenterRight = false;


        List<SphericalPoint> vertexPointList = new ArrayList<>();

        if(imageLimitation.getShapeOfFOV().equalsIgnoreCase(imageLimitation.SHAPE_FOV_LINE)){
            //上端点
            vertexPointList.addAll(getVertexPointLeftCenterRight(position_subpoint_top_1,position_subpoint_top_2,width_Angle,isLeftCenterRight,isLeftByEph));
            //下端点
            vertexPointList.addAll(getVertexPointLeftCenterRight(position_subpoint_bottom_1,position_subpoint_bottom_2,width_Angle,isRightCenterLeft,isLeftByEph));
//
        }else if(imageLimitation.getShapeOfFOV().equalsIgnoreCase(imageLimitation.SHAPE_FOV_CIRCLE)) {
            //上弧线
            vertexPointList.addAll(getVertexPointARC(position_subpoint_top_1, position_subpoint_top_2, width_Angle, isLeftCenterRight, isLeftByEph));

//            //下弧线
            vertexPointList.addAll(getVertexPointARC(position_subpoint_bottom_1, position_subpoint_bottom_2, width_Angle, isRightCenterLeft, isLeftByEph));

        }else if(imageLimitation.getShapeOfFOV().equalsIgnoreCase(imageLimitation.SHAPE_FOV_SQURE)){
            if(accessData.isImageMode_gaze()){
                vertexPointList.addAll(getVertexPointGazeSquare(target_latlon,position_subpoint_top_1, position_subpoint_top_2, width_Angle, isRightCenterLeft, isLeftByEph));

            }
        }
        imageAccessData.setVertexPoints(vertexPointList);

        accessData.setVertexPoints(imageAccessData.getVertex_Points());
    }


    private List<SphericalPoint> getVertexPointGazeSquare(LatLon target_latlon,LatLon pos, LatLon nextPos, Angle width_Angle, boolean isRightCenterLeft, boolean isLeftByEph){
        //方位角正确
        Angle heading = LatLon.greatCircleAzimuth(pos, nextPos);
        Angle heading_along = Angle.fromRadians(heading.radians+Math.PI / 2);
//        LatLon sensorPos = null;
        //如果降轨
//        if (isLeftByEph) {
//            //星下线右侧
//            sensorPos = GlobeMath.computePositionFromPositionByRadWidth(nextPos, heading, width_Angle.getRadians(), 0);
//        } else {
//            //星下线左侧
//            sensorPos = GlobeMath.computePositionFromPositionByRadWidth(nextPos, heading, width_Angle.getRadians(), 1);
//        }
        LatLon coverPoint_head = GlobeMath.computePositionFromPosition(target_latlon, heading_along, imageLimitation.getSwathInKM() / 2, 0);
        LatLon coverPoint_tail = GlobeMath.computePositionFromPosition(target_latlon, heading_along, imageLimitation.getSwathInKM() / 2, 1);

        List<SphericalPoint> vertext_points = new ArrayList<>();

        LatLon coverPoint_head_left = GlobeMath.computePositionFromPosition(coverPoint_head, heading, imageLimitation.getSwathInKM() / 2, 0);
        LatLon coverPoint_head_right = GlobeMath.computePositionFromPosition(coverPoint_head, heading, imageLimitation.getSwathInKM() / 2, 1);
        LatLon coverPoint_tail_right = GlobeMath.computePositionFromPosition(coverPoint_tail, heading, imageLimitation.getSwathInKM() / 2, 1);
        LatLon coverPoint_tail_left = GlobeMath.computePositionFromPosition(coverPoint_tail, heading, imageLimitation.getSwathInKM() / 2, 0);


        vertext_points.add(convertLatLonIntoSphericalPoint(coverPoint_head_left));
        vertext_points.add(convertLatLonIntoSphericalPoint(coverPoint_head_right));
        vertext_points.add(convertLatLonIntoSphericalPoint(coverPoint_tail_right));
        vertext_points.add(convertLatLonIntoSphericalPoint(coverPoint_tail_left));


        return vertext_points;
    }


    private List<SphericalPoint> getVertexPointARC(LatLon pos, LatLon nextPos, Angle width_Angle, boolean isRightCenterLeft, boolean isLeftByEph){
        //方位角正确
        Angle heading = LatLon.greatCircleAzimuth(pos, nextPos);
        LatLon sensorPos = null;
        //如果降轨
        if (isLeftByEph) {
            //星下线右侧
            sensorPos = GlobeMath.computePositionFromPositionByRadWidth(nextPos, heading, width_Angle.getRadians(), 0);
        } else {
            //星下线左侧
            sensorPos = GlobeMath.computePositionFromPositionByRadWidth(nextPos, heading, width_Angle.getRadians(), 1);
        }
        List<SphericalPoint> vertext_points = new ArrayList<>();





        if(isRightCenterLeft) {
            for(double head_point_degree= heading.getDegrees()-180 ;head_point_degree<=heading.getDegrees();head_point_degree+=10){
                LatLon coverARC_point = GlobeMath.computePositionFromPosition(sensorPos, Angle.fromDegrees(head_point_degree), imageLimitation.getSwathInKM() / 2, 0);
                vertext_points.add(convertLatLonIntoSphericalPoint(coverARC_point));
            }
        }else{
            for(double head_point_degree = heading.getDegrees();head_point_degree<=heading.getDegrees()+180;head_point_degree+=10){
                LatLon coverARC_point = GlobeMath.computePositionFromPosition(sensorPos, Angle.fromDegrees(head_point_degree), imageLimitation.getSwathInKM() / 2, 0);
                vertext_points.add(convertLatLonIntoSphericalPoint(coverARC_point));
            }
        }

        return vertext_points;
    }

    private List<SphericalPoint> getVertexPointLeftCenterRight(LatLon pos, LatLon nextPos, Angle width_Angle, boolean isRightCenterLeft, boolean isLeftByEph){
        //方位角正确
        Angle heading = LatLon.greatCircleAzimuth(pos, nextPos);
        LatLon sensorPos = null;
        //如果降轨
        if (isLeftByEph) {
            //星下线右侧
            sensorPos = GlobeMath.computePositionFromPositionByRadWidth(nextPos, heading, width_Angle.getRadians(), 0);
        } else {
            //星下线左侧
            sensorPos = GlobeMath.computePositionFromPositionByRadWidth(nextPos, heading, width_Angle.getRadians(), 1);
        }
        LatLon coverLine_left = GlobeMath.computePositionFromPosition(sensorPos, heading, imageLimitation.getSwathInKM() / 2, 0);
        LatLon coverLine_right = GlobeMath.computePositionFromPosition(sensorPos, heading, imageLimitation.getSwathInKM() / 2, 1);

        List<SphericalPoint> vertext_points = new ArrayList<>();

        if(isRightCenterLeft) {
            vertext_points.add(convertLatLonIntoSphericalPoint(coverLine_right));
            // vertext_points.add(convertLatLonIntoSphericalPoint(sensorPos));
            vertext_points.add(convertLatLonIntoSphericalPoint(coverLine_left));
        }else{
            vertext_points.add(convertLatLonIntoSphericalPoint(coverLine_left));
            // vertext_points.add(convertLatLonIntoSphericalPoint(sensorPos));
            vertext_points.add(convertLatLonIntoSphericalPoint(coverLine_right));
        }
        return vertext_points;
    }

    public static void main(String[] arg){
       
       
    }




    static public  List<SphericalPoint> convertLatLonIntoSphericalPoint(List<LatLon>  pList){
        List<SphericalPoint> sphericalPointList = new ArrayList<>();
        for(LatLon latLon:pList){
            SphericalPoint sphericalPoint  = new SphericalPoint(latLon.longitude.degrees,latLon.latitude.degrees);
            sphericalPointList.add(sphericalPoint);
        }
        return sphericalPointList;
    }

    static public  SphericalPoint convertLatLonIntoSphericalPoint(LatLon latLon){
        SphericalPoint sphericalPoint  = new SphericalPoint(latLon.longitude.degrees,latLon.latitude.degrees);
        return sphericalPoint;
    }




    //全局索引，在imageComputeAll中创建访问窗口时会被改变
    public int index = 0;


    /**
     * 计算成像卫星访问信息，框架，通用于宽幅可见光，雷达，光学成像卫星-最新使用函数
     *
     * @param target
     * @param limit
     */
    public List<AccessData> imageAccessAll(SphericalPoint target, ElectronImageLimitation limit, List<EphData> ephSourceList,int imageTime) {
        List<AccessData> imageAccessDataList = new ArrayList<>();

        // 检查是否已初始化星历数据
        if (ListUtils.isNoE(ephSourceList) || ephSourceList.size() < 2) {
            return imageAccessDataList;
        }
        // 清空存储成像卫星访问计算结果的序列
        AccessData accessMessage;
        index = 0;

        // 定义pi值
//		double Math.PI = Math.PI;
        while (index < ephSourceList.size() - 1) {
            EphData ephData = ephSourceList.get(index);
            EphData ephData_next = ephSourceList.get(index+1);
            boolean flag_descent = ephData.getLat()>ephData_next.getLat();
            boolean flag_continue = (ephData_next.getEpochTime().getTime()-ephData.getEpochTime().getTime())==imageLimitation.getStepLength()*1000;//默认步长1秒
            if(!flag_continue){
                index++;
                continue;
            }else {
                //降轨成像，却为升轨，升轨成像，却为降轨，跳过
                if ((limit.getImageSign() == 0 && !flag_descent) || (limit.getImageSign() == 1 && flag_descent)) {
                    index++;
                    continue;
                }
            }


            //x为星下点到目标点球面距离，单位：km
            //球面距离公式是计算球面上两点间距离的公式。设所求点A ，纬度β1 ，经度α1 ；点B ，纬度β2 ，经度α2。
            // 则距离S=R·arc cos[cosβ1cosβ2cos（α1-α2）+sinβ1sinβ2]，其中R为球体半径。
            double x=Math.cos((90.0-ephData.getLat())*Math.PI/180.0)*Math.cos((90.0-target.getLat())*Math.PI/180.0)
                    +Math.sin((90.0-ephData.getLat())*Math.PI/180.0)
                    *Math.sin((90.0-target.getLat())*Math.PI/180.0)
                    *Math.cos(Math.abs(ephData.getLon()-target.getLon())*Math.PI/180.0);
            x= AstroConst.R_Earth*Math.acos(x)/1000;//距离
//            double x1 = LatLon.(LatLon.fromDegrees(ephData.getLat(),ephData.getLon()),LatLon.fromDegrees(target.getLat(),target.getLon()));
            //小于最大覆盖距离时开始进一步计算访问信息并搜索
            if((x<=limit.getMaxDistance_to_shot())){
                accessMessage=imageComputeAll(target,limit,ephSourceList,imageTime);
                if(accessMessage!=null){
                    accessMessage.setCenter_position(target);
                    accessMessage.setPass((int) accessMessage.getAccessPosition_ephdata().getPassNum());
                    imageAccessDataList.add(accessMessage);
                    //如果计算得到第一次访问结果，按照重访周期直接跳到后面的星历开始计算，一般可见光卫星跳11小时
                    long jump_step_to_next_revisit=1;
                    if(is_eph_continue) {
                        {
                            //降轨成像，却为升轨，升轨成像，却为降轨，跳过
                            if ((limit.getImageSign() == 0 && !flag_descent) || (limit.getImageSign() == 1 && flag_descent)) {
                                index++;
                                continue;
                            }
                        }
                        jump_step_to_next_revisit = limit.getStepSpan();
                    }else {
                        jump_step_to_next_revisit = 1;
                    }
                    //全局索引在accessComputeAll中已经向前移动过了。
                    index=(int) (index+jump_step_to_next_revisit);
//                        System.out.println("accessMessage.getRollAngle():"+accessMessage.getRollAngle()+"\n next id to work:"+index);

                }
                else{
//                        如果计算没有得到访问结果，向后跳1圈左右的时间,进入下一圈继续搜索
                    int jump_step_to_next_pass = 1;
                    if(is_eph_continue) {
                        jump_step_to_next_pass  = (int) (1.3 * 60 * 60 / limit.getStepLength());
                    }else {
                        jump_step_to_next_pass = 1;
                    }
                    index = (int) (index + jump_step_to_next_pass);
//                        System.out.println("accessMessage.getRollAngle():" + accessMessage.getRollAngle() + "\n next id to work:" + index);


                }
            }else{
//                    System.out.println("distance:"+x+"\n limit:"+limit.getMaxDistance()+"\n Index:"+index);
                index++;
            }

        }

        return imageAccessDataList;
    }

    public abstract AccessData imageComputeAll(SphericalPoint target, ElectronImageLimitation limit, List<EphData> ephSourceList,int imageTime);



    public List<AccessData> imageAccessAll(List<SphericalPoint> target, List<EphData> ephSourceList,int imageTime) {
        //逐卫星、逐项目进行访问窗口计算
        //如果卫星满足任务的星型要求,应该可以返回访问窗口计算服务

        //取得该任务
        List<EphData> ephDataList = ephSourceList;//readEphDataBySatIDAndTask(satellite,task);
        List<SphericalPoint> sphericalPoints_array_taskReq =convertAreaToPoints(target);

        int i=0;
        //对一个任务，逐点计算访问窗口
        List<AccessData> all_accessData_to_Target= new ArrayList<>();
        for(SphericalPoint sphericalPoint_one:sphericalPoints_array_taskReq) {
            List<AccessData> accessDatas_ToOnePoint = computeAccessData(ephDataList, sphericalPoint_one,imageTime);
            for(AccessData accessData:accessDatas_ToOnePoint){
                accessData.setPointsArrayOfTarget(sphericalPoints_array_taskReq);
                //局部编码
                //设置网格遍码为区域拆解后一个点的序号。在一个任务内，该遍码唯一。
                accessData.getAccessDataGridsCodeSet().add(i);

                //设置目标区域的编码集和
                for(int j=0;j<sphericalPoints_array_taskReq.size();j++)
                    accessData.getTargetAreaGridsCodeSet().add(j);
            }
            i++;
            all_accessData_to_Target.addAll(accessDatas_ToOnePoint);
        }

        List<AccessData> candidat_accessData_to_task = all_accessData_to_Target;
        if(target.size()>1) {
            //对该任务所有访问数据进行合并处理，形成该任务所有候选访问数据
            candidat_accessData_to_task = accessWindowCombine(all_accessData_to_Target);


        }
        
        //对合并后的窗口，使用网格计算覆盖率，并设置到accessdata的属性。
        for (AccessData accessData : candidat_accessData_to_task) {
            int coverage_size = accessData.getAccessDataGridsCodeSet().size();
            int target_size = accessData.getTargetAreaGridsCodeSet().size();
            if (target_size > 0)
                accessData.setCoverage_percentage((double) coverage_size / (double) target_size);
        }
        return candidat_accessData_to_task;
        

    }


    /*
    [1月1日～2月1日]	（90º ～ -20º）
	[-20º～-90º）
[2月2日～3月1日]	(90 º～ -13.5º）
	[-13.5º～-90º）
[3月2日～4月1日]	(90º ～ -3º）
	[-3º～-90º)
[4月2日～5月1日]	(90º ～9 º)
	[9º～-90º)
[5月2日～6月1日]	(90º ～ 20º)
	[20º～-90º)
[6月2日～7月1日]	(90º ～ 27º)
	[27º～-90º)
[7月2日～8月1日]	(90º ～ 27º)
	[27º～-90º)
[8月2日～9月1日]	(90º ～ 20º)
	[20º～-90º)
[9月2日～10月1日]	(90º ～ 8º)
	[8º～-90º)
[10月2日～11月1日]	(90º ～ -4º)
	[-4º～-90º)
[11月2日～12月1日]	(90º ～ -13.5º)
	[-13.5º～-90º)
[12月2日～12月31日]	(90º ～ -20º)
	-20º～-90º)
     */

    double[] yawAngleDirectionTable = {
            1, 90, -20,
            2, 90, -13.5,
            3, 90, -3,
            4, 90, 9,
            5,90,20,
            6,90,27,
            7,90,27,
            8,90,20,
            9,90,8,
            10,90,-4,
            11,90,-13.5,
            12,90,-20
    };




    public List<AccessData> YAW90Calibration_AccessAll(List<SphericalPoint> targets, List<EphData> ephSourceList,int imageTime){
        //逐卫星、逐项目进行访问窗口计算
        //如果卫星满足任务的星型要求,应该可以返回访问窗口计算服务
        List<AccessData> accessDatas_ToOnePoint = Custom_AccessAll(targets,ephSourceList,imageTime,0f,0f,0f);

        for(AccessData accessData:accessDatas_ToOnePoint) {
            double yawAngle = getYawAngleByEphData(accessData.getAccessPosition_ephdata());
            accessData.setYawAngle((float) yawAngle);
            SphericalPoint subpoint_center = new SphericalPoint();
            subpoint_center.setLon(accessData.getAccessPosition_ephdata().getLon());
            subpoint_center.setLat(accessData.getAccessPosition_ephdata().getLat());
            accessData.setTargetCenter(subpoint_center);
            accessData.setCenter_position(subpoint_center);
            computeVertexPointsOfAccessData(accessData);
        }
        return accessDatas_ToOnePoint;
    }

    public List<AccessData> Custom_AccessAll(List<SphericalPoint> targets, List<EphData> ephSourceList,int imageTime
            ,float rollangle,float pitchangle,float yawangle){
        List<AccessData> accessDatas_ToOnePoint = imageAccessAll(targets,ephSourceList,imageTime);
        for(AccessData accessData:accessDatas_ToOnePoint) {
            accessData.setRollAngle(rollangle);
            accessData.setPitchAngle(pitchangle);
            accessData.setYawAngle(yawangle);


        }
        return accessDatas_ToOnePoint;
    }

    //etssr-1
    private double getYawAngleByEphData(EphData ephData){
        Date date_eph = ephData.getEpochTime();
        Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
        calendar.setTime(date_eph);
        int month = calendar.get(Calendar.MONTH);
        int day = calendar.get(Calendar.DAY_OF_MONTH);
        int plus =0;
        if(day==1){
            if(month>1)
                plus=1;
        }
        int topvalue_index = (month+plus)*3+1;
        int bottomvalue_index = topvalue_index+1;
        double topvalue = yawAngleDirectionTable[topvalue_index];
        double bottomvalue = yawAngleDirectionTable[bottomvalue_index];
        if(ephData.getLat()>bottomvalue && ephData.getLat()<topvalue)
            return 90;
        else
            return -90;
    }
}