using UnityEngine;
using System.Collections;
using System.Collections.Generic;

namespace CharacterEngine.Primitives
{
    public class CECollider
    {
        public static int[,] edges = new int[,]
        {
            {0, 2}, {0, 3}, {1, 2},{1, 3}
        };
        
        private BoxCollider2D collider;
        public BoxCollider2D Coll
        {
            get { return collider; }
        }

        public virtual Vector2 position
        {
            get { return collider.bounds.center; }
        }

        public float Height
        {
            get { return (GetPoint(0) - GetPoint(3)).magnitude; }
        }
        public float Width
        {
            get { return (GetPoint(0) - GetPoint(2)).magnitude; }
        }

        public Vector2 Max
        {
            get { return GetPoint(0); }
        }
        public Vector2 Min
        {
            get { return GetPoint(1); }
        }

        public Vector2 AABBMax
        {
            get { return GetAABBPoint(0); }
        }
        public Vector2 AABBMin
        {
            get { return GetAABBPoint(1); }
        }

        public readonly Vector2[] localPoints;     

        public CECollider (BoxCollider2D coll)
        {
            collider = coll;

            ///COLLIDER CONSTANT RELATIVE POINTS
            localPoints = new Vector2[]
            {
            coll.size * 0.5f, //max
            -coll.size * 0.5f, //min
            new Vector2(-coll.size.x * 0.5f, coll.size.y * 0.5f),
            new Vector2(coll.size.x * 0.5f, -coll.size.y * 0.5f)
            };
        }
        
        /// <summary>
        /// GET REAL WORLD POINT OF COLLIDER
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public virtual Vector2 GetPoint (int index)
        {
            return position + (Vector2)(collider.transform.rotation * Vector2.Scale(localPoints[index], collider.transform.lossyScale));
        }
        public virtual Vector2 GetPoint(int index, Vector2 position)
        {
            return position + (Vector2)(collider.transform.rotation * Vector2.Scale(localPoints[index], collider.transform.lossyScale));
        }
        public virtual Vector2 GetAABBPoint(int index)
        {
            return position + (Vector2)(Vector2.Scale(localPoints[index], collider.transform.lossyScale));
        }
        public virtual Vector2 GetAABBPoint(int index, Vector2 position)
        {
            return position + (Vector2)(Vector2.Scale(localPoints[index], collider.transform.lossyScale));
        }

        public bool ContainsPoint (Vector2 p)
        {
            p = position + (Vector2)(Quaternion.Inverse(collider.transform.rotation) * (p - position));
            bool insideX = p.x > AABBMin.x && p.x < AABBMax.x;
            bool insideY = p.y > AABBMin.y && p.y < AABBMax.y;

            return insideX && insideY;
        }

        /// <summary>
        /// Collision test along a path
        /// </summary>
        public CECollision SweepTest(Vector2 dir, float distance)
        {
            CECollision hit = new CECollision();

            for (int i = 0; i <= CEPrimitives.sweepResolution; i++)
            {
                Vector2 currentDistance = dir.normalized * (distance * (i / CEPrimitives.sweepResolution));
                hit = BoxCast(position + currentDistance);

                if (hit.impulse.magnitude != 0.0f) break;
            }

            return hit;
        }

        /// <summary>
        /// Collision test in an specific point
        /// </summary>
        public CECollision BoxCast(Vector2 position)
        {
            int mask = LayerMask.GetMask("Default");

            CECollision collision = new CECollision();
            RaycastHit2D outHit = new RaycastHit2D();
            

            for (int i = 0; i < edges.GetLength(0); i++)
            {
                outHit = Physics2D.Linecast(GetPoint(edges[i, 0], position), GetPoint(edges[i, 1], position), mask);

                if (!outHit)
                {
                    outHit = Physics2D.Linecast(GetPoint(edges[i, 1], position), GetPoint(edges[i, 0], position), mask);
                }

                if (outHit) break;
            }

            if (outHit)
            {
                CECollider agent = new CECollider((BoxCollider2D)outHit.collider);
                CECollider myAgent = new CEVirtualCollider(position, collider);
                collision = CECollision.ResolveCollision(myAgent, agent);
            }

            return collision;
        }
    }

    public class CEVirtualCollider : CECollider
    {
        protected Vector2 virtualPosition;
        public override Vector2 position
        {
            get
            {
                return virtualPosition;
            }
        }

        public CEVirtualCollider (Vector2 pos, BoxCollider2D coll) : base(coll)
        {
            virtualPosition = pos;
        }
    }
}
