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

public struct CommandInput
{
	public int playerId;
	public string input;
}

public class Game : MonoBehaviour
{
    public static Game CurrentGame;

    [Header("Settings")]
    public int TeamMemberCount = 2;
    protected float SlowmoDuration = 5f;
    protected float NormalSpeedDuration = 1f;

    [Header("Prefabs")]
    public GameObject MemberPrefab;

    [Header("Generated @ Runtime")]
    public List<Player> Players = new List<Player>();

    [Header("References")]
    public GameObject TeamMemberParent;
    public CinemachineTargetGroup CinTargetGroup;
    public AudioSource Soundtrack;

    [Header("Slowmo stuff")]
    float slowEasePoint = 0.95f;
    float normalEasePoint = 0.98f;

    float slowmoSpeed = 0.15f;
    float defaultPhysicsTimestep = 0.02f; // don't change this :P
    float slowmoPitch = 0.98f;
    float normalPitch = 1f;

    public Commands GameCommands;
    protected List<CommandInput> InputBuffer = new List<CommandInput>();
	public int myPlayerId { get; protected set; }

	[SerializeField]
	Transform scoreBoard;

	[SerializeField]
	ScoreHud scoreHudPrefab;

	[SerializeField]
	CommandLine commandLine;

	public void AddInput(CommandInput input)
	{
		if (NetworkCommandRunner.CurrentCommandRunner.isServer)
		{
			InputBuffer.Add(input);
		}
		else
		{
			NetworkCommandRunner.CurrentCommandRunner.AddCommand(input);
		}
	}

    void Awake()
    {
        CurrentGame = this;
        GameCommands = GetComponentInChildren<Commands>();
        CinTargetGroup = TeamMemberParent.GetComponentInChildren<CinemachineTargetGroup>();
    }

    public void InitCommandRunner()
    {
        StartCoroutine(CommandRunner());
		//StartCoroutine (AICommandWriter ());
    }

    public void CreateGame()
    {
		myPlayerId = NetworkCommandRunner.CurrentCommandRunner.isServer ? 0 : 1;

		string myName = "Team " + (myPlayerId + 1);

		var players = GetComponents<Player>();

		for(int i = 0; i < players.Count(); ++i)
        {
			var p = players[i];

            Players.Add(p);
            p.myTeam = new Team(p, TeamMemberCount);
			p.scoreHud = Instantiate(scoreHudPrefab);
			p.scoreHud.transform.SetParent(scoreBoard, false);
			p.scoreHud.Init("Team " + (i + 1));
        }

        AddMembersAsCineTargets();
		commandLine.Init(myName);
        Soundtrack.Play();
    }

    void AddMembersAsCineTargets()
    {
        List<CinemachineTargetGroup.Target> memberTargets = new List<CinemachineTargetGroup.Target>();
        
        foreach(var m in Member.AllMembers)
        {
            CinemachineTargetGroup.Target newTarget = new CinemachineTargetGroup.Target();
            newTarget.target = m.Value.transform;
            newTarget.radius = 0;
            newTarget.weight = 1;

            memberTargets.Add(newTarget);
        }

        CinTargetGroup.m_Targets = memberTargets.ToArray();
    }

    public IEnumerator SlowMoTime()
    {
        Debug.Log("slowmo speed!");
        float slowT = 0;

        if(NetworkCommandRunner.CurrentCommandRunner.isServer)
            NetworkCommandRunner.CurrentCommandRunner.SlowMoClient();

        // slowmo
        while(slowT < SlowmoDuration)
        {
            // normal slowmo
            if(slowT < SlowmoDuration * slowEasePoint)
            {
                Time.timeScale = slowmoSpeed;
                Time.fixedDeltaTime = defaultPhysicsTimestep * slowmoSpeed;
                Soundtrack.pitch = slowmoPitch;
            }
            // ease into regular speed
            else
            {
                float BaseEaseT = (SlowmoDuration * slowEasePoint);
                float percentageComplete = (slowT - BaseEaseT)  / (SlowmoDuration  - BaseEaseT);
                Time.timeScale = Mathf.Lerp(slowmoSpeed, 1f, percentageComplete);
                Time.fixedDeltaTime = Mathf.Lerp(defaultPhysicsTimestep * slowmoSpeed, defaultPhysicsTimestep, percentageComplete);
                Soundtrack.pitch = Mathf.Lerp(slowmoPitch, normalPitch, percentageComplete);
            }

            slowT += Time.unscaledDeltaTime;
            yield return 0;
        }
    }

    public IEnumerator NormalTime()
    {
        Debug.Log("normal speed!");
        float normalT = 0;

        if(NetworkCommandRunner.CurrentCommandRunner.isServer)
            NetworkCommandRunner.CurrentCommandRunner.NormalSpeedClient();

        // slowmo
        while(normalT < SlowmoDuration)
        {
            // normal speed
            if(normalT < NormalSpeedDuration * normalEasePoint)
            {
                Time.timeScale = 1f;
                Time.fixedDeltaTime = defaultPhysicsTimestep;
                Soundtrack.pitch = normalPitch;
            }
            // ease into slowmo speed
            else
            {
                float BaseEaseT = (NormalSpeedDuration * normalEasePoint);
                float percentageComplete = (normalT - BaseEaseT)  / (NormalSpeedDuration  - BaseEaseT);
                Time.timeScale = Mathf.Lerp(1f, slowmoSpeed, percentageComplete);
                Time.fixedDeltaTime = Mathf.Lerp(defaultPhysicsTimestep, defaultPhysicsTimestep * slowmoSpeed, percentageComplete);
                Soundtrack.pitch = Mathf.Lerp(normalPitch, slowmoPitch, percentageComplete);
            }

            normalT += Time.unscaledDeltaTime;
            yield return 0;
        }
    }

    IEnumerator CommandRunner()
    {
		while (Member.AllMembers.Count == 0)
			yield return 0;

        while(true)
        {
            yield return SlowMoTime();

            while(InputBuffer.Count > 0)
            {
				var input = InputBuffer[0];

                string[] split = InputSplitter(input.input);
				string id = split[0];
				string[] parameters = split.Skip(1).ToArray();

				Command command = new Command
				{
					playerId = input.playerId,
					commandId = id,
					parameters = parameters
				};

                NetworkCommandRunner.CurrentCommandRunner.RunNetworkCommand(command);
                InputBuffer.RemoveAt(0);
            }

            yield return NormalTime();
        }
    }

	IEnumerator AICommandWriter()
	{
		while (Member.AllMembers.Count == 0)
			yield return 0;

		float commandMultipluer = 0.3f; // the higher, the more commands per turn

		while (true)
		{
			// destroy with fire. test code.
			string[] randDirections = { "left", "right", "down", "up" };
			string randDir = randDirections [UnityEngine.Random.Range (0, randDirections.Length)];
			Member randomMember = null;

			int randIndex = UnityEngine.Random.Range (0, Players [0].myTeam.Members.Count);
			int index = 0;
			foreach (var m in Players[0].myTeam.Members)
			{
				if (index == randIndex)
					randomMember = m.Value;
				index++;
			}

			AddInput(new CommandInput
			{
				playerId = myPlayerId,
				input = "move " + randomMember.Name + " " + randDir
			});

			yield return new WaitForSeconds (SlowmoDuration * commandMultipluer);
		}
	}

    public string[] InputSplitter(string inputString)
    {        
        return inputString.Split((char[])null, StringSplitOptions.RemoveEmptyEntries);
    }
}
