using OpenCvSharp;

namespace SimulationCommon;

public class Rectangular_Area_Search_Function
{
    //墨卡托投影
    public static double MokatuoLon(double lat)
    {
        double R = 6378.137;
        double y = R * Math.Log(Math.Tan((lat + 90) * Math.PI / 360));
        return y;
    }

    public static double MokatuoLat(double lon)
    {
        double R = 6378.137;
        double x = (lon * Math.PI * R) / 180;
        return x;
    }

    //反墨卡托投影
    public static double RMokatuoLat(double y)
    {
        double R = 6378.137;
        double lat = Math.Atan(Math.Exp(y / R)) * 360 / Math.PI - 90;
        return lat;
    }

    public static double RMokatuoLon(double x)
    {
        double R = 6378.137;
        double lon = x * 180 / (R * Math.PI);
        return lon;
    }

    private static double[] GetLinearEquation(Point2f point1, Point2f point2)
    {
        double sign = 1;
        double parameter_a = point2.Y - point1.Y;

        if (parameter_a < 0)
        {
            sign = -1;
            parameter_a = sign * parameter_a;
        }

        double parameter_b = sign * (point1.X - point2.X);
        double parameter_c = sign * (point1.Y * point2.X - point1.X * point2.Y);

        return new double[] { parameter_a, parameter_b, parameter_c };
    }

    private static double[] GetParallelLine(Point2f point, double[] line)
    {
        double parameter_a = line[0];
        double parameter_b = line[1];
        double parameter_c = -point.X * parameter_a - point.Y * parameter_b;

        return new double[] { parameter_a, parameter_b, parameter_c };
    }

    private static double[] GetpPerpendicular(Point2f point, double[] line)
    {
        double parameter_a = line[1];
        double parameter_b = -line[0];
        double parameter_c = -point.Y * parameter_b - point.X * parameter_a;

        return new double[] { parameter_a, parameter_b, parameter_c };
    }

    private static double[] GetCrossPoint(double[] line1, double[] line2)
    {
        double cross_y = (line1[2] * line2[0] - line2[2] * line1[0]) / (line1[0] * line2[1] - line2[0] * line1[1]);
        double cross_x = (line2[2] * line1[1] - line1[2] * line2[1]) / (line1[0] * line2[1] - line2[0] * line1[1]);

        return new double[] { cross_x, cross_y };
    }

    private static double GetDistance(Point2f point, double[] line)
    {
        return Math.Abs(line[0] * point.X + line[1] * point.Y + line[2]) /
               Math.Sqrt(Math.Pow(line[0], 2) + Math.Pow(line[1], 2));
    }

    private static int CheckDirection(Point2f point, double[] line)
    {
        double result = line[0] * point.X + line[1] * point.Y + line[2];
        if (result < 0) return -1;
        if (result > 0) return 1;
        return 0;
    }

    private static double GetRectangularArea(double[] Point0, double[] Point1, double[] Point2, double[] Point3)
    {
        double width = Math.Sqrt(Math.Pow(Point0[0] - Point1[0], 2) + Math.Pow(Point0[1] - Point1[1], 2));
        double height = Math.Sqrt(Math.Pow(Point1[0] - Point2[0], 2) + Math.Pow(Point1[1] - Point2[1], 2));
        return width * height;
    }

    public static Point2f[] GetConvexHull(List<Point2f> pointList)
    {
        Point2f[] hull = Cv2.ConvexHull(pointList);
        return hull;
    }

