using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace bbiwarg.Utility
{

    class LineSegment2D
    {
        public Vector2D P1 { get; private set; }
        public Vector2D P2 { get; private set; }
        public Line2D Line { get; private set; }
        public float Length { get { return P1.getDistanceTo(P2); } }

        public LineSegment2D(Vector2D p1, Vector2D p2)
        {
            setPoints(p1, p2);
        }

        private void setPoints(Vector2D p1, Vector2D p2)
        {
            //endpoints
            P1 = p1;
            P2 = p2;

            //line
            Line = new Line2D(P1, P2-P1);
        }

        public float getParallelDistanceTo(LineSegment2D line)
        {
            if (Line.onSide(line.P1) != Line.onSide(line.P2)) return 0;

            Vector2D a1 = Line.projectToLine(line.P1);
            Vector2D a2 = Line.projectToLine(line.P2);

            float distanceA1 = a1.getDistanceTo(line.P1);
            float distanceA2 = a2.getDistanceTo(line.P2);
            return Math.Min(distanceA1, distanceA2);
        }

        public float getVerticalDistanceTo(LineSegment2D line)
        {
            Vector2D a1 = Line.projectToLine(line.P1);
            Vector2D a2 = Line.projectToLine(line.P2);

            if (P1.isInBox(a1, a2) || P2.isInBox(a1, a2)) return 0;

            float distanceP1A1 = P1.getDistanceTo(a1);
            float distanceP1A2 = P1.getDistanceTo(a2);
            float distanceP2A1 = P2.getDistanceTo(a1);
            float distanceP2A2 = P2.getDistanceTo(a2);
            return Math.Min(Math.Min(distanceP1A1, distanceP1A2), Math.Min(distanceP2A1, distanceP2A2));
        }

        public float getDistanceTo(Vector2D point)
        {
            // http://stackoverflow.com/questions/849211/shortest-distance-between-a-point-and-a-line-segment
            float l2 = (P1 - P2).dotProduct(P1 - P2);  // i.e. |w-v|^2 -  avoid a sqrt
            if (l2 == 0.0) 
                return point.getDistanceTo(P1);   // v == w case
            // Consider the line extending the segment, parameterized as v + t (w - v).
            // We find projection of point p onto the line. 
            // It falls where t = [(p-v) . (w-v)] / |w-v|^2
            float t = (point - P1).dotProduct(P2 - P1) / l2;
            if (t < 0.0) 
                return point.getDistanceTo(P1);       // Beyond the 'v' end of the segment
            else if (t > 1.0) 
                return point.getDistanceTo(P2);  // Beyond the 'w' end of the segment
            Vector2D projection = P1 + t * (P2 - P1);  // Projection falls on the segment
            return point.getDistanceTo(projection);
        }

        public override string ToString()
        {
            return (int)P1.X + "|" + (int)P1.Y + " --- " + (int)P2.X + "|" + (int)P2.Y;
        }
    }
}
