using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using VisiMath.Collisions;

namespace VisiMath.Geometry
{
    /// <summary>
    /// Sphere with specified center and radius
    /// </summary>
    public class Sphere : BaseGeometry, IVolumetric, IConvexObject, Collisions.ISpatialIndex
    {
        /// <summary>
        /// Center of the sphere
        /// </summary>
        public Vector3 _Center;
        /// <summary>
        /// Radius of the sphere
        /// </summary>
        public float Radius;


        /// <summary>
        /// Standard constructor
        /// </summary>
        /// <param name="cen">Center of the sphere</param>
        /// <param name="rad">Radius</param>
        public Sphere(Vector3 cen, float rad)
        {
            _Center = cen;
            Radius = rad;
        }

        /// <summary>
        /// XML reader constructor
        /// </summary>
        /// <param name="reader">XML reader</param>
        public Sphere(System.Xml.XmlReader reader)
            : base()
        {
            ReadXml(reader);
        }


        #region IGeometry Members

        /// <summary>
        /// Preferred type of the vertex
        /// </summary>
        public override Type PreferredVertexType
        {
            get { return typeof(TexturedVertex); }
        }

        /// <summary>
        /// Sphere can't be directly rendered
        /// </summary>
        public override bool IsPrimitive
        {
            get { return false; }
        }

        /// <summary>
        /// If geometry object is container, it's made of several joined objects
        /// </summary>
        /// <returns>True if container</returns>
        public override bool IsContainer
        {
            get { return false; }
        }
        
        /// <summary>
        /// Center of the object
        /// </summary>
        public override Vector3 Center
        {
            get { return _Center; }
        }

        /// <summary>
        /// Returns closest distance from the specified point to object
        /// </summary>
        /// <param name="pos">position</param>
        /// <returns>Closest distance to the object</returns>
        public override float Distance(ref Vector3 pos)
        {
            float fromc = _Center.Distance(ref pos);

            if (fromc < Radius)
                return 0;
            return (fromc - Radius);
        }

        /// <summary>
        /// Returns closest distance to the ray
        /// </summary>
        /// <param name="ray">Ray to check distance to</param>
        /// <param name="frontOnly">Check only objects in front of the origin</param>
        /// <returns>Closest distance to the ray, if frontOnly and object is behind the ray, returns NaN</returns>
        public override float Distance(Ray ray, bool frontOnly = false)
        {
            float fromc = ray.Distance(_Center);
            if (fromc < Radius)
                return 0;
            return (fromc - Radius);
        }

        /// <summary>
        /// Returns closest distance from the generic geometric object
        /// </summary>
        /// <param name="obj">Object to check distance with</param>
        /// <returns>Closest distance to the object</returns>
        public override float Distance(IGeometry obj)
        {
            float generic = base.Distance(obj);
            if (generic < Radius)
                return 0;
            return (generic - Radius);
        }

        /// <summary>
        /// Returns closest distance to the line segment. Generic, from center, OVERRIDE!
        /// </summary>
        ///<param name="pos1">Start of the line</param>
        ///<param name="pos2">end of the line</param>
        /// <returns>Closest distance</returns>
        public override float DistanceLineSegment(ref Vector3 pos1, ref Vector3 pos2)
        {
            return MathTools.DistanceLineSegmentSphere(ref pos1, ref pos2, ref _Center, Radius);
        }

        /// <summary>
        /// Returns closest distance to the sphere
        /// </summary>
        /// <param name="center">center of the sphere</param>
        /// <param name="radius">radius of the sphere</param>
        /// <returns>Closest distance</returns>
        public override float DistanceSphere(ref Vector3 center, float radius)
        {
            return MathTools.DistanceTwoSpheres(ref center, radius, ref _Center, Radius);
        }

        /// <summary>
        /// Returns closest distance to the triangle.
        /// </summary>
        /// <param name="pos1">First corner</param>
        /// <param name="pos2">Second corner</param>
        /// <param name="pos3">Third corner</param>
        /// <returns>Distance</returns>
        public override float DistanceTriangle(ref Vector3 pos1, ref Vector3 pos2, ref Vector3 pos3)
        {
            return MathTools.DistanceSphereTriangle(ref _Center, Radius, ref pos1, ref pos2, ref pos3);
        }


