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

public class CustomerManager : MonoBehaviour
{
    public Transform CustomerSpawnPoint = null;
    public Transform CustomerExitPoint = null;
    public Transform[] CustomerOrderingPoints = null;
    public Transform[] CustomerWaitingPoints = null;


    // A list of customers waiting to order
    public List<Customer> CustomersWaitingToOrder = new List<Customer>();

    // A list of customers waiting for their drink, with ordered waiting points
    public List<Customer> CustomersWaitingForDrink = new List<Customer>();

    // Customer template
    public GameObject[] CustomerPrefabs = null;

    private float NextSpawnTime = 0.0f;

    [SerializeField]
    private LevelDefinition[] AllLevels = null;
    [SerializeField]
    private LevelDefinition DefaultLevelDefinition = null;

    public LevelDefinition CurrentLevel
    {
        get
        {
            if (GameState.LevelToPlay < AllLevels.Length)
                return AllLevels[GameState.LevelToPlay];

            return DefaultLevelDefinition;
        }
    }

    private MarbleBag<CustomerOrderParameters> customerOrders = null;
    int CustomersSpawned = 0;

    // Start is called before the first frame update
    void Start()
    {
        customerOrders = new MarbleBag<CustomerOrderParameters>(CurrentLevel.Orders);
        CustomersSpawned = 0;
        LevelCompleteDialog.SetActive(false);
    }

    public int CalculateTipAmount(Customer c)
    {
        LevelDefinition level = CurrentLevel;

        float timeCustomerWaited = Time.time - c.SpawnTime;
        int result = 0;

        if (timeCustomerWaited < level.ServingTimeExpectation)
            result = 3;
        else if (timeCustomerWaited < level.ServingTimeExpectation + (level.MaximumTimeExpectation - level.ServingTimeExpectation) * 0.35f)
            result = 2;
        else if (timeCustomerWaited < level.ServingTimeExpectation + (level.MaximumTimeExpectation - level.ServingTimeExpectation) * 0.7f)
            result = 1;

        return result;
    }

    private bool levelCompleted = false;
    public GameObject LevelCompleteDialog = null;
    // Update is called once per frame
    void Update()
    {
        if (Time.time > NextSpawnTime)
        {
            if (CustomersSpawned < CurrentLevel.Orders.Length)
            {
                NextSpawnTime = Time.time + CurrentLevel.SpawnInterval;
                SpawnNewCustomer();
            }
        }

        for(int i = CustomersWaitingToOrder.Count - 1; i >= 0; i--)
        {
            CustomerUpdate(CustomersWaitingToOrder[i]);
        }

        for (int i = CustomersWaitingForDrink.Count - 1; i >= 0; i--)
        {
            CustomerUpdate(CustomersWaitingForDrink[i]);
        }

        // Check for end of level here
        if (CustomersSpawned >= CurrentLevel.Orders.Length && CustomersWaitingToOrder.Count == 0 && CustomersWaitingForDrink.Count == 0)
        {
            if (!levelCompleted)
            {
                levelCompleted = true;
                
                // Set our score for this level to be the score we got on this level
                GameState.Scores[GameState.LevelToPlay] = GameState.Money;

                LevelCompleteDialog.SetActive(true);

            }
        }
    }

    private void CustomerUpdate(Customer c)
    {
        c.SetEmotionState(CalculateTipAmount(c));

        // Time to leave the store
        if (Time.time > c.SpawnTime + CurrentLevel.MaximumTimeExpectation)
        {
            LeaveStoreAngry(c);
        }
    }

    private void LeaveStoreAngry(Customer c)
    {
        // Serve patron
        RemoveCustomerFromEitherList(c);

        c.GetComponent<NavMeshAgent>().SetDestination(CustomerExitPoint.position);

        Destroy(c.gameObject, 2.0f);

    }

    private void RemoveCustomerFromEitherList(Customer c)
    {
        if (CustomersWaitingToOrder.Contains(c))
            CustomersWaitingToOrder.Remove(c);

        if (CustomersWaitingForDrink.Contains(c))
            CustomersWaitingForDrink.Remove(c);

        // Update movement of all remaining waiting for orders
        foreach (Customer otherCustomer in CustomersWaitingForDrink)
        {
            UpdateNavDestinationForCustomer(otherCustomer);
        }

        foreach (Customer otherCustomer in CustomersWaitingToOrder)
        {
            UpdateNavDestinationForCustomer(otherCustomer);
        }

    }

