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

//namespace McGuffinsOfflineNetworkWorkshop {
    public class MAlignedRect2 {
        private bool is_empty = true;

        // diagonally opposite corners
        private MVector2 p0 = new MVector2(0, 0);
        private MVector2 p1 = new MVector2(0, 0);

        public MAlignedRect2() {
        }
        public MAlignedRect2(MVector2 min, MVector2 max) {
            MLogger.Assert(min.x <= max.x, "kbehbhaq48");
            MLogger.Assert(min.y <= max.y, "u0u0um3hma");

            p0.copy(min);
            p1.copy(max);
            is_empty = false;
        }

        public bool isEmpty() { return is_empty; }
        public void clear() { is_empty = true; }

        public MVector2 getMin() { return p0; }
        public MVector2 getMax() { return p1; }
        public MVector2 getDiagonal() { return p1 - p0; }
        public MVector2 getCenter() {
            return MVector2.average(p0, p1);
        }
        // Enlarge the rectangle as necessary to contain the given point
        public void bound(MVector2 p) {
            if (is_empty) {
                p0.copy(p);
                p1.copy(p);
                is_empty = false;
            }
            else {
                if (p.x < p0.x)
                    p0.x = p.x;
                else if (p.x > p1.x)
                    p1.x = p.x;

                if (p.y < p0.y)
                    p0.y = p.y;
                else if (p.y > p1.y)
                    p1.y = p.y;
            }
        }
        // returns a uniformly scaled version of the rectangle with the same center
        public MAlignedRect2 enlarged( float fraction /* 1.5 means enlarge by 50% */ ) {
            MAlignedRect2 r = new MAlignedRect2();
            if ( is_empty) return r;
            MVector2 c = getCenter();
            MVector2 d = getDiagonal();
            float new_half_width = d.x * fraction / 2;
            float new_half_height = d.y * fraction / 2;
            r.bound( new MVector2( c.x-new_half_width, c.y-new_half_height ) );
            r.bound( new MVector2( c.x+new_half_width, c.y+new_half_height ) );
            return r;
        }
        public bool contains(MVector2 p) {
            return ! is_empty
                && p0.x <= p.x && p.x <= p1.x
                && p0.y <= p.y && p.y <= p1.y;
        }
        public bool contains(MAlignedRect2 r) {
            if (is_empty) return false;
            if (r.is_empty) return true;
            return p0.x <= r.p0.x && r.p1.x <= p1.x
                && p0.y <= r.p0.y && r.p1.y <= p1.y;
        }
        public bool intersects(MAlignedRect2 r) {
            return ! is_empty && ! r.is_empty
                && r.p0.x <= p1.x && r.p0.y <= p1.y
                && r.p1.x >= p0.x && r.p1.y >= p0.y;
        }
        public bool intersectsLineSegment( MVector2 a, MVector2 b ) {
            MAlignedRect2 lineSegmentRect = new MAlignedRect2();
            lineSegmentRect.bound(a);
            lineSegmentRect.bound(b);
            if (!intersects(lineSegmentRect))
                return false;
            if (contains(a) || contains(b))
                return true;

            MRay2 ray = new MRay2(a, b - a); // note that the direction vector is not unit-length
            // candidate intersection
            float candidateDistance;
            MVector2 candidatePoint;

            if (ray.direction.x != 0) {
                candidateDistance = -(ray.origin.x - p0.x) / ray.direction.x;
                if (
                    // test if the intersection is between a and b
                    candidateDistance >= 0
                    && candidateDistance <= 1.0f
                ) {
                    candidatePoint = ray.point(candidateDistance);
                    if (p0.y <= candidatePoint.y && candidatePoint.y <= p1.y) {
                        return true;
                    }
                }
                candidateDistance = -(ray.origin.x - p1.x) / ray.direction.x;
                if (
                    // test if the intersection is between a and b
                    candidateDistance >= 0
                    && candidateDistance <= 1.0f
                ) {
                    candidatePoint = ray.point(candidateDistance);
                    if (p0.y <= candidatePoint.y && candidatePoint.y <= p1.y) {
                        return true;
                    }
                }
            }
            if (ray.direction.y != 0) {
                candidateDistance = -(ray.origin.y - p0.y) / ray.direction.y;
                if (
                    // test if the intersection is between a and b
                    candidateDistance >= 0
                    && candidateDistance <= 1.0f
                ) {
                    candidatePoint = ray.point(candidateDistance);
                    if (p0.x <= candidatePoint.x && candidatePoint.x <= p1.x) {
                        return true;
                    }
                }
                candidateDistance = -(ray.origin.y - p1.y) / ray.direction.y;
                if (
                    // test if the intersection is between a and b
                    candidateDistance >= 0
                    && candidateDistance <= 1.0f
                ) {
                    candidatePoint = ray.point(candidateDistance);
                    if (p0.x <= candidatePoint.x && candidatePoint.x <= p1.x) {
                        return true;
                    }
                }
            }
            return false;
        }
        public bool intersects(
            MRay2 ray, // input
            out MVector2 intersection // output
        ) {
            intersection = new MVector2();
            bool intersectionDetected = false;
            float distance = 0;

            // candidate intersection
            float candidateDistance;
            MVector2 candidatePoint;

            if (ray.direction.x != 0) {
                candidateDistance = -(ray.origin.x - p0.x) / ray.direction.x;
                if (
                    candidateDistance >= 0
                    && (!intersectionDetected || candidateDistance < distance)
                ) {
                    candidatePoint = ray.point(candidateDistance);
                    if (p0.y <= candidatePoint.y && candidatePoint.y <= p1.y) {
                        distance = candidateDistance;
                        intersection.copy(candidatePoint);
                        intersectionDetected = true;
                    }
                }
                candidateDistance = -(ray.origin.x - p1.x) / ray.direction.x;
                if (
                    candidateDistance >= 0
                    && (!intersectionDetected || candidateDistance < distance)
                ) {
                    candidatePoint = ray.point(candidateDistance);
                    if (p0.y <= candidatePoint.y && candidatePoint.y <= p1.y) {
                        distance = candidateDistance;
                        intersection.copy(candidatePoint);
                        intersectionDetected = true;
                    }
                }
            }
            if (ray.direction.y != 0) {
                candidateDistance = -(ray.origin.y - p0.y) / ray.direction.y;
                if (
                    candidateDistance >= 0
                    && (!intersectionDetected || candidateDistance < distance)
                ) {
                    candidatePoint = ray.point(candidateDistance);
                    if (p0.x <= candidatePoint.x && candidatePoint.x <= p1.x) {
                        distance = candidateDistance;
                        intersection.copy(candidatePoint);
                        intersectionDetected = true;
                    }
                }
                candidateDistance = -(ray.origin.y - p1.y) / ray.direction.y;
                if (
                    candidateDistance >= 0
                    && (!intersectionDetected || candidateDistance < distance)
                ) {
                    candidatePoint = ray.point(candidateDistance);
                    if (p0.x <= candidatePoint.x && candidatePoint.x <= p1.x) {
                        distance = candidateDistance;
                        intersection.copy(candidatePoint);
                        intersectionDetected = true;
                    }
                }
            }
            return intersectionDetected;
        }
    }
//}