        /// <summary>
        /// Checks if specified position is inside the object
        /// </summary>
        /// <param name="pos">Position to check</param>
        /// <returns>True if vector is inside the object</returns>
        public override bool ContainsPosition(Vector3 pos)
        {
            if (Distance(pos) == 0)
                return true;
            return false;
        }


        /// <summary>
        /// Check for intersection and fills intersection info if intersection is closer to the origin of the ray then value before
        /// </summary>
        /// <param name="inters">Data structure with intersection info</param>
        /// <param name="obj">Ray object</param>
        /// <returns>True if there was an intersection and it was closer then previous stored</returns>
        public override bool GetIntersectionInfo(Ray obj, ref IIntersectionInfo inters)
        {

            Vector3 dst = obj.Origin;
            dst.Substract(ref _Center);
            float B = dst.Dot(ref obj.Direction);
            float C = dst.Dot(ref dst) - Radius * Radius;
            float D = B * B - C;

            if (D < 0)
            {
                return false;
            }

            float dist  = -B - MathTools.Sqrt(D);
            if (dist > inters.Distance)
                return false;
            inters.Distance = dist;
            inters.IntersectionPoint = obj.TravelTo(inters.Distance);
            inters.GeoObject = this;
            return true;
        }

        /// <summary>
        /// Checks for intersection between object and moving sphere
        /// </summary>
        ///<param name="radius">Radius of the sphere</param>
        /// <param name="maxdistance">Maximal distance to travel</param>
        /// <param name="ray">Ray describing movement of the sphere</param>
        /// <returns>Distance to the intersection or -1 if no intersection exists</returns>
        public override float IntersectsMoving(float radius, Ray ray, float maxdistance)
        {
            
            float r;
            Vector3 target = Vector3.MoveInDirection(ref ray.Origin, ref ray.Direction, maxdistance);
            float dist = MathTools.DistanceVectorLineSegment(ref ray.Origin, ref target, ref _Center, out r);

            if ((dist - radius - Radius) > 0)
                return -1;
            return r * maxdistance;
        }



        #region Conversions

        /// <summary>
        /// Preferred primitive type for rendering - prefers indexed triangles
        /// </summary>
        public override PrimitiveTypes PreferredPrimitive
        {
            get { return PrimitiveTypes.IndexedTriangles; }
        }


        /// <summary>
        /// Convert object to list of points
        /// </summary>
        /// <returns>New geometry object containing point based definition of the object</returns>
        /// <param name="quality">
        /// Quality for parametric objects (0-1). Zero is simples possible, 1 maximal for the current dimension of the object. 
        /// If vertexlimit parameter is used, quality will be reduced to fullfill maximal vertex budget
        /// </param>
        /// <param name="VertexType">Required type of the vertex</param>
        /// <param name="vertexlimit">Maximal number of allowed vertices. If set to zero, there's no limit. 
        /// Used for fully parametric objects or to reduce number of the vertices in complicated objects.</param>
        public override Point3DList ToPoints(Type VertexType, float quality = 0, int vertexlimit = 0)
        {
            IGeometry mesh = ToIndexedMesh(VertexType, quality, vertexlimit);
            return mesh.ToPoints(VertexType, quality, vertexlimit);
        }

        /// <summary>
        /// Convert object to list of lines
        /// </summary>
        /// <returns>New geometry object containing edges of the object</returns>
        /// <param name="quality">
        /// Quality for parametric objects (0-1). Zero is simples possible, 1 maximal for the current dimension of the object. 
        /// If vertexlimit parameter is used, quality will be reduced to fullfill maximal vertex budget
        /// </param>
        /// <param name="VertexType">Required type of the vertex</param>
        /// <param name="vertexlimit">Maximal number of allowed vertices. If set to zero, there's no limit. 
        /// Used for fully parametric objects or to reduce number of the vertices in complicated objects.</param>
        public override LineList ToLines(Type VertexType, float quality = 0, int vertexlimit = 0)
        {
            IGeometry mesh = ToIndexedMesh(VertexType, quality, vertexlimit);
            return mesh.ToLines(VertexType, quality, vertexlimit);
        }