    public bool TakeCustomerOrder()
    {
        if (CustomersWaitingToOrder.Count > 0)
        {
            // Identify a customer
            Customer customer = CustomersWaitingToOrder[0];

            // Create a drink order
            CustomerOrderParameters orderParameters = customerOrders.Next; // Get the next order in the marble bag of drink orders

            Drink drinkOrder = GetComponent<DrinkManager>().CreateNewDrink();

            customer.drinkOrder = drinkOrder;
            drinkOrder.drinkOrigination = DrinkRenderMode.Order;
            drinkOrder.HasBoba = orderParameters.hasBoba;
            drinkOrder.IceLevel = orderParameters.iceLevel;
            drinkOrder.TeaType = orderParameters.TeaType;
            drinkOrder.TeaLevel = 1.0f;
            drinkOrder.Sealed = true;

            GetComponent<DrinkManager>().SetDrinkRendererAnchor(drinkOrder, customer.gameObject, null);


            // Send this customer into the waiting for drink line
            customer.CurrentPhase = Customer.CustomerPhases.WaitingForOrder;
            CustomersWaitingToOrder.Remove(customer);
            CustomersWaitingForDrink.Add(customer);
            UpdateNavDestinationForCustomer(customer);

            // Update all the other ordering customers to make room
            foreach(Customer c in CustomersWaitingToOrder)
            {
                UpdateNavDestinationForCustomer(c);
            }

            return true;
        }
        return false;

    }

    public Customer ServeDrink(Drink drink)
    {
        Customer servedPatron = null;
        foreach(Customer candidate in CustomersWaitingForDrink)
        {
            if (drink.Matches(candidate.drinkOrder))
            {
                servedPatron = candidate;
                break;
            }
        }

        if (servedPatron != null)
        {
            // Serve patron
            CustomersWaitingForDrink.Remove(servedPatron);
            servedPatron.GetComponent<NavMeshAgent>().SetDestination(CustomerExitPoint.position);
            Destroy(servedPatron.gameObject, 2.0f);

            // Update movement of all remaining waiting for orders
            foreach(Customer c in CustomersWaitingForDrink)
            {
                UpdateNavDestinationForCustomer(c);
            }

            return servedPatron;
        }

        return null;
    }

    public void SpawnNewCustomer()
    {
        
        GameObject chosenPrefab = CustomerPrefabs[Random.Range(0, CustomerPrefabs.Length)];

        GameObject newCustomer = GameObject.Instantiate(chosenPrefab, CustomerSpawnPoint.position, CustomerSpawnPoint.rotation);

        Customer customer = newCustomer.GetComponent<Customer>();
        customer.CurrentPhase = Customer.CustomerPhases.WaitingToTakeOrder;
        customer.SpawnTime = Time.time;
        CustomersWaitingToOrder.Add(customer);

        UpdateNavDestinationForCustomer(customer);

        CustomersSpawned += 1;

    }

    public void UpdateNavDestinationForCustomer(Customer customer)
    {
        if (customer.CurrentPhase == Customer.CustomerPhases.WaitingToTakeOrder)
        {
            int indexOfCustomer = CustomersWaitingToOrder.IndexOf(customer);

            // Restrict to the available points
            if (indexOfCustomer >= CustomerOrderingPoints.Length)
                indexOfCustomer = CustomerOrderingPoints.Length - 1;

            customer.GetComponent<NavMeshAgent>().SetDestination(CustomerOrderingPoints[indexOfCustomer].position);
        }
        else if (customer.CurrentPhase == Customer.CustomerPhases.WaitingForOrder)
        {
            int indexOfCustomer = CustomersWaitingForDrink.IndexOf(customer);

            if (indexOfCustomer >= CustomerWaitingPoints.Length)
                indexOfCustomer = CustomerWaitingPoints.Length - 1;

            customer.GetComponent<NavMeshAgent>().SetDestination(CustomerWaitingPoints[indexOfCustomer].position);
        }
    }
}
