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

// import the unity AI library
using UnityEngine.AI;

[RequireComponent(typeof(NavMeshAgent))]
public class AICharacter : BaseCharacter
{
    // the target for the AI character to chase
    [SerializeField]
    private Transform target = null;

    // the max range at which to start chasing the target
    [SerializeField]
    private float targetChaseRange = 10.0f;

    // the distance within which to stop from the target
    [SerializeField]
    private float targetStoppingDistance = 2f;

    // indicates how long AI should stop and think (be idle) on reaching a destination waypoint (not target) so it is not constantly moving
    [SerializeField]
    private float thinkingTime = 5f;

    // used to cache the nav mesh agent component attached to this character
    private NavMeshAgent agent;

    // used to cache the waypoint controller component attached to this character
    WaypointController waypointController;

    // indicates if AI is currently in thinking mode
    private bool isThinking = false;

    // store the elapsed thinking (idle) time when AI is in thinking mode
    float elapsedThinkingTime = 0f;

    // The current target waypoint to visit
    Transform currentWaypoint;

    // The agent's current target destination, useful for checking if current destination is player or waypoint
    Transform currentDestination;

    // Start is called before the first frame update
    // Make start override Start method in base (inherited) class
    protected override void Start()
    {
        // Call the base (inherited) class start method first
        base.Start();

        // Get and cache the nav mesh agent component attached to the game object this script is attached to
        agent = GetComponent<NavMeshAgent>();

        // Get and cache the waypoint controller component attached to the game object this script is attached to
        waypointController = GetComponent<WaypointController>();

        // Set a new target waypoint to visit
        SetNewWaypoint();

        // Set the attack range to the target's stopping distance
        attackRange = targetStoppingDistance;

        // Set non-combat mode based on setting
        SetNonCombat(nonCombatMode);
    }

    // Update is called once per frame
    void Update()
    {
        // if no Nav Mesh Agent component attached to game object, no need to continue, return and exit the method early
        if (agent == null) return;

        // Check if AI is attacking and if yes, attack
        // Else
        //      Check if AI is near enough to chase and if yes, update current destination to target
        //      Else if AO is thinking, update the thinking
        //      Else update the current way point
        //      Move the AI passing its movement (agent.velocity). Hint: see what you did in player


        if (IsAttacking())
        {
            Attack();
        }
        else
        {
            if (IsTargetWithinRange()) // Chase
            {
                SetCurrentDestination(target, attackRange);
            }
            else if (isThinking) // Think
            {
                UpdateThinking();
            }
            else // Patrol
            {
                UpdateCurrentWaypoint();
            }

            Move(agent.velocity);
        }
    }

    // Check if AI is in attack mode
    protected override bool IsAttacking()
    {
        // if target or non combat mode or current target destination not set, no one to attack
        // Otherwise, check if current destination is the target and agent is close enough (within attack range) to attack
        if (target == null || nonCombatMode || agent.destination == null)
            return false;
        else
        {
            if (currentDestination == target && HasReachedDestination(attackRange))
            {
                return true;
            }
        }

        return false;
    }

    // Set the Nav Mash Agent's target destination
    private void SetCurrentDestination(Transform destination, float stoppingDistance)
    {
        // Update the current destination so you can use to check if current destination is target or player
        currentDestination = destination;

        // Update the nav mesh agent's destination and stopping distance to the specified passed in parameters
        agent.destination = destination.position;
        agent.stoppingDistance = stoppingDistance;

        // make the AI agent resume movement to the updated destination
        agent.isStopped = false;
    }

    // Checks if target (player) is within chasing distance
    private bool IsTargetWithinRange()
    {
        // if no target has been set, no need to continue, return and exit the method early
        if (target == null) return false;

        // calculate the position vector difference / offset
        Vector3 positionOffset = target.position - transform.position;

        // As you only need to check if target is nearby, you do not need the actual distance value 
        // use squared magnitude instead of actual distance which uses square root which is computationally slower than squared magnitude
        // See more: https://docs.unity3d.com/ScriptReference/Vector3-sqrMagnitude.html
        return positionOffset.sqrMagnitude <= targetChaseRange * targetChaseRange;
    }

    // Check if Nav Mesh Agent has reached its current target destination i.e. remaining distance is less than/equal stopping distance
    private bool HasReachedDestination(float stoppingDistance)
    {
        // Check if AI agent is NOT stopped AND the AI agent's remaining distance is less than the passed in stopping distance parameter
        // If yes, make the AI agent stop movement (set isStopped property) and return true
        // Else,  make the AI agent resume/continue movement to the destination and return false
        if (!agent.isStopped && agent.remainingDistance <= stoppingDistance)
        {
            agent.isStopped = true;
            return true;
        }

        agent.isStopped = false;
        return false;
    }

    // Reset the thinking duration (elapsedThinkingTime)
    private void StartThinking()
    {
        // What do you think needs to happen to the elapsed thinking time and isThinking variables?
        elapsedThinkingTime = 0f;
        isThinking = true;
    }

    // Update the thinking duration
    private void UpdateThinking()
    {
        // Increase thinking counter by time elapsed since last frame
        elapsedThinkingTime += Time.deltaTime;

        // If the elapsed thinking time is greater than or equal the the thinking time, the AI agent can stop thinking
        // Update isThinking
        // Set a new target waypoint to visit - what method allows you to get and set a new waypoint or see what you did in Start
        if (elapsedThinkingTime >= thinkingTime)
        {
            isThinking = false;

            // No longer thinking, get new waypoint to visit
            SetNewWaypoint();
        }
    }

    // Update Nav Mesh Agent's destination back to the current waypoint in case AI took a detour to chase the target (player)
    private void UpdateCurrentWaypoint()
    {
        // Update agent's destination to current waypoint in case it changed in the last last frame and set the stopping distance to the waypoint stopping distance (waypointController.StoppingDistance)
        SetCurrentDestination(currentWaypoint, waypointController.StoppingDistance);

        // If agent has reached waypoint, start thinking
        if (HasReachedDestination(waypointController.StoppingDistance))
        {
            StartThinking();
        }
    }

    // Gets and sets a new waypoint to visit
    void SetNewWaypoint()
    {
        // If there is a waypoint controller script component attached (will be cached in waypointController)
        //      use the waypointController's Get Next Waypoint method to set (assign) a new currentWaypoint
        // currentWaypoint = ...
        if (waypointController != null)
        {
            currentWaypoint = waypointController.GetNextWaypoint();
        }

    }
}
