using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityStandardAssets.Characters.ThirdPerson;
using UnityStandardAssets.Characters.ThirdPerson;

public class Gorgon : MonoBehaviour {
    public static List<Gorgon> all;
    private ThirdPersonCharacter controller;
    private enum State { Searching, Alert, Hunt, Attack}
    private State state;

    [SerializeField]
    private int id = 10;

    [SerializeField]
    private float walkSpeed = 30, alertSpeed = 50;
    
    private MazeCell currentCell, prevCell, nextCell;
    private bool traveling = false, changedPath = false;
    private float alertTime = 30;
    private Stack<MazeCell> currentPath;
	
    [SerializeField]
    private int maxHealth = 0;
    private int curHealth = 100;

    private Animator anim;

    private bool alive = true, attacking = false;

    void Awake()
    {
        if (id >= PlayerPrefs.GetInt("NumGorgons", 1))
        {
            Destroy(gameObject);
            return;
        }
        if (all == null)
            all = new List<Gorgon>();
        all.Add(this);
        controller = GetComponent<ThirdPersonCharacter>();
        curHealth = maxHealth;
        anim = GetComponentInChildren<Animator>();
    }
    // Use this for initialization
    void Start()
    {
        var sp = MazeSpawner.instance;
        int col = id == 1 ? 0 : sp.Columns - 1;
        int row = id == 2 ? 0 : sp.Rows - 1;

        transform.position = new Vector3((col * sp.CellSize), 1, (row * sp.CellSize));
        currentCell = sp.GetMazeCell(row, col);
    }
	
    private void MoveTowardsNextCell(float speed)
    {
        var nextPos = Vector3.MoveTowards(transform.position, nextCell.position, Time.deltaTime * speed);
        controller.Move(nextPos - transform.position, false, false);
        if (Vector3.Distance(transform.position, nextCell.position) < 1)
        { 
            prevCell = currentCell;
            currentCell = nextCell;
            nextCell = null;
            currentCell.timesVisited = (currentCell.timesVisited + 1) % int.MaxValue;
        }
    }

    // Update is called once per frame
    void Update () {
        if (!alive || attacking) return;
        RaycastHit hit;
        Physics.Raycast(transform.position, transform.forward, out hit);
        if (hit.collider != null && hit.collider.GetComponent<Player>())
        {
            nextCell = null;
            state = State.Hunt;
        }
        if (Vector3.Distance(transform.position, Player.instance.transform.position) < 3)
        {
            state = State.Attack;
            nextCell = null;
        }
        else if (state == State.Attack)
            state = State.Searching;
        /*if (attackTimer > 0)
            attackTimer -= Time.deltaTime;*/

		switch (state)
        {
            case State.Attack:
                controller.Move(Vector3.zero, false, false);
                /*if (attackTimer <= 0)*/
                    StartCoroutine(Attack());
                break;
            case State.Hunt:
                if (nextCell != null)
                    MoveTowardsNextCell(alertSpeed);
                else
                {
                    MazeCell next = null;
                    foreach (var cell in currentCell.neighbours)
                    {
                        Physics.Raycast(transform.position, cell.position-transform.position, out hit);
                        if (hit.collider.GetComponent<Player>())
                            next = cell;
                    }
                    if (next != null)
                        nextCell = next;
                    else
                        state = State.Searching;
                }
                break;
            case State.Searching:
                if (nextCell != null)
                    MoveTowardsNextCell(walkSpeed);
                else
                {
                    var count = currentCell.neighbours.Count;
                    if (count > 1)
                    {
                        var possible = new List<MazeCell>(currentCell.neighbours);
                        possible.Remove(prevCell);
                        var min = possible[0].timesVisited;
                        var index = 0;
                        for (int i = 1; i < possible.Count; i++)
                        {
                            var cur = possible[i].timesVisited;
                            if (cur < min)
                            {
                                min = cur;
                                index = i;
                            }
                        }
                        nextCell = possible[index];
                    }
                    else
                        nextCell = currentCell.neighbours[0];
                }
                break;
            case State.Alert:
                alertTime -= Time.deltaTime;
                if (alertTime <= 0)
                    state = State.Searching;
                else
                {
                    if (nextCell != null)
                        MoveTowardsNextCell(alertSpeed);
                    else if (currentPath.Count > 0)
                    {
                        nextCell = currentPath.Pop();
                        if (currentPath.Count > 0 && Vector3.Distance(currentPath.Peek().position, transform.position) < Vector3.Distance(nextCell.position, transform.position))
                            nextCell = currentPath.Pop();
                    }
                    else
                        state = State.Searching;
                }
                break;
        }
	}

    public void OnInterceptedSound(float alertLevel)
    {
        if (state == State.Hunt || !alive) return;
        alertTime = Mathf.Max(alertLevel, alertTime);
        state = State.Alert;
        currentPath = FindPathToPlayer(currentCell, new List<MazeCell>());
        changedPath = true;
    }

    public Stack<MazeCell> FindPathToPlayer(MazeCell currentCell, List<MazeCell> visited)
    {
        visited.Add(currentCell);

        if (Vector3.Distance(currentCell.position, Player.instance.transform.position) < 3)
        {
            var p = new Stack<MazeCell>();
            p.Push(currentCell);
            return p;
        }

        Stack<MazeCell> minPath = null;

        foreach (var cell in currentCell.neighbours)
        {
            if (!visited.Contains(cell))
            {
                var v = new List<MazeCell>(visited);
                var cur = FindPathToPlayer(cell, v);
                if (cur != null)
                {
                    if (minPath == null || minPath.Count > cur.Count)
                        minPath = cur;
                }
            }
        }

        if (minPath != null)
        {
            minPath.Push(currentCell);
            return minPath;
        }

        return null;
    }

    public void TakeDamage(int dam)
    {
        curHealth -= dam;
        if (curHealth <= 0)
            Die();
    }

    private IEnumerator Attack()
    {
		attacking = true;
		controller.Move(Player.instance.transform.position - transform.position, false, false);
		/*var animTimer = 0f;
        var emote = Random.Range(5, 7);
        anim.SetInteger(Animator.StringToHash("emote"), emote);
        yield return new WaitForSeconds(1);*/
        StartCoroutine(SoundBlastGenerator.instance.PerformClap(transform.position, SoundBlast.Type.Medusa));
		yield return new WaitForSeconds (1);
        /*yield return new WaitForSeconds(4);*/
		attacking = false;
    }

    public void Die()
    {
		if (!alive)
			return;
        anim.SetInteger(Animator.StringToHash("emote"), 10);
        alive = false;
        Player.instance.killCount++;

        all.Remove(this);
        if (all.Count == 0)
            PauseMenu.instance.GameOver(true);
    }
}
