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

public class Passersby : MonoBehaviour
{
    private Rigidbody rigBody;
    private Animator animator;
    private MovePath movePath;
    private Passersby nearPassersby;
    private SemaphorePeople nearSemaphore;
    private Transform nearCar;
    private Transform nearPlayer;
    private AnimationState lastState;
    [SerializeField] private float curMoveSpeed;
    private float moveSpeed;
    private float startWalkSpeed;
    private float startRunSpeed;
    private bool redSemaphore;
    private bool insideSemaphore;
    private bool tempStop;
    private bool timeToWalk = true;

    [Tooltip("Layers of car, traffic light, pedestrians, player / Слои автомобиля, светофора, пешеходов, игрока")] public LayerMask targetMask;
    [HideInInspector] public LayerMask obstacleMask;
    [SerializeField] private AnimationState animationState;
    [SerializeField] [Tooltip("Walking speed / Скорость ходьбы")] private float walkSpeed;
    [SerializeField] [Tooltip("Running speed / Скорость бега")] private float runSpeed;
    [SerializeField] [Tooltip("Swing speed / Скорость поворота")] private float speedRotation;
    [SerializeField] [Tooltip("Viewing Angle / Угол обзора")] private float viewAngle;
    [SerializeField] [Tooltip("Radius of visibility / Радиус видимости")] private float viewRadius;
    [SerializeField] [Tooltip("Distance to pedestrian / Расстояние до пешехода")] private float distToPeople;

    [SerializeField] [Tooltip("Set your animation speed / Установить свою скорость анимации?")] private bool _overrideDefaultAnimationMultiplier;
    [SerializeField] [Tooltip("Speed animation walking / Скорость анимации ходьбы")] private float _customWalkAnimationMultiplier = 1.0f;
    [SerializeField] [Tooltip("Running animation speed / Скорость анимации бега")] private float _customRunAnimationMultiplier = 1.0f;

    public AnimationState ANIMATION_STATE
    {
        get { return animationState; }
        set { animationState = value; }
    }
    public AnimationState LastState
    {
        get{ return lastState;}
        set{lastState = value;}
    }
    public float WALK_SPEED
    {
        get { return walkSpeed; }
        set { walkSpeed = value; }
    }
    public float RUN_SPEED
    {
        get { return runSpeed; }
        set { runSpeed = value; }
    }
    public float SPEED_ROTATION
    {
        get { return speedRotation; }
        set { speedRotation = value; }
    }
    public float VIEW_ANGLE
    {
        get { return viewAngle; }
        set { viewAngle = value; }
    }
    public float VIEW_RADIUS
    {
        get { return viewRadius; }
        set { viewRadius = value; }
    }
    public bool INSIDE
    {
        get { return insideSemaphore; }
        set { insideSemaphore = value; }
    }
    public bool RED
    {
        get { return redSemaphore; }
        set { redSemaphore = value; }
    }
    public float DIST_TO_PEOPLE
    {
        get{return distToPeople;}
        set{distToPeople = value;}
    }
    public float CustomWalkAnimationMultiplier
    {
        get { return _customWalkAnimationMultiplier; }
        set { _customWalkAnimationMultiplier = value; }
    }
    public float CustomRunAnimationMultiplier
    {
        get {return _customRunAnimationMultiplier;}
        set {_customRunAnimationMultiplier = value;}
    }
    public bool OverrideDefaultAnimationMultiplier
    {
        get { return _overrideDefaultAnimationMultiplier; }
        set { _overrideDefaultAnimationMultiplier = value; }
    }

    private void Awake()
    {
        rigBody = GetComponent<Rigidbody>();
        animator = GetComponent<Animator>();
        movePath = GetComponent<MovePath>();
    }

    private void Start()
    {
        lastState = animationState;
        startWalkSpeed = walkSpeed;
        startRunSpeed = runSpeed;

        animator.CrossFade(animationState.ToString(), 0.1f, 0, Random.Range(0.0f, 1.0f));
    }

    private void Update()
    {
        ActionNearPassersby();
        ActionNearSemaphore();
        ActionNearCar();
        ActionNearPlayer();

        Move();

        if (insideSemaphore)
        {
            if (redSemaphore)
            {
                animationState = AnimationState.run;
            }
        }

        if(tempStop)
        {
            if(curMoveSpeed < 0.3f)
            {
                animator.Play("idle1");
            }
        }
        else
        {
            animator.Play(animationState.ToString());
        }

        curMoveSpeed = Mathf.Lerp(curMoveSpeed, moveSpeed, Time.deltaTime * 4.5f);

        AISight();
    }

    private void FixedUpdate()
    {
        GetPath();
    }

