using System;
using System.Drawing;
using System.Xml.Serialization;

namespace Icarus.Logic.Physics
{
    /// <summary>
    /// Triangles will be used as level collision shapes that can
    /// be collided with.  All Triangles must be right triangles for
    /// simplicity.  The right angle should be inforced in the level
    /// builder.
    /// </summary>
    [XmlInclude(typeof(Physics.TriangleBL))]
    [XmlInclude(typeof(Physics.TriangleBR))]
    [XmlInclude(typeof(Physics.TriangleTL))]
    [XmlInclude(typeof(Physics.TriangleTR))]
    public class Triangle
    {

        #region Declarations

        /// <summary>
        /// Bounding rectangle of
        /// </summary>
        private PhysicsRectangle rectTriangleBounds;
        
        /// <summary>
        /// Point 1 of triangle
        /// </summary>
        private Point p1;

        /// <summary>
        /// Point 2 of triangle
        /// </summary>
        private Point p2;

        /// <summary>
        /// Point 3 of triangle
        /// </summary>
        private Point p3;

        /// <summary>
        /// The slope of a this triangle's Hyp.
        /// </summary>
        private float fSlope;

        /// <summary>
        /// Text Description of this object for level editing
        /// </summary>
        private string strDescription = "";

        #endregion

        #region Properties/Feilds

        /// <summary>
        /// Provides access to the rectangle bounding box
        /// of this triangle.
        /// </summary>
        public PhysicsRectangle TriangleBounds
        {
            get { return this.rectTriangleBounds; }
            set { this.rectTriangleBounds = value; }
        }

        /// <summary>
        /// Provides access to this triangles point 1
        /// </summary>
        public Point P1
        {
            get { return this.p1; }
            set 
            { 
                this.p1 = value;
                this.Slope = this.calculateSlope();
                this.calculateTriangleBounds();
            }
        }

        /// <summary>
        /// Provides access to this triangles point 2
        /// </summary>
        public Point P2
        {
            get { return this.p2; }
            set 
            { 
                this.p2 = value; 
                this.Slope = this.calculateSlope();
                this.calculateTriangleBounds();
            }
        }

        /// <summary>
        /// Provides access to this triangles point 3
        /// </summary>
        public Point P3
        {
            get { return this.p3; }
            set 
            {
                this.p3 = value; 
                this.Slope = this.calculateSlope();
                this.calculateTriangleBounds();
            }
        }

        /// <summary>
        /// The slope of this triangle's hypotenuse
        /// </summary>
        public float Slope
        {
            get { return this.fSlope; }
            set { this.fSlope = value; }

        }

        /// <summary>
        /// A text Description of this object for level editing
        /// </summary>
        public string Description
        {
            get { return this.strDescription; }
            set { this.strDescription = value; }
        }

        #endregion

        #region Construction
		
        public Triangle()
        {
            this.rectTriangleBounds = new PhysicsRectangle();
            this.p1 = new Point();
            this.p2 = new Point();
            this.p3 = new Point();
            this.calculateTriangleBounds();
            this.Slope = this.calculateSlope();

            this.Description = "Triangle";
        }


        public Triangle(Point p1, Point p2, Point p3)
        {
            this.rectTriangleBounds = new PhysicsRectangle();
            this.p1 = p1;
            this.p2 = p2;
            this.p3 = p3;
            this.calculateTriangleBounds();
            this.Slope = this.calculateSlope();

            this.Description = "Triangle";
        }

        #endregion

        #region Methods