        /// <summary>
        /// Convert object to list of triangles. Valid only for surface based objects, otherwise returns null.
        /// </summary>
        /// <returns>New geometry object containing triangle faces of the object surface.</returns>
        /// <param name="quality">
        /// Quality for parametric objects (0-1). Zero is simples possible, 1 maximal for the current dimension of the object. 
        /// If vertexlimit parameter is used, quality will be reduced to fullfill maximal vertex budget
        /// </param>
        /// <param name="VertexType">Required type of the vertex</param>
        /// <param name="vertexlimit">Maximal number of allowed vertices. If set to zero, there's no limit. 
        /// Used for fully parametric objects or to reduce number of the vertices in complicated objects.</param>
        public override TriangleList ToTriangles(Type VertexType, float quality = 0, int vertexlimit = 0)
        {
            IGeometry mesh = ToIndexedMesh(VertexType, quality, vertexlimit);
            return mesh.ToTriangles(VertexType, quality, vertexlimit);
        }



        /// <summary>
        /// Convert object to indexed triangle mesh. Valid only for surface based objects, otherwise returns null.
        /// </summary>
        /// <returns>New geometry object containing triangle faces of the object surface</returns>
        /// <param name="quality">
        /// Quality for parametric objects (0-1). Zero is simples possible, 1 maximal for the current dimension of the object. 
        /// If vertexlimit parameter is used, quality will be reduced to fullfill maximal vertex budget
        /// </param>
        /// <param name="vertexlimit">Maximal number of allowed vertices. If set to zero, there's no limit. 
        /// Used for fully parametric objects or to reduce number of the vertices in complicated objects.</param>
        /// <param name="VertexType">Required type of the vertex</param>
        public override IndexedMesh ToIndexedMesh(Type VertexType, float quality = 0, int vertexlimit = 0)
        {

            bool doTexC = VertexFactory.HasTexCoord(VertexType);

            doTexC = true;

            int n = 2; //basic quality, vertexcount = (2*n+1)(4*n+4) +2 = 8n^2 + 12n + 6, for n = 4 vertexcount is 182, for 10 is 926, for 50 is 20606

            if (quality < 0)
                quality = 0;
            if (quality > 1)
                quality = 1;

            if (vertexlimit == 0)
            {
                n = (int)(20 * quality);
            }
            else
            {
                while (true)
                {
                    int vc = 8 * n * n + 12 * n + 6;
                    if (vc > vertexlimit)
                        break;
                    n++;
                }
            }
            if (n < 0)
                n = 0;


            int separators = 4 * n + 4;
            float segmentrad = MathTools.HalfPi / (n + 1);

            // vertices creation
            Vector3 pos = new Vector3();
            Vector3 norm;

            int vcount = separators * (2*n+1) + 2;

            int txadd = 0;

            if (doTexC)
            {
                vcount += 2 * n + 1; // left right seam
                vcount += 2 * separators; //poles

                txadd = 1;
            }


            IVertexArray Vertices = VertexFactory.CreateArray(VertexType, vcount);
            

            int vertindex = 0;


		    for (int i = -n; i <= n; i++)
            {
                float theta = segmentrad * i;
                float z = Radius * MathTools.Sin(theta);
                float rcostheta = Radius * MathTools.Cos(theta);
                
               for (int j = 0; j < separators + txadd; j++)
               {
                   {
                       float phi = segmentrad * j;

                       pos.Assign(rcostheta * MathTools.Sin(phi), rcostheta * MathTools.Cos(phi), z);
                       norm = pos.NormalizedCopy();

                       IVertex vt = VertexFactory.CreateNewVertex(VertexType);

                       if (doTexC)
                       {
                           float u, v;
                           v = 0.5f - i / ((float)2 * n + 1);
                           u = j / (float)separators;
                           vt.TexCoord2 = new Vector2(u, v);
                       }
                       pos.Add(ref _Center);
                       vt.Position = pos;
                       vt.Normal = norm;


                       Vertices[vertindex] = vt;
                   }

                   vertindex++;
			    }
            }

            int poleindex = vertindex;

		    pos.Assign(0, 0, Radius);
		    norm = pos.NormalizedCopy();
		    pos.Add(ref _Center);

            IVertex vt2 = VertexFactory.CreateNewVertex(VertexType);
            vt2.Position = pos;
            vt2.Normal = norm;

            int cnt = 1;
            if (doTexC)
                cnt = separators + 1;

            for (int i = 0; i < cnt; i++)
            {
                vt2.TexCoord2 = new Vector2((float)i/separators, 0);
                Vertices[vertindex] = vt2;
                vertindex++;
            }
            
            pos.Assign(0, 0, -Radius);
		    norm = pos.NormalizedCopy();
		    pos.Add(_Center);


            vt2 = VertexFactory.CreateNewVertex(VertexType);
            vt2.Position = pos;
            vt2.Normal = norm;

            for (int i = 0; i < cnt; i++)
            {
                vt2.TexCoord2 = new Vector2((float)i / separators, 1);
                Vertices[vertindex] = vt2;
                vertindex++;
            }

            int[] Indices = new int[2 * n * separators*6 + separators*6];

            int indiceindex = 0;


            if (!doTexC)
            {

                for (int i = 0; i < 2 * n; i++)
                {
                    for (int j = 0; j < separators; j++)
                    {
                        Indices[indiceindex + 0] = (i * separators + j);
                        Indices[indiceindex + 1] = (i * separators + j + separators);
                        Indices[indiceindex + 2] = (i * separators + (j + 1) % separators + separators);
                        Indices[indiceindex + 3] = (i * separators + (j + 1) % separators + separators);
                        Indices[indiceindex + 4] = (i * separators + (j + 1) % separators);
                        Indices[indiceindex + 5] = (i * separators + j);
                        indiceindex += 6;
                    }
                }
                for (int i = 0; i < separators; i++)
                {
                    int e = 2 * n;
                    Indices[indiceindex + 2] = (e * separators + i);
                    Indices[indiceindex + 1] = (e * separators + (i + 1) % separators);
                    Indices[indiceindex + 0] = (poleindex);


                    Indices[indiceindex + 3] = i;
                    Indices[indiceindex + 4] = ((i + 1) % separators);
                    Indices[indiceindex + 5] = (poleindex + 1);
                    indiceindex += 6;
                }
            }
            else
            {

                for (int i = 0; i < 2 * n; i++)
                {
                    for (int j = 0; j < separators; j++)
                    {
                        int isep = i * (separators + 1);
                        Indices[indiceindex + 0] = (isep + j);
                        Indices[indiceindex + 1] = (isep + j + (separators+1));
                        Indices[indiceindex + 2] = (isep + (j + 1) + (separators + 1));

                        Indices[indiceindex + 3] = (isep + (j + 1) + (separators + 1));
                        Indices[indiceindex + 4] = (isep + (j + 1));
                        Indices[indiceindex + 5] = (isep + j);
                        indiceindex += 6;
                    }
                }

                
                for (int i = 0; i < separators; i++)
                {
                    int e = 2 * n * (separators + 1);
                    
                    Indices[indiceindex + 2] = e + i;
                    Indices[indiceindex + 1] = e + i + 1;
                    Indices[indiceindex + 0] = (poleindex + i);

                    Vector2 txp = Vertices[Indices[indiceindex + 0]].TexCoord2;
                    Vector2 tx1 = Vertices[Indices[indiceindex + 1]].TexCoord2;
                    Vector2 tx2 = Vertices[Indices[indiceindex + 2]].TexCoord2;



                    Indices[indiceindex + 3] = i;
                    Indices[indiceindex + 4] = (i + 1);
                    Indices[indiceindex + 5] = (poleindex + separators + 1 + i);


                    Vector2 txpxx = Vertices[Indices[indiceindex + 5]].TexCoord2;
                    Vector2 tx1xx = Vertices[Indices[indiceindex + 3]].TexCoord2;
                    Vector2 tx2xx = Vertices[Indices[indiceindex + 4]].TexCoord2;


                    indiceindex += 6;

                }
            }

            return new IndexedMesh(Vertices, Indices);
        }