    private void Move()
    {
        switch (animationState)
        {
            case AnimationState.idle1:
                moveSpeed = 0.0f;

                if (curMoveSpeed < 0.15f)
                {
                    curMoveSpeed = 0.0f;
                }
                break;

            case AnimationState.walk:
                moveSpeed = walkSpeed;

                if (_overrideDefaultAnimationMultiplier)
                {
                    animator.speed = curMoveSpeed * _customWalkAnimationMultiplier;
                }
                else
                {
                    animator.speed = curMoveSpeed * 1.2f;
                }
                break;

            case AnimationState.run:
                moveSpeed = runSpeed;

                if (_overrideDefaultAnimationMultiplier)
                {
                    animator.speed = curMoveSpeed * _customRunAnimationMultiplier;
                }
                else
                {
                    animator.speed = curMoveSpeed / 3;
                }
                break;
        }

        tempStop = (animationState == AnimationState.idle1) ? true : false;
    }

    private void AISight()
    {
        nearPassersby = null;
        nearSemaphore = null;
        nearCar = null;
        nearPlayer = null;

        Collider[] targetsInViewRadius = Physics.OverlapSphere(transform.position, viewRadius, targetMask);
        List<Passersby> passersby = new List<Passersby>();
        List<SemaphorePeople> semaphores = new List<SemaphorePeople>();
        List<Transform> cars = new List<Transform>();
        List<Transform> players = new List<Transform>();

        SortTargetsInView<Passersby>(passersby, targetsInViewRadius, "People");
        SortTargetsInView<SemaphorePeople>(semaphores, targetsInViewRadius, "PeopleSemaphore");
        SortTargetsInView<Transform>(cars, targetsInViewRadius, "Car");
        SortTargetsInView<Transform>(players, targetsInViewRadius, "Player");

        if (nearPassersby == null && nearSemaphore == null)
        {
            if (nearCar == null && nearPlayer == null)
            {
                if(!insideSemaphore && !redSemaphore)
                    animationState = lastState;
            }
        }
    }

    private void GetPath()
    {
        Vector3 randFinishPos = new Vector3(movePath.finishPos.x + movePath.randXFinish, movePath.finishPos.y, movePath.finishPos.z + movePath.randZFinish);

        Vector3 targetPos = new Vector3(randFinishPos.x, rigBody.transform.position.y, randFinishPos.z);

        var richPointDistance = Vector3.Distance(Vector3.ProjectOnPlane(rigBody.transform.position, Vector3.up), Vector3.ProjectOnPlane(randFinishPos, Vector3.up));

        if (richPointDistance < 0.2f && animationState == AnimationState.walk && ((movePath.loop) || (!movePath.loop && movePath.targetPoint > 0 && movePath.targetPoint < movePath.targetPointsTotal)))
        {
            if (movePath.forward)
            {
                if (movePath.targetPoint < movePath.targetPointsTotal)
                    targetPos = movePath.walkPath.getNextPoint(movePath.w, movePath.targetPoint + 1);
                else
                    targetPos = movePath.walkPath.getNextPoint(movePath.w, 0);
                targetPos.y = rigBody.transform.position.y;
            }
            else
            {
                if (movePath.targetPoint > 0)
                    targetPos = movePath.walkPath.getNextPoint(movePath.w, movePath.targetPoint - 1);
                else
                    targetPos = movePath.walkPath.getNextPoint(movePath.w, movePath.targetPointsTotal);
                targetPos.y = rigBody.transform.position.y;
            }
        }

        if (richPointDistance < 0.5f && animationState == AnimationState.run && ((movePath.loop) || (!movePath.loop && movePath.targetPoint > 0 && movePath.targetPoint < movePath.targetPointsTotal)))
        {
            if (movePath.forward)
            {
                if (movePath.targetPoint < movePath.targetPointsTotal)
                    targetPos = movePath.walkPath.getNextPoint(movePath.w, movePath.targetPoint + 1);
                else
                    targetPos = movePath.walkPath.getNextPoint(movePath.w, 0);
                targetPos.y = rigBody.transform.position.y;
            }
            else
            {
                if (movePath.targetPoint > 0)
                    targetPos = movePath.walkPath.getNextPoint(movePath.w, movePath.targetPoint - 1);
                else
                    targetPos = movePath.walkPath.getNextPoint(movePath.w, movePath.targetPointsTotal);
                targetPos.y = rigBody.transform.position.y;
            }
        }

        Vector3 direction = targetPos - transform.position;

        if (direction != Vector3.zero)
        {
            Vector3 newDir = Vector3.zero;

            rigBody.constraints = RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationZ;
            newDir = Vector3.RotateTowards(transform.forward, direction, speedRotation * Time.deltaTime, 0.0f);

            transform.rotation = Quaternion.LookRotation(newDir);
            //transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(direction), speedRotation * Time.deltaTime);
        }

        if (richPointDistance > movePath._walkPointThreshold)
        {
            if (Time.deltaTime > 0)
            {
                transform.position += transform.forward * curMoveSpeed * Time.deltaTime;
                //rigBody.MovePosition(transform.position + transform.forward * curMoveSpeed * Time.fixedDeltaTime);
            }
        }
        else if (richPointDistance <= movePath._walkPointThreshold && movePath.forward)
        {

            if (movePath.targetPoint != movePath.targetPointsTotal)
            {
                movePath.targetPoint++;

                movePath.finishPos = movePath.walkPath.getNextPoint(movePath.w, movePath.targetPoint);
            }
            else if (movePath.targetPoint == movePath.targetPointsTotal)
            {
                if (movePath.loop)
                {
                    movePath.finishPos = movePath.walkPath.getStartPoint(movePath.w);

                    movePath.targetPoint = 0;
                }
                else
                {
                    if (movePath != null)
                    {
                        movePath.walkPath.SpawnPoints[movePath.w].AddToSpawnQuery(new MovePathParams { });
                    }

                    Destroy(gameObject);
                }
            }

        }
        else if (richPointDistance <= movePath._walkPointThreshold && !movePath.forward)
        {
            if (movePath.targetPoint > 0)
            {
                movePath.targetPoint--;

                movePath.finishPos = movePath.walkPath.getNextPoint(movePath.w, movePath.targetPoint);
            }
            else if (movePath.targetPoint == 0)
            {
                if (movePath.loop)
                {
                    movePath.finishPos = movePath.walkPath.getNextPoint(movePath.w, movePath.targetPointsTotal);

                    movePath.targetPoint = movePath.targetPointsTotal;
                }
                else
                {
                    movePath.walkPath.SpawnPoints[movePath.w].AddToSpawnQuery(new MovePathParams { });
                    Destroy(gameObject);
                }
            }
        }
    }

