using UnityEngine;

namespace Passer.Humanoid {

    public class HumanoidCollisionHandler : MonoBehaviour {
        public HumanoidControl humanoid;

        public void OnCollisionEnter(Collision collision) {
            OnTriggerStay(collision.collider);
        }

        public void OnTriggerEnter(Collider otherCollider) {
            OnTriggerStay(otherCollider);
        }

        public void OnTriggerStay(Collider otherCollider) {
            Rigidbody rigidbody = otherCollider.attachedRigidbody;

            // static colliders
            if (rigidbody == null)
                humanoid.triggerEntered = true;

            if (!otherCollider.isTrigger && !humanoid.IsMyRigidbody(rigidbody)
                && (rigidbody == null || !rigidbody.transform.IsChildOf(humanoid.transform))
                ) {
                Vector3 worldVelocity = humanoid.headTarget.neck.target.transform.TransformDirection(humanoid.velocity);
                worldVelocity += humanoid.targetVelocity;
                humanoid.hitNormal = DetermineHitNormal(worldVelocity);
                humanoid.triggerEntered = true;
            }
        }

        public void OnTriggerExit() {
            humanoid.triggerEntered = false;
        }

        public void OnCollisionExit(Collision collision) {
            humanoid.triggerEntered = false;
        }

        private Vector3 DetermineHitNormal(Vector3 velocity) {
            //Vector3 normalizedVelocity = new Vector3(velocity.x, 0, velocity.z).normalized;
            Vector3 normalizedVelocity = velocity.normalized;
            if (humanoid.hitNormal != Vector3.zero && Vector3.Angle(normalizedVelocity, humanoid.hitNormal) < 90)
                normalizedVelocity = -humanoid.hitNormal;

            CapsuleCollider cc = humanoid.bodyCapsule;
            Vector3 capsuleCenter = humanoid.hipsTarget.hips.bone.transform.position + cc.center;
            Vector3 capsuleOffset = ((cc.height - cc.radius) / 2) * humanoid.up;

            Vector3 backSweep = normalizedVelocity * (cc.radius + 0.2F);
            Vector3 top = capsuleCenter + capsuleOffset - backSweep;
            Vector3 bottom = capsuleCenter - capsuleOffset - backSweep;

            Vector3 hitNormal;
            if (CapsulecastAllNormal(top, bottom, cc.radius, normalizedVelocity, velocity.magnitude * Time.deltaTime + cc.radius + 0.3F, out hitNormal)) {
                return hitNormal;
            }

            if (Vector3.Angle(humanoid.hitNormal, normalizedVelocity) < 90) {
                return -normalizedVelocity;
            }
            else {
                return humanoid.hitNormal;
            }
        }

        private bool CapsulecastAllNormal(Vector3 top, Vector3 bottom, float radius, Vector3 direction, float maxDistance, out Vector3 hitNormal) {
            RaycastHit[] hits = Physics.CapsuleCastAll(top, bottom, radius, direction, maxDistance, Physics.DefaultRaycastLayers, QueryTriggerInteraction.Ignore);

            hitNormal = Vector3.zero;
            for (int i = 0; i < hits.Length; i++) {
                if (!hits[i].collider.isTrigger && hits[i].point.sqrMagnitude > 0) {
                    hitNormal = hits[i].normal;
                    return true;
                }
            }
            return false;
        }

    }
}