        #endregion

        /// <summary>
        /// Volume of the sphere
        /// </summary>
        protected float _volume;
        /// <summary>
        /// Volume of the object
        /// </summary>
        public float Volume 
        { 
            get
            {
                //note!!! if someone changed the radius, volem wouldn't be correct...
                //if (_volume == 0) 
                {
                    _volume = 4 * MathTools.Pi * Radius * Radius * Radius / 3;
                }
                return _volume;
            }
        }


        #endregion

        #region IConvexObject Members

        /// <summary>
        /// Gets supporting vertex in local coordinates
        /// </summary>
        /// <param name="vertex">Save vertex</param>
        /// <param name="direction">Direction vector</param>
        public void GetLocalSupportingVertex(ref Vector3 direction, out Vector3 vertex)
        {
            Vector3 vecnorm = direction;
            if (vecnorm.LengthSquared < MathTools.Epsilon)
                vecnorm.Assign(-1);
            vecnorm.Normalize();
            vertex = _Center;
            vertex.AddScaled(ref vecnorm, Radius);  //Check if this really works!
        }

        /// <summary>
        /// Returns implicit (math expression) or explicit (vertex based) type of convex object
        /// </summary>
        public ConvexObjectType BaseType { get { return ConvexObjectType.Implicit; } }