    private void SortTargetsInView<T>(IList<T> list, Collider[] targetsInViewRadius, string tag)
    {
        foreach(Collider c in targetsInViewRadius)
        {
            if(!c.transform.CompareTag(tag) || c.transform.position == transform.position) continue;

            if(typeof(T) == typeof(Passersby))
            {
                if(c.transform.parent.name != transform.parent.name || c.GetComponent<MovePath>().w != movePath.w) continue;
            }

            Vector3 forward = transform.TransformDirection(Vector3.forward);
            Vector3 target = c.transform.position - transform.position;

            if (Vector3.Dot(forward, target) > 0)
            {
                list.Add(c.GetComponent<T>());
            }
        }

        if(typeof(T) == typeof(Passersby))
        {
            List<Passersby> passersby = list as List<Passersby>;
            NearTarget<Passersby>(passersby);
        }
        else if(typeof(T) == typeof(SemaphorePeople))
        {
            List<SemaphorePeople> semaphore = list as List<SemaphorePeople>;
            NearTarget<SemaphorePeople>(semaphore);
        }
        else if(typeof(T) == typeof(Transform))
        {
            if (tag == "Car")
            {
                List<Transform> cars = list as List<Transform>;
                NearTarget<Transform>(cars, "Car");
            }
            else if(tag == "Player")
            {
                List<Transform> players = list as List<Transform>;
                NearTarget<Transform>(players, "Player");
            }
        }
    }

