using System;
using System.Collections;
using System.Collections.Generic;
using JetBrains.Annotations;
using NaughtyAttributes;
using UnityEngine;

[RequireComponent(typeof(Rigidbody2D))]
public class Entity : MonoBehaviour {
	[Flags]
	public enum EntityFlag {
		Vampire = 0,
		Monster = 1,
		Gladiator = 2,
	}

	public Arena arena = null!;
	
    [SerializeField]
    [Required]
    public GameFlowManager gameFlowManager = null!;

    [field: SerializeField] public float Health { get; private set; }
    [Min(10f)]
    protected float initialHealth;
    [SerializeField] [Required] protected HealthBar healthBar;
    bool isAlive = true;
    public int bloodTokens = 1;
    [field: SerializeField] public float movementSpeed { get; private set; }
    [field: SerializeField] public float rotSpeed { get; private set; }
    [SerializeField] private float fov;
    [field: SerializeField] public float attackRange { get; private set; }
    [field: SerializeField] public float attackDmg { get; private set; }
    [SerializeField] protected float attackCooldown;
    protected float attackTimer;
    public Transform target;
    public EntityFlag entityType { get; protected set; }
    public Vector3 direction { get; set; }
    public Rigidbody2D rb { get; private set; }
    public Collider2D collider { get; private set; }
    [field: SerializeField]public SpriteRenderer renderer { get; private set; }
    [SerializeField] GameObject halo;
    [SerializeField] protected Color deadColor = Color.red;
    [SerializeField] protected Color emptyColor = Color.grey;
    [HideInInspector] public Animator animator;
    bool beingSucked;

    virtual protected void Awake(){
        rb = GetComponent<Rigidbody2D>();
        collider = GetComponent<Collider2D>();
        animator = GetComponentInChildren<Animator>();
    }

    protected virtual void Start() {
	    if (direction == Vector3.zero && !(this is VampireEntity))
		    Debug.LogWarning("Entity had null direction.");

        attackTimer = attackCooldown;
        initialHealth = Health;
        if (halo != null) {
            halo.SetActive(false);
        }
    }

    protected virtual void Update() { }

    protected virtual void FixedUpdate() {}

    protected virtual void Attack() {

    }

    protected virtual void SpecialAttack() {

    }

    protected virtual void MoveToTarget() {
        //Would be nice if we could force this to be in FixedUpdate
        direction = Vector3.RotateTowards(direction, (target.position - transform.position), rotSpeed * Time.fixedDeltaTime, 0.0f);
        if (!IsInAttackRange()) {
            rb.MovePosition(transform.position + direction * movementSpeed * Time.fixedDeltaTime);
        }

    }

    //Apply damage to the entity, returns true if it is still alive
    public virtual bool TakeDamage(float amount, Entity other) {
        Health -= amount;
        healthBar.SetHealthFraction(Health / initialHealth);

        if (Health <= 0) {
            OnDied();
            return false;
        }
        return true;
    }

    public virtual void HealDamage(float amount) {
        Health += amount;
        healthBar.SetHealthFraction(Health / initialHealth);

        if(Health > initialHealth) {
            Health = initialHealth;
        }
    }

    protected bool IsInAttackRange() {
        float distance = Vector2.Distance(transform.position, target.position);
        return  distance <= attackRange;
    }

    protected bool IsLookingAtTarget() {
        float angle = Vector2.SignedAngle(direction, (target.position - transform.position));
        return angle >= -fov && angle <= fov;
    }

    public bool IsAlive() {
        return isAlive;
    }

    protected virtual void OnDied() { 
        isAlive = false;
        if(!(collider is null)){
            collider.isTrigger = true;
        }
        if(!(rb is null)){
            rb.isKinematic = true;
            rb.velocity = Vector2.zero;
        }
        healthBar.gameObject.SetActive(false);
        if(bloodTokens > 0){
            renderer.color = deadColor;
            renderer.sortingOrder = -1;
        }else{
            renderer.color = emptyColor;
            renderer.sortingOrder = -2;
        }
    }

    protected virtual void OnEmpty(){
        renderer.color = emptyColor;
        renderer.sortingOrder = -2;
    }

    public void OnSuck(bool val){
        beingSucked = val;
    }

    public bool IsBeingSucked(){
        return beingSucked;
    }
    public void EnableHalo() {
        halo.SetActive(true);
    }

    public void DisableHalo() {
        halo.SetActive(false);
    }
}