        /// <summary>
        /// Returns number of vertices, valid for explicit object types
        /// </summary>
        public int VertexCount { get { return 0; } }

        /// <summary>
        /// Returns vertex with specified index, valid for explicit object types
        /// </summary>
        /// <param name="index">index of vertex</param>
        /// <param name="vertex">Vector to save vertex</param>
        public void GetVertex(int index, out Vector3 vertex)
        {
            vertex = Vector3.Zero;
        }


        #endregion


        #region SpatialIndex
        /// <summary>
        /// Returns spatial index as box
        /// </summary>
        public AABox AsBox
        {
            get
            {
                Vector3 min = _Center; min.Substract(Radius, Radius, Radius);
                Vector3 max = _Center; max.Add(Radius, Radius, Radius);
                return new AABox(ref min, ref max);
            }
        }
        /// <summary>
        /// Returns spatial index as sphere
        /// </summary>
        public Sphere AsSphere
        {
            get
            {
                return this;
            }
        }
        /// <summary>
        /// returns spatial index as point (center)
        /// </summary>
        public Vector3 AsPoint { get { return Center; } }

        /// <summary>
        /// return true if index contains (intersects) another index
        /// </summary>
        /// <param name="index">Second spatial index</param>
        /// <returns>True if intersection exists</returns>
        public bool Contains(ref ISpatialIndex index)
        {
            AABox box = index as AABox;
            if (box != null)
                return Contains(box);
            Sphere sph = index as Sphere;
            if (sph != null)
                return Contains(sph);
            Point3D point = index as Point3D;
            if (point != null)
                return Contains(point);
            LineSegment3D line = index as LineSegment3D;
            if (line != null)
                return Contains(line);

            return Contains(index as IConvexObject);
        }

        /// <summary>
        /// Return true if index contains position
        /// </summary>
        /// <param name="index">Position index</param>
        /// <returns>True if intersection exists</returns>
        public bool Contains(Vector3 index)
        {
            return ContainsPosition(index);
        }
        /// <summary>
        /// Return true if index contains position
        /// </summary>
        /// <param name="index">Position index</param>
        /// <returns>True if intersection exists</returns>
        public bool Contains(ref Vector3 index)
        {
            return ContainsPosition(index);
        }
        /// <summary>
        /// Return true if index contains position
        /// </summary>
        /// <param name="index">Position index</param>
        /// <returns>True if intersection exists</returns>
        public bool Contains(Point3D index)
        {
            return ContainsPosition(index.GetPosition(0));
        }

        /// <summary>
        /// Return true if index contains line
        /// </summary>
        /// <param name="line1">Start of the line</param>
        /// <param name="line2">End of the line</param>
        /// <returns>True if intersection exists</returns>
        public bool ContainsLine(Vector3 line1, Vector3 line2)
        {
            return
                (MathTools.DistanceLineSegmentSphere(ref line1, ref line2, ref _Center, Radius) == 0);
        }
        /// <summary>
        /// Return true if index contains line
        /// </summary>
        /// <param name="line1">Start of the line</param>
        /// <param name="line2">End of the line</param>
        /// <returns>True if intersection exists</returns>
        public bool ContainsLine(ref Vector3 line1, ref Vector3 line2)
        {
            return
                (MathTools.DistanceLineSegmentSphere(ref line1, ref line2, ref _Center, Radius) == 0);
        }
        /// <summary>
        /// Return true if index contains line
        /// </summary>
        /// <param name="index">line index</param>
        /// <returns>True if intersection exists</returns>
        public bool Contains(LineSegment3D line)
        {
            return ContainsLine(line.GetPosition(0), line.GetPosition(1));
        }

