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

public enum ScoreType
{
    callCompleted = 0,
    callFailed = 1
}

public class IncomingConnectionController : MonoBehaviour {

    public static IncomingConnectionController instance;

    public Level [] levels;

    [ReadOnly] public Level levelBeingRun;

    [ReadOnly] public int currentlyRunningLevel;

    IEnumerator runningLevelRoutine;

    void Awake ()
    {
        if (instance == null)
        {
            instance = this;
        }
        else
        {
            Destroy(this);
        }
    }

    [ContextMenu("Run Next Level")]
    public bool RunNextLevel ()
    {
        currentlyRunningLevel++;
        if (currentlyRunningLevel - 1 < levels.Length)
        {
            if (currentlyRunningLevel == 1)
            {
                AudioManager.instance.Play("music");
                AudioManager.instance.Stop("music_opening");
            }
            if (levels [currentlyRunningLevel - 1] != null)
            {
                RunLevel(levels [currentlyRunningLevel - 1]);
                return true;
            }
        }
        else
        {
            Debug.LogError("NO Mo levels to run");
        }
        return false;
    }

    public void RunLevel (Level levelToRun)
    {
        SocketController.instance.ClearAllSockets();
        TopSocketController.instance.ClearAllSockets();

        if (runningLevelRoutine != null)
        {
            StopCoroutine(runningLevelRoutine);
            runningLevelRoutine = null;
        }
        runningLevelRoutine = runLevel(levelToRun);
        StartCoroutine(runningLevelRoutine);
    }

    public const float delayBeforeLevelStart = 4f;
    public const float timeToWaitBeforeRetryingToFindAvailableWire = 1f;

    IEnumerator runLevel (Level levelToRun)
    {
        if (levelToRun.numberOfWires < levelToRun.numberOfChannels)
        {
            Debug.LogError("Level has more channels than cables configured - not running");
            yield break;
        }

        levelBeingRun = levelToRun;
        StressLights.instance.Clear();
        Clock.instance.Clear();
        StressLights.instance.totalPossibleStress = levelToRun.levelStressCap;
        Clock.instance.SetCountDown(levelToRun.levelTimeLength);

        SocketChannelSettings [] channelsUsed = getRandomChannels(levelToRun.numberOfChannels);

        Dictionary<SocketChannelSettings, int> numberOfWiresAvailPerChannel = getNumberOfWiresPerChannel(channelsUsed, levelToRun.numberOfWires);
        Dictionary<SocketChannelSettings, int> numberOfWiresInUsePerChannel = initialiseDictionaryWithChannels(channelsUsed, 0);

        SocketController.instance.FillSocketsFromAvailable(numberOfWiresAvailPerChannel);

        //CursorBehaviour.instance.setCurrentState(GameState.handFree);

        float timeOfLastCall = Time.time;

        int numberOfCallsLeftInLevel = levelToRun.totalCallsToReceive;
        while (numberOfCallsLeftInLevel > 0)
        {
            while (Time.time - timeOfLastCall < delayBeforeLevelStart)
            {
                yield return null;
            }
            if (Time.time - timeOfLastCall > levelToRun.avgIntervalBetweenConnections)
            {
                List<SocketChannelSettings> channelsWithSlotFree = getChannelsWithSlotFree(channelsUsed, numberOfWiresInUsePerChannel, numberOfWiresAvailPerChannel);
                while (channelsWithSlotFree.Count == 0)
                {
                    channelsWithSlotFree = getChannelsWithSlotFree(channelsUsed, numberOfWiresInUsePerChannel, numberOfWiresAvailPerChannel);
                    yield return new WaitForSeconds(timeToWaitBeforeRetryingToFindAvailableWire);
                }

                SocketChannelSettings channelToReceiveCall = channelsWithSlotFree [Random.Range(0, channelsWithSlotFree.Count)];
                TopSocketController.instance.setCallReceived(channelToReceiveCall, levelToRun.avgConnectionDuration);
                timeOfLastCall = Time.time;
                numberOfCallsLeftInLevel--;
            }
            else
            {
                yield return null;
            }
        }

        levelBeingRun = null;
        StartCoroutine(playNextLevel());
    }

    IEnumerator playNextLevel ()
    {
        FadeCanvas.instance.setFade(true);
        float timeAtStart = Time.time;
        while (Time.time - timeAtStart < 3f)
        {
            yield return null;
        }
        if (RunNextLevel())
        {
            FadeCanvas.instance.setFade(false);
        }
        else
        {
            WonGame();
            SocketController.instance.ClearAllSockets();
            TopSocketController.instance.ClearAllSockets();
        }
    }

