using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using Palmmedia.ReportGenerator.Core.Reporting.Builders;
using UnityEditor.Experimental.Licensing;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Users;
using UnityEngine.InputSystem.Utilities;
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, string> playerControlSchemes = new Dictionary<int, string>();
	private float restartMatchTime = 0;
	private ControlSchemeDetection controlSchemeDetector;

	void Awake()
	{
		if (GM == null)
		{
			GM = this;
		}
	}

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

	// Start is called before the first frame update
	void Start()
	{
		currentState = GameState.Starting;
		controlSchemeDetector = new ControlSchemeDetection();
		controlSchemeDetector.ControlSchemeDetected += AssignPlayerControls;
		controlSchemeDetector.EnableDetection();
		announcements.AnnounceText("You both press a key \n on your controll scheme of choice \n to start the match");
		enabled = false;
	}

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

	private void CheckLosingCondition(GameObject go)
	{
		if (!go.CompareTag("Player") || currentState == GameState.End)
			return;
		currentState = GameState.End;
		Destroy(go);
		announcements.AnnounceText(go.name + " has lost the match", 2f);
		restartMatchTime = 2.2f;
		players.Clear();
		enabled = true;
	}

	private void StartNewMatch()
	{
		string currentSceneName = SceneManager.GetActiveScene().name;
		SceneManager.LoadScene(currentSceneName);
	}

	public void RegisterPlayer(PlayerController pc)
	{
		if (!players.ContainsKey(pc.instanceID))
		{
			players[pc.instanceID] = pc;
		}
	}

	private void AssignPlayerControls(object sender, string controlScheme)
	{
		if (players.Count < minPlayerCount)
		{
			return;
		}
		if (players.Count <= playerControlSchemes.Count)
		{
			controlSchemeDetector.DisableDetection();
			currentState = GameState.Match;
			StartMatch();
			return;
		}

		foreach (int playerId in players.Keys)
		{
			if (playerControlSchemes.ContainsValue(controlScheme))
			{
				return;
			}
			if (playerControlSchemes.ContainsKey(playerId))
			{
				continue;
			}
			PlayerController pc = players[playerId];
			if (controlScheme.Contains("Keyboard"))
			{
				pc.playerInput.SwitchCurrentControlScheme(controlScheme, Keyboard.current);
			}
			else if (controlScheme.Contains("Controller"))
			{
				pc.playerInput.SwitchCurrentControlScheme(controlScheme, Gamepad.current);
			}
			playerControlSchemes[playerId] = controlScheme;
		}
	}
}

public class ControlSchemeDetection : IPlayerActions
{
	InputActionMaps actionMaps;
	public event EventHandler<string> 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)
	{
		int bindingIndex = context.action.GetBindingIndexForControl(context.control);
		InputBinding binding = context.action.bindings[bindingIndex];
		string controlScheme = binding.groups.Split(';')[0];
		ControlSchemeDetected.Invoke(this, controlScheme);
	}
}