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

namespace VisiMath.Geometry
{
    /// <summary>
    /// One simple point in space
    /// </summary>
    public class Point3D : Geometry, Collisions.ISpatialIndex
    {

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

        /// <summary>
        /// Constructs geopoint from vertex
        /// </summary>
        /// <param name="vertex">vertex</param>
        public Point3D(IVertex vertex)
            : base(typeof(Vertex), 1)
        {
            Vertices[0] = vertex;
        }

        /// <summary>
        /// Returns copy of the geometric data
        /// </summary>
        /// <returns>Full copy of the geometry</returns>
        public override IGeometry GetCopy()
        {
            return new Point3D(Vertices[0]);
        }


        /// <summary>
        /// Constructs geopoint from position data (just position vertex!)
        /// </summary>
        /// <param name="pos"></param>
        public Point3D(Vector3 pos)
            : base(typeof(PositionVertex), 1)
        {
            PositionVertex ver = new PositionVertex(pos);
            Vertices[0] = ver;
        }

        /// <summary>
        /// Is object geometric renderable primitive from enumaration PrimitiveTypes?
        /// </summary>
        public override bool IsPrimitive
        {
            get { return true; }
        }


        /// <summary>
        /// Center of the object
        /// </summary>
        public override Vector3 Center
        {
            get { return Vertices[0].Position; }
        }

        /// <summary>
        /// Preferred primitive type for rendering
        /// </summary>
        public override PrimitiveTypes PreferredPrimitive
        {
            get { return PrimitiveTypes.PointList; }
        }

        /// <summary>
        /// Converts object to new geometry object with specified primitive type and optionally with quality parameters.
        /// Point can be converted only to point or pointlist. Rest types return null.
        /// </summary>
        /// <param name="primType">Specified type of the primitive</param>
        /// <param name="VertexType">Required type of the vertex</param>
        /// <param name="quality">Required quality</param>
        /// <param name="vertexlimit">Vertex limit</param>
        /// <returns>New object of specified type</returns>
        public override IGeometry ConvertToPrimitive(Type VertexType, PrimitiveTypes primType, float quality = 0, int vertexlimit = 0)
        {
            switch (primType)
            {
                case PrimitiveTypes.Point:
                    return this;
                case PrimitiveTypes.PointList:
                    return new Point3DList(VertexType, this.Vertices);
                default:
                    return null;
            }
        }

        /// <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>
        /// Returns new point list with this point. Parameters are ignored.
        /// </summary>
        /// <returns>New pointlist</returns>
        public override Point3DList ToPoints(Type VertexType, float quality = 0, int vertexlimit = 0)
        {
            return new Point3DList(VertexType, this.Vertices);
        }

        /// <summary>
        /// Point can't be converted to lines - returns null
        /// </summary>
        public override LineList ToLines(Type VertexType, float quality = 0, int vertexlimit = 0)
        {
            return null;
        }

        /// <summary>
        /// Point can't be converted to triangles - returns null
        /// </summary>
        public override TriangleList ToTriangles(Type VertexType, float quality = 0, int vertexlimit = 0)
        {
            return null;
        }

        /// <summary>
        /// Point can't be converted to mesh - returns null
        /// </summary>
        public override IndexedMesh ToIndexedMesh(Type VertexType, float quality = 0, int vertexlimit = 0)
        {
            return null;
        }


        /// <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)
        {
            return obj.Distance(this.Vertices[0].Position);
        }

        /// <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)
        {
            Vector3 vec = Vertices[0].Position;
            return MathTools.DistanceVectorLineSegment(ref pos1, ref pos2, ref vec);
        }
        
        /// <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)
        {
            Vector3 pos = Vertices[0].Position;
            return MathTools.DistanceVectorSphere(ref center, radius, ref pos);
        }

        /// <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)
        {
            Vector3 pos = Vertices[0].Position;
            return MathTools.DistanceVectorTriangle(ref pos1, ref pos2, ref pos3, ref pos);
        }

        /// <summary>
        /// Returns true if point is inside object
        /// </summary>
        /// <param name="obj">Object to intersect with</param>
        /// <returns>true if point is inside object</returns>
        public override bool Intersects(IGeometry obj)
        {
            return obj.ContainsPosition(this.Vertices[0].Position);
        }

