using System;
using System.Collections.Generic;

namespace Oni.Akira
{
    internal class Room
    {
        private Polygon floorPolygon;
        private RoomBspNode bspTree;
        private BoundingBox boundingBox;
        private RoomGrid grid;
        private Plane floorPlane;
        private float height;
        private readonly List<RoomAdjacency> adjacencies = new List<RoomAdjacency>();

        public BoundingBox BoundingBox
        {
            get { return boundingBox; }
            set { boundingBox = value; }
        }

        public RoomBspNode BspTree
        {
            get { return bspTree; }
            set { bspTree = value; }
        }

        public RoomGrid Grid
        {
            get { return grid; }
            set { grid = value; }
        }

        public Polygon FloorPolygon
        {
            get { return floorPolygon; }
            set { floorPolygon = value; }
        }

        public Plane FloorPlane
        {
            get { return floorPlane; }
            set { floorPlane = value; }
        }

        public bool IsStairs => floorPlane.Normal.Y < 0.999f;

        public float Height
        {
            get { return height; }
            set { height = value; }
        }

        public List<RoomAdjacency> Ajacencies => adjacencies;

        public bool Contains(Vector3 point)
        {
            if (!boundingBox.Contains(point))
                return false;

            bool front = false;
            RoomBspNode node = bspTree;

            while (node != null)
            {
                front = (node.Plane.DotCoordinate(point) >= MathHelper.Eps);
                node = front ? node.FrontChild : node.BackChild;
            }

            return !front;
        }

        public bool Intersect(BoundingBox bbox)
        {
            if (!boundingBox.Intersects(bbox))
                return false;

            bool front = false;
            RoomBspNode node = bspTree;

            while (node != null)
            {
                int intersects = node.Plane.Intersects(bbox);

                if (intersects == 0)
                    return true;

                front = intersects > 0;
                node = front ? node.FrontChild : node.BackChild;
            }

            return !front;
        }

        public List<Vector3[]> GetFloorPolygons()
        {
            var polys = new List<Vector3[]>();

            if (floorPolygon != null)
            {
                polys.Add(floorPolygon.Points.ToArray());
                return polys;
            }

            var min = new Vector2(boundingBox.Min.X, boundingBox.Min.Z);
            var max = new Vector2(boundingBox.Max.X, boundingBox.Max.Z);

            var root = new Polygon2(new[]
            {
                new Vector2(min.X, min.Y),
                new Vector2(max.X, min.Y),
                new Vector2(max.X, max.Y),
                new Vector2(min.X, max.Y)
            });

            var cutter = new Polygon2Clipper(bspTree);

            foreach (Polygon2 polygon in cutter.Clip(root))
            {
                var points = new Vector3[polygon.Length];

                for (int i = 0; i < points.Length; i++)
                {
                    var point = polygon[i];

                    points[i].X = point.X;
                    points[i].Y = (-floorPlane.D - floorPlane.Normal.Z * point.Y - floorPlane.Normal.X * point.X) / floorPlane.Normal.Y;
                    points[i].Z = point.Y;
                }

                Array.Reverse(points);

                polys.Add(points);
            }

            return polys;
        }
    }
}
