﻿using System;
using System.Collections.Generic;
using o0.Geometry2D.Float;

namespace ZIM
{
    // 屏幕识别使用，固定的顶点顺序： 左下，右下，左上，右上，严格遵守
    public class OrdinalQuadrilateral : Geometry<OrdinalQuadrilateral>
    {
        public Vector A;
        public Vector B;
        public Vector C;
        public Vector D;

        public override int Count => 4;

        public OrdinalQuadrilateral(Vector a, Vector b, Vector c, Vector d)
        {
            this.A = a;
            this.B = b;
            this.C = c;
            this.D = d;
        }

        public OrdinalQuadrilateral(IEnumerable<Vector> enumable)
        {
            var e = enumable.GetEnumerator();
            this.A = e.MoveNext() ? e.Current : default;
            this.B = e.MoveNext() ? e.Current : default;
            this.C = e.MoveNext() ? e.Current : default;
            this.D = e.MoveNext() ? e.Current : default;
        }

        public static OrdinalQuadrilateral Identity { get; } = new OrdinalQuadrilateral(new Vector(0, 0), new Vector(1, 0), new Vector(0, 1), new Vector(1, 1));

        public override Vector this[int index]
        {
            get
            {
                switch (index % Count)
                {
                    default:
                    case 0:
                        return A;
                    case 1:
                        return B;
                    case 2:
                        return C;
                    case 3:
                        return D;
                }
            }
            set
            {
                switch (index % Count)
                {
                    default:
                    case 0:
                        A = value;
                        break;
                    case 1:
                        B = value;
                        break;
                    case 2:
                        C = value;
                        break;
                    case 3:
                        D = value;
                        break;
                }
            }
        }

        public override string ToString()
        {
            return $"[{A}, {B}, {C}, {D}]";
        }

        // AABB包围盒，返回矩形左下角和右上角的坐标
        public (Vector, Vector) AABBRect()
        {
            float minX = this[0].x;
            float maxX = this[0].x;
            float minY = this[0].y;
            float maxY = this[0].y;

            for (int i = 1; i < Count; i++)
            {
                minX = Math.Min(minX, this[i].x);
                maxX = Math.Max(maxX, this[i].x);
                minY = Math.Min(minY, this[i].y);
                maxY = Math.Max(maxY, this[i].y);
            }

            return (new Vector(minX, minY), new Vector(maxX, maxY));
        }

        public Vector InterpolationFactors(Vector p)
        {
            float u, v;
            float _a = (C.x - D.x + B.x - A.x) * (A.y - C.y) - (C.y - D.y + B.y - A.y) * (A.x - C.x);
            float _b = (D.x - C.x) * (A.y - C.y) - (D.y - C.y) * (A.x - C.x) + (p.x - C.x) * (C.y - D.y + B.y - A.y) - (p.y - C.y) * (C.x - D.x + B.x - A.x);
            float _c = (p.x - C.x) * (D.y - C.y) - (p.y - C.y) * (D.x - C.x);
            if (_a < float.Epsilon)
            {
                v = -_c / _b;
            }
            else
            {
                float delta = _b * _b - 4 * _a * _c;

                if (delta < 0)
                {
                    return default;
                    throw new Exception("Delta is smaller than zero.");
                }

                float sqrtDelta = (float)Math.Sqrt(delta);

                v = (-_b + sqrtDelta) / (2 * _a);
                if (v < 0 || v > 1)
                    v = (-_b - sqrtDelta) / (2 * _a);
            }
            u = ((p.x - C.x) - (A.x - C.x) * v) / ((D.x - C.x) + (C.x - D.x + B.x - A.x) * v);

            return new Vector(u, 1 - v);
        }

        public static OrdinalQuadrilateral Fit(IEnumerable<Vector> pixels, Vector textureSize)
        {
            Vector[] vertex = new Vector[4] { new Vector(0, 0), new Vector(textureSize.x, 0), new Vector(textureSize.y, 0), textureSize };
            Vector[] dir = new Vector[4] { new Vector(1, 1).Normalized, new Vector(-1, 1).Normalized, new Vector(1, -1).Normalized, new Vector(-1, -1).Normalized };
            (float, Vector)[] min = new (float, Vector)[4] { (float.MaxValue, default), (float.MaxValue, default), (float.MaxValue, default), (float.MaxValue, default) };

            foreach (var i in pixels)
            {
                for (int j = 0; j < 4; j++)
                {
                    var len = (i - vertex[j]).Dot(dir[j]);
                    if (len < min[j].Item1)
                        min[j] = (len, i);
                }
            }

            return new OrdinalQuadrilateral(min[0].Item2, min[1].Item2, min[2].Item2, min[3].Item2);
        }
    }
}