        /// <summary>
        /// Checks if specified position is inside the object - for point only if the point has same coordinates
        /// </summary>
        /// <param name="pos">Position to check</param>
        /// <returns>True if vector is inside the object</returns>
        public override bool ContainsPosition(Vector3 pos)
        {
            if (pos == this.Vertices[0].Position)
                return true;
            return false;
        }

        /// <summary>
        /// Returns closest point of the intersection between object and ray.
        /// For point it's either it's position or invalid vector
        /// </summary>
        /// <param name="obj">Object to check</param>
        /// <returns>Point of intersection or invalid Vector3 (with double.NaN as values)</returns>
        public override Vector3 IntersectionPoint(IGeometry obj)
        {
            if (obj.ContainsPosition(this.Vertices[0].Position))
                return this.Vertices[0].Position;
            Vector3 rval = new Vector3(float.NaN);
            return rval;
        }

        public override bool GetIntersectionInfo(Ray obj, ref IIntersectionInfo inters)
        {
            Vector3 me = this.Vertices[0].Position;
            float dst = obj.Distance(me);
            if (dst == 0)
            {
                float dst2 = obj.Origin.Distance(ref me);

                if (dst2 < inters.Distance)
                {
                    inters.Distance = dst2;
                    inters.GeoObject = this as IGeometry;
                    inters.IntersectionPoint = me;
                    return true;
                }
            }
            return false;
        }

        /// <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 t;
            Vector3 targ = Vector3.MoveInDirection(ref ray.Origin, ref ray.Direction, maxdistance);
            Vector3 me = Vertices[0].Position;
            float dist = MathTools.DistanceVectorLineSegment(ref ray.Origin, ref targ, ref me, out t);

            if (dist < radius)
                return -1;
            return t * maxdistance;
        }


        #region SpatialIndex
        /// <summary>
        /// Returns spatial index as box
        /// </summary>
        public AABox AsBox
        {
            get
            {
                return new AABox(this.GetPosition(0), this.GetPosition(0));
            }
        }
        /// <summary>
        /// Returns spatial index as sphere
        /// </summary>
        public Sphere AsSphere
        {
            get
            {
                return new Sphere(this.GetPosition(0), 0);
            }
        }
        /// <summary>
        /// returns spatial index as point (center)
        /// </summary>
        public Vector3 AsPoint { get { return this.GetPosition(0); } }

