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

public enum CameraClass
{
    Single, BlackWhite, Color
}

public enum CPUClass
{
    Basic, Advanced
}

public enum Tool
{
    Cable, FusionReactor, Hammer, Grabbers, Refueler
}

public class DroneController : MonoBehaviour {

    /// <summary>
    /// units per second
    /// </summary>
    public float speed = 0.2f;

    /// <summary>
    /// degrees per second
    /// </summary>
    public float turnSpeed = 20f;

    [Header("Creation")]
    public CPUClass cpu;
    public CameraClass camClass;
    public Tool tool;
    public int jouleCapacity;
    public int joules;
    public GameObject[] toolPrefabs;
    
    public bool canStopActions { get { return cpu == CPUClass.Advanced; } }
    public bool canPickup { get { return tool == Tool.Grabbers; } }
    public bool canToggleLight { get { return cpu == CPUClass.Advanced; } }

    public float colorfulness
    {
        get
        {
            switch (camClass)
            {
                case CameraClass.Single:
                    return 0.25f;
                case CameraClass.BlackWhite:
                    return 0f;
                case CameraClass.Color:
                    return 0.4f;
                default:
                    return 0f;
            }
        }
    }

    public float darkness
    {
        get
        {
            switch (camClass)
            {
                case CameraClass.Single:
                    return 0.3f;
                case CameraClass.BlackWhite:
                    return 0.7f;
                case CameraClass.Color:
                    return 0.5f;
                default:
                    return 0f;
            }
        }
    }

    public bool stutteredRender { get { return camClass == CameraClass.Single; } } 

    public bool isDoing { get; private set; }
    public Queue<Action> actionQueue;
    public Interactible holding
    {
        get { return _holding; }
        set
        {
            // remove current holding
            if (_holding != null)
            {
                _holding.transform.SetParent(world.transform);
                _holding.transform.position += transform.rotation * Vector3.forward;
                _holding.transform.position -= new Vector3(0, 0.5f, 0);
            }
            if (value != null)
            {
                value.transform.SetParent(transform);
                value.transform.position -= transform.rotation * Vector3.forward;
                value.transform.position += new Vector3(0, 0.5f, 0);
            }
            _holding = value;
        }
    }

    private Interactible _holding;

    public static DroneController active
    {
        get { return _active; }
        set
        {
            if (_active == value)
                return;
            var prev = _active;
            _active = value;
            if (value != null && OutputController.instance != null)
                OutputController.instance.Log("now controlling " + value.gameObject.name, OutputController.MessageType.Output);
            Game.instance.OnDroneSelect(prev, value);
        }
    }

    private static DroneController _active;

    public World world
    {
        get
        {
            return Game.instance.worlds[World.GetWorldIndex(transform.position)].world;
        }
    }

    public int worldIndex
    {
        get
        {
            return World.GetWorldIndex(transform.position);
        }
    }

	void Start () {
        isDoing = false;
        actionQueue = new Queue<Action>();
	}

    public static DroneController Create(GameObject prefab, string name, CameraClass camClass, CPUClass cpu, Tool tool,
        int batteryCapacity, int batteryFill)
    {
        var cost = GetCost(camClass, cpu, tool, batteryCapacity, batteryFill);

        if (cost > AC.instance.joules)
        {
            OutputController.instance.Log("Not enough entropy available", OutputController.MessageType.Error);
            return null;
        }

        var tile = Game.instance.GetDronePlacementTile();
        if (tile == -1)
        {
            OutputController.instance.Log("All spaces for drones occupied", OutputController.MessageType.Error);
            return null;
        }
        var world = Game.instance.GetDronePlacementWorld();
        var pos = world.GetPosition(tile);

        var go = Instantiate(prefab);
        go.transform.position = pos;
        go.name = name;

        var dc = go.GetComponentInChildren<DroneController>();
        dc.camClass = camClass;
        dc.cpu = cpu;
        dc.tool = tool;
        dc.jouleCapacity = batteryCapacity;
        dc.joules = batteryFill;

        var toolPrefab = dc.toolPrefabs[(int)tool];
        var tgo = Instantiate(toolPrefab);
        tgo.transform.SetParent(dc.transform);
        tgo.transform.localPosition = Vector3.zero;

        AC.instance.joules -= cost;

        return dc;
    }

    public static int GetCost(CameraClass camClass, CPUClass cpu, Tool tool, int batteryCapacity, int batteryFill)
    {
        // TODO
        return batteryCapacity / 2 + batteryFill;
    }
	
	void Update () {
		if (!isDoing && actionQueue.Count > 0)
        {
            isDoing = true;
            var action = actionQueue.Dequeue();
            if (action.cost > joules)
            {
                OutputController.instance.Log("Local entropy too high to act. More joules required.", OutputController.MessageType.Error);
                isDoing = false;
            }
            else
            {
                joules -= action.cost;
                StartCoroutine(action.Execute(this));
            }
        }
	}

    public void StopAction()
    {
        if (!canStopActions)
        {
            OutputController.instance.Log("This drone cannot interrupt its actions.", OutputController.MessageType.Error);
            return;
        }
        this.actionQueue.Clear();
        FinishAction();
    }

    /// <summary>
    /// called only by Actions when they're done
    /// </summary>
    public void FinishAction()
    {
        isDoing = false;
    } 

    /// <summary>
    /// called only by actions when they fail
    /// </summary>
    /// <param name="refund">if -1, gives a full refund</param>
    public void FailAction(Action action, string error, int refund = -1)
    {
        if (refund == -1)
            refund = action.cost;
        joules += refund;
        OutputController.instance.Log(error, OutputController.MessageType.Error);
        FinishAction();
    }
    
    public void Enqueue(Action action)
    {
        actionQueue.Enqueue(action);
    }
}