    public SocketChannelSettings [] getRandomChannels (int numberOfChannelToGrab)
    {
        List<SocketChannelSettings> socketChannelPool = new List<SocketChannelSettings>();
        socketChannelPool = HayWiresSettings.socketSettings.availableChannels.ToList();

        List<SocketChannelSettings> channelsToUse = new List<SocketChannelSettings>();

        while (numberOfChannelToGrab > 0)
        {
            if (socketChannelPool.Count > 0)
            {
                SocketChannelSettings channelToGrab = socketChannelPool [Random.Range(0, socketChannelPool.Count)];
                while (channelsToUse.Contains(channelToGrab))
                {
                    channelToGrab = socketChannelPool [Random.Range(0, socketChannelPool.Count)];
                }
                channelsToUse.Add(channelToGrab);
                socketChannelPool.Remove(channelToGrab);
                numberOfChannelToGrab--;
            }
            else
            {
                numberOfChannelToGrab = 0;
            }
        }

        return channelsToUse.ToArray();
    }

    public Dictionary<SocketChannelSettings, int> getNumberOfWiresPerChannel (SocketChannelSettings [] channelsArray, int totalWires)
    {
        Dictionary<SocketChannelSettings, int> numberOfWiresPerChannel = initialiseDictionaryWithChannels(channelsArray, 1);

        for (int j = 0; j < totalWires - channelsArray.Length; j++)
        {
            SocketChannelSettings randomChannel = channelsArray [Random.Range(0, channelsArray.Length)];
            numberOfWiresPerChannel [randomChannel]++;
        }
        return numberOfWiresPerChannel;
    }

    public Dictionary<SocketChannelSettings, int> initialiseDictionaryWithChannels (SocketChannelSettings [] channelsArray, int initValue)
    {
        Dictionary<SocketChannelSettings, int> numberOfWiresPerChannel = new Dictionary<SocketChannelSettings, int>();
        for (int i = 0; i < channelsArray.Length; i++)
        {
            numberOfWiresPerChannel.Add(channelsArray [i], initValue);
        }
        return numberOfWiresPerChannel;
    }

    public List<SocketChannelSettings> getChannelsWithSlotFree (SocketChannelSettings [] channelsUsed, Dictionary<SocketChannelSettings, int> numberOfWiresInUsePerChannel, Dictionary<SocketChannelSettings, int> numberOfWiresAvailPerChannel)
    {
        List<SocketChannelSettings> channelsWithSlotFree = new List<SocketChannelSettings>();
        for (int i = 0; i < channelsUsed.Length; i++)
        {
            if (numberOfWiresInUsePerChannel [channelsUsed [i]] < numberOfWiresAvailPerChannel [channelsUsed [i]])
            {
                channelsWithSlotFree.Add(channelsUsed [i]);
            }
        }
        return channelsWithSlotFree;
    }

    public void setScoreType (ScoreType typeToScore)
    {
        if (levelBeingRun != null)
        {
            if (typeToScore == ScoreType.callCompleted)
            {
                StressLights.instance.addStress(levelBeingRun.stressChangeForCallSuccess);
            }
            else if (typeToScore == ScoreType.callFailed)
            {
                StressLights.instance.addStress(levelBeingRun.stressChangeForCallFail);
            }
        }
    }

    public void LostGame ()
    {
        FadeCanvas.instance.setFade(true);
        StartCoroutine(RunFailedScreen());
    }


    public void WonGame ()
    {
        FadeCanvas.instance.setFade(true);
        StartCoroutine(RunWonScreen());
    }

    IEnumerator RunWonScreen ()
    {
        yield return new WaitForSeconds(2f);

        SocketController.instance.ClearAllSockets();
        TopSocketController.instance.ClearAllSockets();
        StressLights.instance.Clear();
        Clock.instance.Clear();
        currentlyRunningLevel = 0;

        FadeCanvas.instance.showWIN();
        AudioManager.instance.Play("connection_successful");
        yield return new WaitForSeconds(2f);
        FadeCanvas.instance.setFade(false);
    }

    IEnumerator RunFailedScreen ()
    {
        yield return new WaitForSeconds(2f);

        SocketController.instance.ClearAllSockets();
        TopSocketController.instance.ClearAllSockets();
        StressLights.instance.Clear();
        Clock.instance.Clear();
        currentlyRunningLevel = 0;

        FadeCanvas.instance.showFired();
        AudioManager.instance.Play("light_failed_spark");
        yield return new WaitForSeconds(2f);
        FadeCanvas.instance.setFade(false);
    }
}