        /// <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 (GetPosition(0) == 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 (GetPosition(0) == 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 (GetPosition(0) == 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.PointOnLineSegment(GetPosition(0), line1, line2);
        }
        /// <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)
        {
            Vector3 me = GetPosition(0);

            return MathTools.PointOnLineSegment(ref me, ref line1, ref line2);
        }
        /// <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 (GetPosition(0).DistanceSquared(ref center) <= 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 (GetPosition(0).DistanceSquared(ref center) <= 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)
        {
            return sphere.ContainsPosition(GetPosition(0));
        }

        /// <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 ContainsBox(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)
        {
            Vector3 me = GetPosition(0);
            for (int i = 0; i < 3; i++)
            {
                if ((me[i] < min[i])||(me[i] > max[i]))
                    return false;
            }
            return true;
        }
        /// <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 box.Contains(this.GetPosition(0));
        }

        /// <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)
        {
            return convex.ContainsPosition(this.GetPosition(0));
        }


        /// <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)
        {
            Point3D point = index as Point3D;
            if (point != null)
                return (GetPosition(0) == point.GetPosition(0));
            return false;
        }

        /// <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 false;
        }
        /// <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 false;
        }
        /// <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 false;
        }

        /// <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)
        {
            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 false;
        }

        /// <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 false;
        }
        /// <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 false;
        }
        /// <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 false;
        }

        /// <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)
        {
            return false;
        }



        #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)
        {
            vertex = Vertices[0].Position;
        }

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

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

        /// <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 = Vertices[0].Position;
        }


        #endregion


        #endregion
    

} // Geopoint


    /// <summary>
    /// List of points
    /// </summary>
    public class Point3DList : Geometry, IGeoContainer<Point3D>
    {

        /// <summary>
        /// Returns copy of the geometric data
        /// </summary>
        /// <returns>Full copy of the geometry</returns>
        public override IGeometry GetCopy()
        {
            return new Point3DList(Vertices.GetCopy());
        }


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

        /// <summary>
        /// Construct point list with specified number of vertices
        /// </summary>
        /// <param name="count">Number of points</param>
        public Point3DList(int count) : base(count)
        {
        }

        /// <summary>
        /// Construct pointlist from vertex array
        /// </summary>
        /// <param name="source">source vertex array</param>
        public Point3DList(IVertexArray source) : base(source)
        {
        }

        /// <summary>
        /// Construct pointlist from vertex array with different vertextype
        /// </summary>
        /// <param name="source">source vertex array</param>
        /// <param name="VertexType">Required type of the vertex</param>
        public Point3DList(Type VertexType, IVertexArray source)
            : base(VertexType, source)
        {
        }

        /// <summary>
        /// Construct pointlist with specified vertex type and number of vertices
        /// </summary>
        /// <param name="count">number of vertices</param>
        /// <param name="VertexType">Required type of the vertex</param>
        public Point3DList(Type VertexType, int count)
            : base(VertexType, count)
        {
        }


        /// <summary>
        /// PointList is primitive
        /// </summary>
        public override bool IsPrimitive
        {
            get { return true; }
        }

        /// <summary>
        /// Pointlist prefers conversion to pointlist (no other is possible)
        /// </summary>
        public override PrimitiveTypes PreferredPrimitive
        {
            get { return PrimitiveTypes.PointList; }
        }

        /// <summary>
        /// Point list can work as a container for points
        /// </summary>
        public override bool IsContainer
        {
            get { return true; }
        }


        /// <summary>
        /// No conversion needed, returns pointer to self unless diferent vertextype is requested
        /// </summary>
        public override Point3DList ToPoints(Type VertexType, float quality = 0, int vertexlimit = 0)
        {
            if (VertexType == PreferredVertexType)
                return this;
            return new Point3DList(VertexType, this.Vertices);
        }

        /// <summary>
        /// Point list can't be converted to the line list, returns null
        /// </summary>
        public override LineList ToLines(Type VertexType, float quality = 0, int vertexlimit = 0)
        {
            return null;
        }

        /// <summary>
        /// Point list can't be converted to the triangle list, returns null
        /// </summary>
        public override TriangleList ToTriangles(Type VertexType, float quality = 0, int vertexlimit = 0)
        {
            return null;
        }

        /// <summary>
        /// Point list can't be converted to the mesh, returns null
        /// </summary>
        public override IndexedMesh ToIndexedMesh(Type VertexType, float quality = 0, int vertexlimit = 0)
        {
            return null;
        }

        /// <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 dst = float.MaxValue;
            
            foreach (IVertex iv in Vertices)
            {
                float newdst = obj.Distance(iv.Position);
                if (newdst < dst)
                    dst = newdst;
            }
            return dst;
        }

        /// <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 dst = float.MaxValue;
            foreach (IVertex ver in Vertices)
            {
                float newdst = ray.Distance(ver.Position, frontOnly);
                if (newdst != float.NaN)
                {
                    if (newdst < dst)
                    {
                        dst = newdst;
                    }
                }
            }
            if (dst == float.MaxValue)
                return float.NaN;
            return dst;
        }

        /// <summary>
        /// For generic geometry returns distance from the center of the object
        /// </summary>
        /// <param name="pos"></param>
        /// <returns>Closest distance to any point</returns>
        public override float Distance(ref Vector3 pos)
        {
            float dst = float.MaxValue;
            foreach (IVertex ver in Vertices)
            {
                float newdst = ver.Position.DistanceSquared(ref pos);
                if (newdst < dst)
                    dst = newdst;               
            }
            if (dst == float.MaxValue)
                return float.MaxValue;
            return MathTools.Sqrt(dst);
        }

        /// <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)
        {
            float dst = float.MaxValue;
            foreach (IVertex ver in Vertices)
            {
                float newdst = MathTools.DistanceVectorLineSegment(pos1, pos2, ver.Position);
                if (newdst < dst)
                    dst = newdst;
            }
            return dst;
        }
        /// <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)
        {
            float dst = float.MaxValue;
            foreach (IVertex ver in Vertices)
            {
                Vector3 pos = ver.Position;
                float newdst = MathTools.DistanceVectorSphere(ref center, radius, ref pos);
                if (newdst < dst)
                    dst = newdst;
            }
            return dst;
        }

        /// <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)
        {
            float dst = float.MaxValue;
            foreach (IVertex ver in Vertices)
            {
                Vector3 pos = ver.Position;
                float newdst = MathTools.DistanceVectorTriangle(ref pos1, ref pos2, ref pos3, ref pos);
                if (newdst < dst)
                    dst = newdst;
            }
            return dst;
        }
        
        /// <summary>
        /// Checks for intersection with generic geo object
        /// </summary>
        /// <param name="obj">Object to check with</param>
        /// <returns>True if intersection exists</returns>
        public override bool Intersects(IGeometry obj)
        {
            foreach (IVertex iv in Vertices)
            {
                if (obj.Distance(iv.Position) == 0)
                    return true;
            }
            return false;
        }

        /// <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)
        {
            foreach (IVertex iv in Vertices)
            {
                if (iv.Position == pos)
                    return true;
            }
            return false;
        }


        /// <summary>
        /// Returns closest point of the intersection between object and ray
        /// </summary>
        /// <param name="ray"></param>
        /// <returns>Point of intersection or invalid Vector3 (with double.NaN as values)</returns>
        public override Vector3 IntersectionPoint(IGeometry obj)
        {
            foreach (IVertex iv in Vertices)
            {
                if (obj.ContainsPosition(iv.Position))
                    return iv.Position;
            }
            return Vector3.Invalid;
        }

        /// <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)
        {
            foreach (IVertex iv in Vertices)
            {
                Vector3 me = iv.Position;
                float dst = obj.Distance(me);
                if (dst == 0)
                {
                    float dst2 = obj.Origin.Distance(ref me);

                    if (dst2 < inters.Distance)
                    {
                        inters.Distance = dst2;
                        inters.GeoObject = this as IGeometry;
                        inters.IntersectionPoint = me;
                        return true;
                    }
                }
            }
            return false;            
        }

        /// <summary>
        /// Adds all points touching the ray to the list
        /// </summary>
        /// <param name="ilist">List for intersections</param>
        /// <param name="obj">Ray object</param>
        /// <returns>True if there was an intersection</returns>        
        public override bool GetIntersectionInfo(Ray obj, IntersectionList ilist)
        {
            IIntersectionInfo inters = IntersectionInfo.CreateNew();
            bool rval = false;

            foreach (IVertex iv in Vertices)
            {
                Vector3 me = iv.Position;
                float dst = obj.Distance(me);
                if (dst == 0)
                {
                    float dst2 = obj.Origin.Distance(ref me);

                    inters.Distance = dst2;
                    inters.GeoObject = this as IGeometry;
                    inters.IntersectionPoint = me;
                    ilist.Add(inters);
                    rval = true;
                }
            }
            return rval;                                   
        }

        /// <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 t;
            Vector3 targ = Vector3.MoveInDirection(ref ray.Origin, ref ray.Direction, maxdistance);

            float mindist = float.MaxValue;

            for (int i = 0; i < this.Vertices.Count; i++)
            {
                Vector3 me = Vertices[i].Position;
                float dist = MathTools.DistanceVectorLineSegment(ref ray.Origin, ref targ, ref me, out t);
                if (dist < radius)
                {
                    float newdst = t * maxdistance;
                    mindist = MathTools.Min(mindist, newdst);
                }
            }
            if (mindist == float.MaxValue)
                return -1;
            return mindist;
        }



        #region IGeoContainer<Point3D> Members

        public Point3D this[int index]
        {
            get
            {
                return new Point3D(this.Vertices[index]);
            }
            set
            {
                //NOTE!!! should probably copy vertex... not just assign
                this.Vertices[index] = value.Vertices[0];
            }
        }

        public int Count
        {
            get { return Vertices.Count; }
        }

        /// <summary>
        /// PoinList is not dynamic, no adding possible
        /// </summary>
        /// <param name="geoobject"></param>
        public void Add(Point3D geoobject)
        {
            //do nothing
        }

        #endregion

        #region IEnumerable<IGeometry> Members

        public IEnumerator<IGeometry> GetEnumerator()
        {
            return new GeoEnumerator<Point3D>(this);
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return Vertices.GetEnumerator();
        }

        #endregion
    }
}