    private void NearTarget<T>(IList<T> list, string tag = "")
    {
        foreach(T t in list)
        {
            Transform target = null;
            
            if(typeof(T) == typeof(Passersby))
            {
                Passersby p = t as Passersby;
                target = p.GetComponent<Transform>();
            }
            else if(typeof(T) == typeof(SemaphorePeople))
            {
                SemaphorePeople s = t as SemaphorePeople;
                target = s.GetComponent<Transform>();
            }
            else if(typeof(T) == typeof(Transform))
            {
                if (tag == "Car")
                {
                    Transform car = t as Transform;
                    target = car.GetComponent<Transform>();
                }
                else if(tag == "Player")
                {
                    Transform player = t as Transform;
                    target = player.GetComponent<Transform>();
                }
            }

            Vector3 dirToTarget = ((new Vector3(target.position.x, target.position.y, target.position.z)) - transform.position).normalized;

            if (Vector3.Angle(transform.forward, dirToTarget) < viewAngle / 2)
            {
                float dstToTarget = Vector3.Distance(transform.position, target.position);
                float dstToNearTarget = 0.0f;

                if(typeof(T) == typeof(Passersby))
                {
                    if (nearPassersby != null)
                    {
                        dstToNearTarget = Vector3.Distance(transform.position, nearPassersby.transform.position);
                    }
                }
                else if(typeof(T) == typeof(SemaphorePeople))
                {
                    if (nearSemaphore != null)
                    {
                        dstToNearTarget = Vector3.Distance(transform.position, nearSemaphore.transform.position);
                    }
                }
                else if(typeof(T) == typeof(Transform))
                {
                    if (tag == "Car")
                    {
                        if (nearCar != null)
                        {
                            dstToNearTarget = Vector3.Distance(transform.position, nearCar.transform.position);
                        }
                    }
                    else if(tag == "Player")
                    {
                        if (nearPlayer != null)
                        {
                            dstToNearTarget = Vector3.Distance(transform.position, nearPlayer.transform.position);
                        }
                    }
                }

                if (!Physics.Raycast(transform.position, dirToTarget, dstToTarget, obstacleMask))
                {
                    if(typeof(T) == typeof(Passersby))
                    {
                        if (nearPassersby == null)
                        {
                            nearPassersby = t as Passersby;
                            continue;
                        }
                        else
                        {
                            if (dstToTarget < dstToNearTarget)
                            {
                                nearPassersby = t as Passersby;
                            }
                        }
                    }
                    else if(typeof(T) == typeof(SemaphorePeople))
                    {
                        if (nearSemaphore == null)
                        {
                            nearSemaphore = t as SemaphorePeople;
                            continue;
                        }
                        else
                        {
                            if (dstToTarget < dstToNearTarget)
                            {
                                nearSemaphore = t as SemaphorePeople;
                            }
                        }
                    }
                    else if(typeof(T) == typeof(Transform))
                    {
                        if (tag == "Car")
                        {
                            if (nearCar == null)
                            {
                                nearCar = t as Transform;
                                continue;
                            }
                            else
                            {
                                if (dstToTarget < dstToNearTarget)
                                {
                                    nearCar = t as Transform;
                                }
                            }
                        }
                        else if(tag == "Player")
                        {
                            if (nearPlayer == null)
                            {
                                nearPlayer = t as Transform;
                                continue;
                            }
                            else
                            {
                                if (dstToTarget < dstToNearTarget)
                                {
                                    nearPlayer = t as Transform;
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    private void ActionNearPassersby()
    {
        if (nearPassersby != null)
        {
            float distance = Vector3.Distance(transform.position, nearPassersby.transform.position);

            if (distance < distToPeople)
            {
                if (nearPassersby.ANIMATION_STATE == AnimationState.idle1)
                {
                    animationState = AnimationState.idle1;
                    timeToWalk = false;
                }
                else
                {
                    walkSpeed = nearPassersby.walkSpeed;
                    runSpeed = nearPassersby.runSpeed;
                }
            }

            if(animationState == AnimationState.idle1 && nearPassersby.ANIMATION_STATE != AnimationState.idle1)
            {
                if (!timeToWalk)
                {
                    StartCoroutine(StartMove());
                    timeToWalk = true;
                }
            }
        }
        else
        {
            walkSpeed = startWalkSpeed;
            runSpeed = startRunSpeed;
        }
    }

    private void ActionNearSemaphore()
    {
        if (nearSemaphore == null) return;

        float distance = Vector3.Distance(transform.position, nearSemaphore.transform.position);

        if(distance < 25.0f)
        {
            if(nearSemaphore.PEOPLE_CAN)
            {
                animationState = lastState;
            }
            else
            {
                if(!insideSemaphore)
                {
                    animationState = AnimationState.idle1;
                }
            }
        }
    }

    private void ActionNearCar()
    {
        if (nearCar == null) return;

        float distance = Vector3.Distance(transform.position, nearCar.transform.position);

        if (distance < 10.0f)
        {
            animationState = AnimationState.idle1;
        }
        else
        {
            animationState = lastState;
        }
    }

    private void ActionNearPlayer()
    {
        if (nearPlayer == null) return;

        float distance = Vector3.Distance(transform.position, nearPlayer.transform.position);

        if (distance < 10.0f)
        {
            animationState = AnimationState.idle1;
        }
        else
        {
            animationState = lastState;
        }
    }

    IEnumerator StartMove()
    {
        yield return new WaitForSeconds(1.2f);
        animationState = lastState;
        tempStop = false;
    }

    public Vector3 DirFromAngle(float angleInDegrees, bool angleIsGlobal)
    {
        if (!angleIsGlobal)
        {
            angleInDegrees += transform.eulerAngles.y;
        }
        return new Vector3(Mathf.Sin(angleInDegrees * Mathf.Deg2Rad), 0, Mathf.Cos(angleInDegrees * Mathf.Deg2Rad));
    }
}