        /// <summary>
        /// Return true if index contains sphere
        /// </summary>
        /// <param name="center">sphere center</param>
        /// <param name="radius">sphere radius</param>
        /// <returns>True if intersection exists</returns>
        public bool Contains(Vector3 center, float radius)
        {
            if (_Center.DistanceSquared(ref center) < (Radius + radius) * (Radius + radius))
                return true;
            return false;
        }
        /// <summary>
        /// Return true if index contains sphere
        /// </summary>
        /// <param name="center">sphere center</param>
        /// <param name="radius">sphere radius</param>
        /// <returns>True if intersection exists</returns>
        public bool Contains(ref Vector3 center, float radius)
        {
            if (_Center.DistanceSquared(ref center) < (Radius + radius) * (Radius + radius))
                return true;
            return false;
        }
        /// <summary>
        /// Return true if index contains sphere
        /// </summary>
        /// <param name="sphere">sphere index</param>
        /// <returns>True if intersection exists</returns>
        public bool Contains(Sphere sphere)
        {
            if (_Center.DistanceSquared(ref sphere._Center) < (Radius + sphere.Radius) * (Radius + sphere.Radius))
                return true;
            return false;
        }

        /// <summary>
        /// Return true if index contains axis alligned box
        /// </summary>
        /// <param name="max">Maximal corner</param>
        /// <param name="min">Minimal corner</param>
        /// <returns>True if intersection exists</returns>
        public bool ContainsBox(Vector3 min, Vector3 max)
        {
            return MathTools.Intersects(ref _Center, Radius, ref min, ref max);
        }
        /// <summary>
        /// Return true if index contains axis alligned box
        /// </summary>
        /// <param name="max">Maximal corner</param>
        /// <param name="min">Minimal corner</param>
        /// <returns>True if intersection exists</returns>
        public bool ContainsBox(ref Vector3 min, ref Vector3 max)
        {
            return MathTools.Intersects(ref _Center, Radius, ref min, ref max);
        }
        /// <summary>
        /// Return true if index contains axis alligned box
        /// </summary>
        /// <param name="box">AABox</param>
        /// <returns>True if intersection exists</returns>
        public bool Contains(AABox box)
        {
            return MathTools.Intersects(ref _Center, Radius, ref box.Minimum, ref box.Maximum);
        }

        /// <summary>
        /// Return true if index contains generic convex object
        /// </summary>
        /// <param name="convex">Convex object</param>
        /// <returns>True if intersection exists</returns>
        public bool Contains(IConvexObject convex)
        {
            int vertcount = convex.VertexCount;

            for (int i = 0; i < vertcount; i++)
            {
                Vector3 vec;
                convex.GetVertex(i, out vec);
                if (ContainsPosition(vec))
                    return true;
            }
            return false;
        }


        /// <summary>
        /// return true if index fully contains another index
        /// </summary>
        /// <param name="index">Second spatial index</param>
        /// <returns>True if index fully contains other object</returns>
        public bool ContainsFully(ref ISpatialIndex index)
        {
            AABox box = index as AABox;
            if (box != null)
                return ContainsFully(box);
            Sphere sph = index as Sphere;
            if (sph != null)
                return ContainsFully(sph);
            Point3D point = index as Point3D;
            if (point != null)
                return Contains(point);
            LineSegment3D line = index as LineSegment3D;
            if (line != null)
                return ContainsFully(line);

            return ContainsFully(index as IConvexObject);
        }

        /// <summary>
        /// Return true if index fully contains line
        /// </summary>
        /// <param name="line1">Start of the line</param>
        /// <param name="line2">End of the line</param>
        /// <returns>True if index fully contains other object</returns>
        public bool ContainsFullyLine(Vector3 line1, Vector3 line2)
        {
            return (ContainsPosition(line1) && ContainsPosition(line2));
        }
        /// <summary>
        /// Return true if index fully contains line
        /// </summary>
        /// <param name="line1">Start of the line</param>
        /// <param name="line2">End of the line</param>
        /// <returns>True if index fully contains other object</returns>
        public bool ContainsFullyLine(ref Vector3 line1, ref Vector3 line2)
        {
            return (ContainsPosition(line1) && ContainsPosition(line2));
        }
        /// <summary>
        /// Return true if index fully contains line
        /// </summary>
        /// <param name="line">Line segment</param>
        /// <returns>True if index fully contains other object</returns>
        public bool ContainsFully(LineSegment3D line)
        {
            return (ContainsPosition(line.GetPosition(0)) && ContainsPosition(line.GetPosition(1)));
        }

