using System;
using UnityEngine;
using Unity.MLAgents;
using Unity.MLAgents.Sensors;
using Unity.MLAgents.Actuators;
using System.Collections.Generic;

[RequireComponent(typeof(MovementController))]
public class NPC : Agent, ICharacter
{
    [HideInInspector]
    public Character AgentCharacter;
    public CharacterCondition Condition;

    public NPC_BaseState NPC_State { get; private set; }

    public Character GetCharacter => AgentCharacter;

    private NPC_DirectPointState DirectState;
    private NPC_InCoverState CoverState;
    private NPC_RunningState RunningState;

    private MovementController moveController;
    private BufferSensorComponent bufferSensor;

    private void Awake()
    {
        DirectState = new NPC_DirectPointState();
        CoverState = new NPC_InCoverState();
        RunningState = new NPC_RunningState();
        NPC_State = DirectState;

        AgentCharacter = new Character();
        Condition = AgentCharacter.Condition;
    }
    private void Start()
    {
        AgentCharacter = new Character();
        Condition = AgentCharacter.Condition;

        moveController = gameObject.GetComponent<MovementController>();
        bufferSensor = gameObject.GetComponent<BufferSensorComponent>();

        GameManager.OnResetScene += AgentCharacter.ResetCharacter;
    }

    public override void OnEpisodeBegin()
    {
      
    }

    public override void CollectObservations(VectorSensor sensor)
    {
        sensor.AddObservation(Condition.HealthPoints);
        sensor.AddObservation(Condition.ArmourPoints);
        sensor.AddObservation(Condition.Ammunition);
        sensor.AddObservation((int)NPC_State.State);



        var candidates = moveController.getPointsCandidate();
        foreach (var point in candidates)
        {

            bufferSensor.AppendObservation(new float[] {
                //1 position in navpointId
                (float)moveController.currentPosition.PointId,
                //2 distance to flag
                moveController.currentPosition.FlagDistance,
                //3 death count in point
                moveController.currentPosition.DeathAttr,
                //4 flagEnemyDistance
                GameManager.IsCloserToFlagFromNextNavPoint(point, transform.position)==true?1:0,
                //5 EnemyVsNavPointDistance
                GameManager.IsCloserToEnemyThanToNextNavPoint(point,transform.position, AgentCharacter.Team)==true?1:0
            }); 

        }
    }

    public override void Heuristic(in ActionBuffers actionsOut)
    {
        var discreteActionsOut = actionsOut.DiscreteActions;
        if (Input.GetKeyDown(KeyCode.W))
        {
            discreteActionsOut[0] = 1;
        }
    }

    public override void OnActionReceived(ActionBuffers actions)
    {
        if (actions.DiscreteActions[0] == 1)
        {
            moveController.MoveToRandomPoint();
            NPC_State = RunningState;
        }
    }

    public event Action<object> OnKilledEvent;
    public void GetDamage(float damage)
    {
        AgentCharacter.LastTimeHit = TimeManager.Instance.CurrentTime;
        Condition.GiveHealth(-Mathf.RoundToInt(damage * (1 - Condition.ArmourPoints * 0.5f)));
        Condition.GiveArmour(-Mathf.RoundToInt(Mathf.Sqrt(damage) * 5));

        if (Condition.HealthPoints < 0)
        {
            OnKilledEvent?.Invoke(this);
            moveController.currentPosition.DeathAttr += 1;
        }
    }

    private void OnDestroy()
    {
        Debug.LogWarning("Pooled object was destroyed");
    }
}
