using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;
using static InputActionMaps;

public enum GameState { Starting, Match, End, Paused }
public class GameManager : MonoBehaviour
{
	public static GameManager GM { get; private set; }
	[SerializeField] ZoneRules zone;
	[SerializeField] Announcments announcements;
	[SerializeField] int minPlayerCount = 2;
	public GameState currentState { get; private set; }
	private Dictionary<int, PlayerController> players = new Dictionary<int, PlayerController>();
	private Dictionary<int, Tuple<string, int>> playerControlSchemes =
		new Dictionary<int, Tuple<string, int>>();
	private float restartMatchTime = 0;
	private ControlSchemeDetection controlSchemeDetector;

	void Awake()
	{
		if (GM != null)
		{
			GM.zone = zone;
			GM.announcements = announcements;
			Destroy(gameObject);
		}
		else
		{
			GM = this;
			DontDestroyOnLoad(gameObject);
		}
	}

	// void OnEnable()
	// {
	// 	SceneManager.sceneLoaded += OnSceneLoaded;
	// }

	// void OnDisable()
	// {
	// 	SceneManager.sceneLoaded -= OnSceneLoaded;
	// }

	// void OnSceneLoaded(Scene scene, LoadSceneMode mode)
	// {
	// 	StartControlAssignment();
	// }

	void Update()
	{
		if (restartMatchTime > 0)
		{
			restartMatchTime -= Time.deltaTime;
		}
		else if (currentState == GameState.End)
		{
			restartMatchTime = 0;
			StartNewMatch();
			enabled = false;
		}
	}

	// Start is called before the first frame update
	void Start()
	{
		if (currentState == GameState.End)
		{
			return;
		}
		currentState = GameState.Starting;
		controlSchemeDetector = new ControlSchemeDetection();
		controlSchemeDetector.ControlSchemeDetected += DetectPlayerControls;
		controlSchemeDetector.EnableDetection();
		announcements.AnnounceText("Choose your control schemes \n Each player press a button \n to start the match \n (see description for controls)");
	}

	void StartMatch()
	{
		if (zone != null)
		{
			zone.onPlayZoneExited += CheckLosingCondition;
		}
		if (announcements != null)
		{
			announcements.StopAnnouncement();
			announcements.QueueAnnounceText("Match Start", 1.6f);
		}
	}

	private void CheckLosingCondition(GameObject go)
	{
		if (!go.CompareTag("Player") || currentState == GameState.End)
			return;
		PlayerController pc = go.GetComponent<PlayerController>();
		currentState = GameState.End;
		zone.onPlayZoneExited -= CheckLosingCondition;
		announcements.AnnounceText(pc.playerName + " has lost the match", 2f);
		Destroy(go);
		restartMatchTime = 2.2f;
		enabled = true;
	}

	private void StartNewMatch()
	{
		string currentSceneName = SceneManager.GetActiveScene().name;
		players.Clear();
		SceneManager.LoadScene(currentSceneName);
		currentState = GameState.Starting;
	}

	public void RegisterPlayer(PlayerController pc)
	{
		if (!players.ContainsKey(pc.playerId))
		{
			players[pc.playerId] = pc;
		}
		DetectPlayerControls(this, new Tuple<string, int>("New Player", 0));
	}

	private void DetectPlayerControls(object eventCauser, Tuple<string, int> controlScheme)
	{
		if (players.Count < minPlayerCount)
		{
			return;
		}
		AssignPlayerControls(controlScheme);
		if (players.Count <= playerControlSchemes.Count)
		{
			if (players.All(pc => PlayerHasActiveControls(pc.Value)))
			{
				controlSchemeDetector.DisableDetection();
				currentState = GameState.Match;
				StartMatch();
			}
		}
	}

	private void AssignPlayerControls(Tuple<string, int> controlScheme)
	{
		foreach (int playerId in players.Keys)
		{
			PlayerController pc = players[playerId];
			if (PlayerHasActiveControls(pc))
			{
				continue;
			}
			if (playerControlSchemes.ContainsKey(playerId))
			{
				controlScheme = playerControlSchemes[playerId];
			}
			else if (playerControlSchemes.ContainsValue(controlScheme))
			{
				return;
			}
			if (controlScheme.Item1.Contains("Keyboard"))
			{
				pc.playerInput.SwitchCurrentControlScheme(controlScheme.Item1,
					InputSystem.GetDeviceById(controlScheme.Item2));
			}
			else if (controlScheme.Item1.Contains("Controller"))
			{
				pc.playerInput.SwitchCurrentControlScheme(controlScheme.Item1,
					InputSystem.GetDeviceById(controlScheme.Item2));
			}
			if (controlScheme.Item1 != "New Player")
			{
				playerControlSchemes[playerId] = controlScheme;
			}
		}
	}

	private bool PlayerHasActiveControls(PlayerController pc)
	{
		if (pc == null)
		{
			return false;
		}
		string currentScheme = pc.playerInput.currentControlScheme;
		if (currentScheme != null && currentScheme != "New Player")
		{
			return true;
		}
		return false;
	}
}

public class ControlSchemeDetection : IPlayerActions
{
	InputActionMaps actionMaps;
	public event EventHandler<Tuple<string, int>> ControlSchemeDetected;

	public ControlSchemeDetection()
	{
		actionMaps = new InputActionMaps();
		actionMaps.Player.SetCallbacks(this);
	}

	public void EnableDetection()
	{
		actionMaps.Player.Enable();
	}
	public void DisableDetection()
	{
		actionMaps.Player.Disable();
	}

	public void OnBoost(InputAction.CallbackContext context)
	{
		readControlScheme(context);
	}

	public void OnReset(InputAction.CallbackContext context)
	{
		readControlScheme(context);
	}

	public void OnSteer(InputAction.CallbackContext context)
	{
		readControlScheme(context);
	}

	public void OnThrust(InputAction.CallbackContext context)
	{
		readControlScheme(context);
	}

	public void readControlScheme(InputAction.CallbackContext context)
	{
		if (!context.canceled || context.performed)
		{
			return;
		}
		int bindingIndex = context.action.GetBindingIndexForControl(context.control);
		InputBinding binding = context.action.bindings[bindingIndex];
		string controlScheme = binding.groups.Split(';')[0];
		ControlSchemeDetected.Invoke(this, new Tuple<string, int>(controlScheme,
			context.control.device.deviceId));
	}
}