        /// <summary>
        /// Return true if index fully contains sphere
        /// </summary>
        /// <param name="center">sphere center</param>
        /// <param name="radius">sphere radius</param>
        /// <returns>True if index fully contains other object</returns>
        public bool ContainsFully(Vector3 center, float radius)
        {
            return ContainsFully(ref center, radius);
        }
        /// <summary>
        /// Return true if index fully contains sphere
        /// </summary>
        /// <param name="center">sphere center</param>
        /// <param name="radius">sphere radius</param>
        /// <returns>True if index fully contains other object</returns>
        public bool ContainsFully(ref Vector3 center, float radius)
        {
            if (Radius < radius)
                return false;
            float dst = _Center.DistanceSquared(ref center);
            if (dst <= (Radius - radius) * (Radius - radius))
                return true;
            return false;
        }
        /// <summary>
        /// Return true if index fully contains sphere
        /// </summary>
        /// <param name="sphere">sphere index</param>
        /// <returns>True if index fully contains other object</returns>
        public bool ContainsFully(Sphere sphere)
        {
            return ContainsFully(ref sphere._Center, sphere.Radius);
        }

        /// <summary>
        /// Return true if index fully contains axis alligned box
        /// </summary>
        /// <param name="max">Maximal corner</param>
        /// <param name="min">Minimal corner</param>
        /// <returns>True if index fully contains other object</returns>
        public bool ContainsFullyBox(Vector3 min, Vector3 max)
        {
            return (ContainsPosition(min) && ContainsPosition(max));
        }
        /// <summary>
        /// Return true if index fully contains axis alligned box
        /// </summary>
        /// <param name="max">Maximal corner</param>
        /// <param name="min">Minimal corner</param>
        /// <returns>True if index fully contains other object</returns>
        public bool ContainsFullyBox(ref Vector3 min, ref Vector3 max)
        {
            return (ContainsPosition(min) && ContainsPosition(max));
        }
        /// <summary>
        /// Return true if index fully contains axis alligned box
        /// </summary>
        /// <param name="box">AABox</param>
        /// <returns>True if index fully contains other object</returns>
        public bool ContainsFully(AABox box)
        {
            return (ContainsPosition(box.Minimum) && ContainsPosition(box.Maximum));
        }

        /// <summary>
        /// Return true if index FULLY contains generic convex object
        /// </summary>
        /// <param name="convex">Convex object</param>
        /// <returns>True if object is fully contained in index</returns>
        public bool ContainsFully(IConvexObject convex)
        {
            int vertcount = convex.VertexCount;

            for (int i = 0; i < vertcount; i++)
            {
                Vector3 vec;
                convex.GetVertex(i, out vec);
                if (!ContainsPosition(vec))
                    return false;
            }
            return true;
        }




        #endregion

        /// <summary>
        /// Deserializes object from XML
        /// </summary>
        /// <param name="reader">XML reader</param>
        public override void ReadXml(System.Xml.XmlReader reader)
        {
            if (reader["type"] != this.GetType().Name)
                throw new ArgumentException("IGeometry:ReadXML - wrong xml node type!");

            while (reader.Read())
            {
                if (reader.IsStartElement())
                {
                    if (reader.Name == "Center")
                    {
                        _Center.ReadXml(reader);
                    }
                    else if (reader.Name == "Radius")
                    {
                        Radius = XMLHelper.ReadAsFloat(reader);
                    }
                }
                else break;
            }
        }

        /// <summary>
        /// Serializes object to XML
        /// </summary>
        /// <param name="writer">XML writer to save values</param>
        public override void WriteXml(System.Xml.XmlWriter writer)
        {
            writer.WriteAttributeString("type", this.GetType().Name);
            _Center.WriteXml(writer, "Center");
            XMLHelper.WriteFloat(writer, "Radius", Radius);
        }

        /// <summary>
        /// Returns copy of the geometric data
        /// </summary>
        /// <returns>Full copy of the geometry</returns>
        public override IGeometry GetCopy()
        {
            return new Sphere(this._Center, this.Radius);
        }


    }
}