        /// <summary>
        /// Determines if this triangle intersects with the passed rectangle
        /// </summary>
        /// <param name="rect">Rectangle to check for intersection with triangle</param>
        /// <returns>True - intersection; false - no intersection</returns>
        public bool IntersectsWith(PhysicsRectangle rect)
        {
            //TODO: see if anyone can think of a simpler, faster, better way to write this function.
            int p1x, p2x, p3x, p4x,
                p1y, p2y, p3y, p4y;
            
            //See if the rectangle even comes close with a few quick tests
            if(!this.TriangleBounds.IntersectsWith(rect))
            {
                //The rectangle in question does not even intersect this triangles bounding rect
                return false;
            }
            else
            {
                //Make copy of triangle bounding rectangle
                PhysicsRectangle rectIntersection = new PhysicsRectangle(this.TriangleBounds.X, this.TriangleBounds.Y,
                                                                         this.TriangleBounds.Width, this.TriangleBounds.Height);
                //Change to intersection of rect and triangle bounding rectangle
                rectIntersection.Intersect(rect);
                if(rectIntersection.Height*rectIntersection.Width >=
                    ((this.TriangleBounds.Height*this.TriangleBounds.Width)/2.0f))
                {
                    /* Intersection with bounding rectangle is greater than half the area of the
                     * bounding rectangle so we know the trangle was intercepted. */
                    return true;
                }

                //check to see if the intersecting rect hit one of the triangle legs
                int fLeg1 = 0, fLeg2 = 0;
                if((int)this.P1.x == (int)this.P2.x)
                {
                    fLeg1 = (int)this.P1.x;
                }
                else if((int)this.P1.x == (int)this.P3.x)
                {
                    fLeg1 = (int)this.P1.x;
                }
                else if((int)this.P2.x == (int)this.P3.x)
                {
                    fLeg1 = (int)this.P2.x;
                }

                if((int)this.P1.y == (int)this.P2.y)
                {
                    fLeg2 = (int)this.P1.y;
                }
                else if((int)this.P1.y == (int)this.P3.y)
                {
                    fLeg2 = (int)this.P1.y;
                }
                else if((int)this.P2.y == (int)this.P3.y)
                {
                    fLeg2 = (int)this.P2.y;
                }

                p1x = rectIntersection.X; 
                p1y = rectIntersection.Y;
            
                p2x = rectIntersection.X + rectIntersection.Width; 
                p2y = rectIntersection.Y;
            
                p3x = rectIntersection.X + rectIntersection.Width; 
                p3y = rectIntersection.Y + rectIntersection.Height;
            
                p4x = rectIntersection.X; 
                p4y = rectIntersection.Y + rectIntersection.Height;

                if(fLeg1 == p1x)
                {
                    return true;
                }
                else if(fLeg1 == p2x)
                {
                    return true;
                }
                else if(fLeg1 == p3x)
                {
                    return true;
                }
                else if(fLeg1 == p4x)
                {
                    return true;
                }

                if(fLeg2 == p1y)
                {
                    return true;
                }
                else if(fLeg2 == p2y)
                {
                    return true;
                }
                else if(fLeg2 == p3y)
                {
                    return true;
                }
                else if(fLeg2 == p4y)
                {
                    return true;
                }

            }
            
            
            //Now test all points of rect to see if they are located within the triangle
            
            //All Points of rectangle to test with
            p1x = rect.X; 
            p1y = rect.Y;
            
            p2x = rect.X + rect.Width; 
            p2y = rect.Y;
            
            p3x = rect.X + rect.Width; 
            p3y = rect.Y + rect.Height;
            
            p4x = rect.X; 
            p4y = rect.Y + rect.Height;

            //Setup some vectors for triangle collision test.
            Vector v1, v2, v3, vCross1, vCross2, vCross3;
            
            v1 = new Vector();
            v2 = new Vector();
            v3 = new Vector();
            vCross1 = new Vector();
            vCross2 = new Vector();
            vCross3 = new Vector();
            
            // Set point values in array for looping
            float[] iArryTmpX = {p1x, p2x, p3x, p4x};
            float[] iArryTmpY = {p1y, p2y, p3y, p4y};
            
            //Repeat for all 4 corners of the rect in question
            for(int i = 0; i < 4; i++)
            {
                v1.i = iArryTmpX[i] - this.P1.x;
                v1.j = iArryTmpY[i] - this.P1.y;
                v1.k = 0.0f;

                v2.i = iArryTmpX[i] - this.P2.x;
                v2.j = iArryTmpY[i] - this.P2.y;
                v2.k = 0.0f;

                v3.i = iArryTmpX[i] - this.P3.x;
                v3.j = iArryTmpY[i] - this.P3.y;
                v3.k = 0.0f;

                // Calculate normal vectors
                Vector.CrossMultVector(v1, v2, vCross1);
                Vector.CrossMultVector(v2, v3, vCross2);
                Vector.CrossMultVector(v3, v1, vCross3);

                /*
                *  If the normal vectors all point the same direction, the point is located
                * within the triangle
                */
                if((vCross1.k >= 0 && vCross2.k >= 0 && vCross3.k >= 0) ||
                    (vCross1.k <= 0 && vCross2.k <= 0 && vCross3.k <= 0))
                {
                    return true;
                }           
            }
    
            //Finally we can say the rectangle did not intersect the triangle
            return false;
            
        }


