using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using EVE.EVEObjects;
using EVE.EVEShaders;
using EVE.EVEOctrees;

namespace EVE.EVEOctrees
{
    public class EVEOctNode
    {
        Vector3 mCenter;
        float mLength;

        EVEOctNodeCollection mNodes;
        EVEObjectCollection mObjects;

        public EVEOctNode(Vector3 newCenter, float newLength)
        {
            mCenter = newCenter;
            mLength = newLength;

            mNodes = new EVEOctNodeCollection();
            mObjects = new EVEObjectCollection();

            LoadGraphicContent();
        }

        private bool Contains(Vector3 checkPoint)
        {
            float halfLength = mLength / 2.0f;

            if (
                mCenter.X + halfLength > checkPoint.X &&
                mCenter.X - halfLength < checkPoint.X &&
                mCenter.Y + halfLength > checkPoint.Y &&
                mCenter.Y - halfLength < checkPoint.Y &&
                mCenter.Z + halfLength > checkPoint.Z &&
                mCenter.Z - halfLength < checkPoint.Z
            ) { return true; }

            return false;
        }

        public void DistributeObjects()
        {
            foreach (EVEObject obj in mObjects)
            {
                foreach (EVEOctNode node in mNodes)
                {
                    if (node.Contains(obj.Position))
                    {
                        node.AddObject(obj);
                    }
                }
            }
            mObjects.Clear();
        }

        public void AddObject(EVEObject newObject)
        {
            if (mNodes.Count != 0)
            {

                foreach (EVEOctNode node in mNodes)
                {
                    if (node.Contains(newObject.Position))
                    {
                        node.AddObject(newObject);
                    }
                }
            }
            else if (mObjects.Count >= EVEOctreeManager.MaxObjectsPerNode)
            {
                mObjects.Add(newObject);
                Split();
            }
            else
            {
                mObjects.Add(newObject);
            }
        }


        private void Split()
        {
            float offset = mLength / 4.0f;

            for (int x = -1; x <= 1; x += 2)
            {
                for (int y = -1; y <= 1; y += 2)
                {
                    for (int z = -1; z <= 1; z += 2)
                    {
                        EVEOctNode newNode = new EVEOctNode(
                            mCenter + new Vector3(x * offset, y * offset, z * offset), offset * 2.0f);
                        mNodes.Add(newNode);
                    }
                }
            }

            DistributeObjects();
        }

        VertexPositionColor[] points;
        int[] index;

        public void Draw(GraphicsDevice myDevice)
        {
            if (EVEOctreeManager.DrawOctNodes)
            {
                myDevice.RenderState.PointSize = EVEOctreeManager.LineWeight;
                myDevice.VertexDeclaration = EVEOctreeManager.VertexDeclaration;

                BasicEffect b = EVEShaderManager.GetShader("BasicEffect").Effect as BasicEffect;
                b.World = EVECameras.EVECameraManager.ActiveCamera.World;
                b.View = EVECameras.EVECameraManager.ActiveCamera.View;
                b.Projection = EVECameras.EVECameraManager.ActiveCamera.Projection;
                b.DiffuseColor = EVEOctreeManager.LineColor.ToVector3();

                b.Begin();
                foreach (EffectPass pass in b.CurrentTechnique.Passes)
                {
                    pass.Begin();
                    myDevice.DrawUserIndexedPrimitives<VertexPositionColor>(PrimitiveType.LineList, points, 0, 8, index, 0, 12);
                    pass.End();
                }
                b.End();
            }

            foreach (EVEOctNode node in mNodes)
            {
                node.Draw(myDevice);
            }
        }

        private void LoadGraphicContent()
        {
            float halfLength = mLength / 2.0f;
            points = new VertexPositionColor[8];

            points[0] = new VertexPositionColor(mCenter + new Vector3(-halfLength, -halfLength, -halfLength), EVEOctreeManager.LineColor);
            points[1] = new VertexPositionColor(mCenter + new Vector3(halfLength, -halfLength, -halfLength), EVEOctreeManager.LineColor);
            points[2] = new VertexPositionColor(mCenter + new Vector3(-halfLength, halfLength, -halfLength), EVEOctreeManager.LineColor);
            points[3] = new VertexPositionColor(mCenter + new Vector3(halfLength, halfLength, -halfLength), EVEOctreeManager.LineColor);
            points[4] = new VertexPositionColor(mCenter + new Vector3(-halfLength, -halfLength, halfLength), EVEOctreeManager.LineColor);
            points[5] = new VertexPositionColor(mCenter + new Vector3(halfLength, -halfLength, halfLength), EVEOctreeManager.LineColor);
            points[6] = new VertexPositionColor(mCenter + new Vector3(-halfLength, halfLength, halfLength), EVEOctreeManager.LineColor);
            points[7] = new VertexPositionColor(mCenter + new Vector3(halfLength, halfLength, halfLength), EVEOctreeManager.LineColor);

            int[] inds = { 0, 1, 0, 2, 1, 3, 2, 3, 4, 5, 4, 6, 5, 7, 6, 7, 0, 4, 1, 5, 2, 6, 3, 7 };

            index = inds;
        }
    }
}