    public static List<double[]> MinEnclosingRectangle(Point2f[] hull)
    {
        double min_rectangular_area = double.PositiveInfinity;
        List<double[]> Point_muster = new List<double[]> { };

        for (int num = 0; num < hull.Length - 1; num++)
        {
            double[] Base_line = GetLinearEquation(hull[num], hull[num + 1]);
            double max_Ditance = 0;
            Point2f Point = hull[num + 1];
            Point2f Point_left = hull[num + 1];
            Point2f Point_right = hull[num + 1];

            foreach (Point2f i_point in hull)
            {
                if ((i_point.X == hull[num].X && i_point.Y == hull[num].Y) ||
                    (i_point.X == hull[num + 1].X && i_point.Y == hull[num + 1].Y))
                {
                    continue;
                }

                double distance = GetDistance(i_point, Base_line);

                if (distance > max_Ditance)
                {
                    max_Ditance = distance;
                    Point = i_point;
                }
            }

            double[] line_Parallel_Base = GetParallelLine(Point, Base_line);

            double[] line_Perpendicular_Base_right = null;
            double[] line_Perpendicular_Base_left = null;

            foreach (Point2f j_point in hull)
            {
                double[] line_Preparatory = GetpPerpendicular(j_point, Base_line);
                int point_right = 0;
                int point_left = 0;

                foreach (Point2f k_point in hull)
                {
                    if (k_point.X == j_point.X && k_point.Y == j_point.Y)
                    {
                        continue;
                    }

                    //添加点在直线上的条件
                    if (CheckDirection(k_point, line_Preparatory) <= 0)
                    {
                        point_left++;
                    }
                    else if (CheckDirection(k_point, line_Preparatory) >= 0)
                    {
                        point_right++;
                    }
                }

                if (point_right == hull.Length - 1)
                {
                    line_Perpendicular_Base_right = line_Preparatory;
                }

                if (point_left == hull.Length - 1)
                {
                    line_Perpendicular_Base_left = line_Preparatory;
                }
            }

            if (line_Perpendicular_Base_left == null || line_Perpendicular_Base_right == null)
            {
                double max_Ditance_left = 0;
                double distance_left = 0;
                double max_Distance_right = 0;
                double distance_right = 0;

                double[] line_inite_Preparatory = GetpPerpendicular(hull[num], Base_line);

                foreach (Point2f m_point in hull)
                {
                    if (CheckDirection(m_point, line_inite_Preparatory) <= 0)
                    {
                        distance_left = GetDistance(m_point, line_inite_Preparatory);
                        if (distance_left >= max_Ditance_left)
                        {
                            max_Ditance_left = distance_left;
                            Point_left = m_point;
                            line_Perpendicular_Base_left = GetpPerpendicular(Point_left, Base_line);
                        }
                    }
                    if (CheckDirection(m_point, line_inite_Preparatory) >= 0)
                    {
                        distance_right = GetDistance(m_point, line_inite_Preparatory);
                        if (distance_right >= max_Distance_right)
                        {
                            max_Distance_right = distance_right;
                            Point_right = m_point;
                            line_Perpendicular_Base_right = GetpPerpendicular(Point_right, Base_line);
                        }
                    }

                }

            }

            double[] Point0 = GetCrossPoint(Base_line, line_Perpendicular_Base_left);
            double[] Point1 = GetCrossPoint(Base_line, line_Perpendicular_Base_right);
            double[] Point2 = GetCrossPoint(line_Parallel_Base, line_Perpendicular_Base_right);
            double[] Point3 = GetCrossPoint(line_Parallel_Base, line_Perpendicular_Base_left);

            //List<double> temp0 = new List<double>();
            //List<double> temp1 = new List<double>();
            //List<double> temp2 = new List<double>();
            //List<double> temp3 = new List<double>();

            //double lat0 = Point0[0];
            //double lon0 = Point0[1];
            //double lat1 = Point1[0];
            //double lon1 = Point1[1];
            //double lat2 = Point2[0];
            //double lon2 = Point2[1];
            //double lat3 = Point3[0];
            //double lon3 = Point3[1];

            //temp0.Add(Math.Max(Math.Max(Math.Max(lat0, lat1), lat2), lat3));
            //temp0.Add(Math.Min(Math.Min(Math.Min(lon0, lon1), lon2), lon3));

            //temp1.Add(Math.Max(Math.Max(Math.Max(lat0, lat1), lat2), lat3));
            //temp1.Add(Math.Max(Math.Max(Math.Max(lon0, lon1), lon2), lon3));

            //temp2.Add(Math.Min(Math.Min(Math.Min(lat0, lat1), lat2), lat3));
            //temp2.Add(Math.Max(Math.Max(Math.Max(lon0, lon1), lon2), lon3));

            //temp3.Add(Math.Min(Math.Min(Math.Min(lat0, lat1), lat2), lat3));
            //temp3.Add(Math.Min(Math.Min(Math.Min(lon0, lon1), lon2), lon3));

            //Point0 = temp0.ToArray();
            //Point1 = temp1.ToArray();
            //Point2 = temp2.ToArray();
            //Point3 = temp3.ToArray();

            double rectangular_area = GetRectangularArea(Point0, Point1, Point2, Point3);

            if (rectangular_area < min_rectangular_area)
            {
                min_rectangular_area = rectangular_area;
                Point_muster = new List<double[]> {
                        Point0,
                        Point1,
                        Point2,
                        Point3
                    };
            }



            //double lat = Rectangular_Area_Search_Function.RMokatuoLat(Point0[0]);
            //double lon = Rectangular_Area_Search_Function.RMokatuoLon(Point0[1]);
            //Console.WriteLine("Point0:" + lat + "-" + lon);

            //double lat1 = Rectangular_Area_Search_Function.RMokatuoLat(Point1[0]);
            //double lon1 = Rectangular_Area_Search_Function.RMokatuoLon(Point1[1]);
            //Console.WriteLine("Point1:" + lat1 + "-" + lon1);

            //double lat2 = Rectangular_Area_Search_Function.RMokatuoLat(Point2[0]);
            //double lon2 = Rectangular_Area_Search_Function.RMokatuoLon(Point2[1]);
            //Console.WriteLine("Point2:" + lat2 + "-" + lon2);

            //double lat3 = Rectangular_Area_Search_Function.RMokatuoLat(Point3[0]);
            //double lon3 = Rectangular_Area_Search_Function.RMokatuoLon(Point3[1]);
            //Console.WriteLine("Point3:" + lat3 + "-" + lon3);
        }

        //foreach (var item in Point_muster)
        //{
        //    Console.WriteLine("Point_muster:" + item[0] + "-" + item[1]);
        //}
        min_area = GetRectangularArea(Point_muster[0], Point_muster[1], Point_muster[2], Point_muster[3]);

        //double width = Math.Sqrt(Math.Pow(Point_muster[0][0] - Point_muster[1][0], 2) + Math.Pow(Point_muster[0][1] - Point_muster[1][1], 2));
        //double height = Math.Sqrt(Math.Pow(Point_muster[1][0] - Point_muster[2][0], 2) + Math.Pow(Point_muster[1][1] - Point_muster[2][1], 2));
        //Console.WriteLine("width:" + width + "-" + "height:" + height);
        return Point_muster;
    }


    public static double min_area;
}