        /// <summary>
        /// Performs the necessary operations on a PO
        /// that has collided with this triangle
        /// </summary>
        /// <param name="po">Physics Object that collided with triangle</param>
        /// <returns>Action to be taken after collision occurs(ie: remove, doNothing)</returns>
        public virtual int intersectionAction(PhysicsObject po)
        {
            
            po.Velocity.i = 0.0f;
            po.Velocity.j = 0.0f;
            po.Velocity.k = 0.0f;

            return (int)PhysicsSettings.enumActions.DoNothing;
        }
        
        /// <summary>
        /// Calculates the bounding box of this triangle
        /// </summary>
        public void calculateTriangleBounds()
        {
            //Find X
            if(this.P1.x < this.P2.x)
            {
                if(this.P1.x < this.P3.x)
                {
                    this.rectTriangleBounds.X = (int)this.P1.x;
                    this.rectTriangleBounds.Width = (int)(this.P3.x - this.P1.x);
                }
                else
                {
                    this.rectTriangleBounds.X = (int)this.P3.x;
                    this.rectTriangleBounds.Width = (int)(this.P2.x - this.P3.x);
                }
            }
            else
            {
                if(this.P2.x < this.P3.x)
                {
                    this.rectTriangleBounds.X = (int)this.P2.x;
                    this.rectTriangleBounds.Width = (int)(this.P3.x - this.P2.x);
                }
                else 
                {
                    this.rectTriangleBounds.X = (int)this.p3.x;
                    this.rectTriangleBounds.Width = (int)(this.P1.x - this.P3.x);
                }
            }
        
            //Find Y
            if(this.P1.y < this.P2.y)
            {
                if(this.P1.y < this.P3.y)
                {
                    this.rectTriangleBounds.Y = (int)this.P1.y;
                    this.rectTriangleBounds.Height = (int)(this.P3.y - this.P1.y);
                }
                else
                {
                    this.rectTriangleBounds.Y = (int)this.P3.y;
                    this.rectTriangleBounds.Height = (int)(this.P2.y - this.P3.y);
                }
            }
            else
            {
                if(this.P2.y < this.P3.y)
                {
                    this.rectTriangleBounds.Y = (int)this.P2.y;
                    this.rectTriangleBounds.Height = (int)(this.P3.y - this.P2.y);
                }
                else 
                {
                    this.rectTriangleBounds.Y = (int)this.P3.y;
                    this.rectTriangleBounds.Height = (int)(this.P1.y - this.P3.y);
                }
            }

        }


        /// <summary>
        /// Calculate this triangle's hypotenuse slope
        /// </summary>
        /// <returns>Slope of Hypotenuse</returns>
        public float calculateSlope()
        {
            //Find the two points in question
            if(this.P1.x != this.P2.x &&
                this.P1.y != this.P2.y)
            {
                //P1 and P2
                return ((this.P1.y - this.P2.y)/(this.P1.x - this.P2.x));
            }
            else if(this.P1.x != this.P3.x &&
                this.P1.y != this.P3.y)
            {
                //P1 and P3
                return ((this.P1.y - this.P3.y)/(this.P1.x - this.P3.x));
            }
            else
            {
                //P2 and P3
                if((this.P2.x - this.P3.x) == 0.0f) return 0.0f;
                return ((this.P2.y - this.P3.y)/(this.P2.x - this.P3.x));
            }

        }



        #endregion
    }
}
