﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using Photon.Pun;
using Photon.Realtime;
using TMPro;
using PhotonHashtable = ExitGames.Client.Photon.Hashtable;

public class GameLauncher : MonoBehaviourPunCallbacks {

	public CloudRegion cloudRegion;

	string _gameVersion = "1.1";

	public GameObject testPlayer;
	public bool testPlay = false;

	public int maxPlayersPerRoom = 8;
	public int maxSpectatorsPerRoom = 1;
	public bool joinAsSpectator = false;
    public SpectatorController spectatorController;

	public int totalPlayers = 0;
	public int totalSpectators = 0;

	public TMP_InputField classIDField;
	public TMP_InputField spectateIDField;
	public TMP_Text joinErrorMessage;

	public CanvasManagerScript handler;
	public TrackManagerScript manager;
	public RacerLobbyUI racerLobbyUI;

	public float matchStartTime = -1f;

	public enum CloudRegion {
		USEast, USWest, Europe, CanadaEast
	}


	public Dictionary<CloudRegion, string> cloudRegionTokens = new Dictionary<CloudRegion, string>() {
		{ CloudRegion.USEast, "us" },
		{ CloudRegion.USWest, "usw" },
		{ CloudRegion.Europe, "eu" },
		{ CloudRegion.CanadaEast, "cae" },
	};

	void Awake() {
		if (!handler)
		{
			handler = FindObjectOfType<CanvasManagerScript>();
		}

		PhotonNetwork.AutomaticallySyncScene = true;

		string token = "us";
		if(cloudRegionTokens.TryGetValue(cloudRegion, out token))
			((ServerSettings)Resources.Load("PhotonServerSettings", typeof(ServerSettings))).AppSettings.FixedRegion = token;

	}

    private void Start()
    {
		if(!handler)
		{
			handler = FindObjectOfType<CanvasManagerScript>();
		}
	}

    public void Connect() {
		if (PhotonNetwork.IsConnected) {

			if (joinAsSpectator)
			{
				PhotonHashtable playerProperties = new PhotonHashtable() { { "IsSpectator", true }, { "DisplayName", PersistentData.localPlayerData.username } };
				PhotonNetwork.LocalPlayer.SetCustomProperties(playerProperties);
				PhotonHashtable expectedCustomRoomProperties = new PhotonHashtable() { { "spectator_slots_availabile", true } };
				PhotonNetwork.JoinRandomRoom(expectedCustomRoomProperties, (byte)(maxPlayersPerRoom + maxSpectatorsPerRoom));
			}
			else
			{
				PhotonHashtable playerProperties = new PhotonHashtable() { { "IsSpectator", false }, { "DisplayName", PersistentData.localPlayerData.username } };
				PhotonNetwork.LocalPlayer.SetCustomProperties(playerProperties);
				PhotonHashtable expectedCustomRoomProperties = new PhotonHashtable() { { "player_slots_availabile", true } };
				PhotonNetwork.JoinRandomRoom(expectedCustomRoomProperties, (byte)(maxPlayersPerRoom + maxSpectatorsPerRoom));
			}
			//PhotonNetwork.JoinRandomRoom();
		}
		else {
			((ServerSettings)Resources.Load("PhotonServerSettings", typeof(ServerSettings))).AppSettings.AppVersion = _gameVersion;
			//PhotonNetwork.GameVersion = _gameVersion;
			PhotonNetwork.ConnectUsingSettings();
		}
	}

	public override void OnConnectedToMaster() {
		Debug.Log("OnConnectedToMaster() was called by PUN");
		if (joinAsSpectator)
		{
			PhotonHashtable playerProperties = new PhotonHashtable() { { "IsSpectator", true }, { "DisplayName", PersistentData.localPlayerData.username } };
			PhotonNetwork.LocalPlayer.SetCustomProperties(playerProperties);
			PhotonHashtable expectedCustomRoomProperties = new PhotonHashtable() { { "spectator_slots_availabile", true } };
			PhotonNetwork.JoinRandomRoom(expectedCustomRoomProperties, (byte)(maxPlayersPerRoom + maxSpectatorsPerRoom));
		}
		else
		{
			PhotonHashtable playerProperties = new PhotonHashtable() { { "IsSpectator", false }, { "DisplayName", PersistentData.localPlayerData.username } };
			PhotonNetwork.LocalPlayer.SetCustomProperties(playerProperties);
			PhotonHashtable expectedCustomRoomProperties = new PhotonHashtable() { { "player_slots_availabile", true } };
			PhotonNetwork.JoinRandomRoom(expectedCustomRoomProperties, (byte)(maxPlayersPerRoom + maxSpectatorsPerRoom));
		}
	}


	public override void OnDisconnected(DisconnectCause cause) {
		Debug.LogWarningFormat("OnDisconnected() was called by PUN with reason {0}", cause);
		GameErrorLogger.WriteToLog("OnDisconnected() was called by PUN with reason " + cause);
	}

	public override void OnJoinRandomFailed(short returnCode, string message) {
		Debug.Log("OnJoinRandomFailed() was called by PUN. No random room available, so we create one.\nCalling: PhotonNetwork.CreateRoom");

		RoomOptions roomOptions = new RoomOptions();
		roomOptions.CustomRoomPropertiesForLobby = new string[] { "player_slots_availabile", "spectator_slots_availabile" };
		roomOptions.CustomRoomProperties = new PhotonHashtable() { { "player_slots_availabile", true }, { "spectator_slots_availabile", true } };
		roomOptions.MaxPlayers = (byte)(maxPlayersPerRoom + maxSpectatorsPerRoom);
		PhotonNetwork.CreateRoom(null, roomOptions);

		// #Critical: we failed to join a random room, maybe none exists or they are all full. No worries, we create a new room.
		//PhotonNetwork.CreateRoom(null, new RoomOptions { MaxPlayers = (byte)(maxPlayersPerRoom + maxSpectatorsPerRoom) });
		/*
		Debug.Log("OnJoinRandomFailed() was called by PUN. No random room available, so we create one.\nCalling: PhotonNetwork.CreateRoom");
		GameErrorLogger.WriteToLog("OnJoinRandomFailed() was called by PUN. No random room available, so we create one.\nCalling: PhotonNetwork.CreateRoom");
		// #Critical: we failed to join a random room, maybe none exists or they are all full. No worries, we create a new room.
		PhotonNetwork.CreateRoom(null, new RoomOptions { MaxPlayers = (byte)maxPlayersPerRoom });
		*/
	}

	public override void OnJoinedRoom() {
		Debug.Log("Region: " + PhotonNetwork.CloudRegion);
		Debug.Log("OnJoinedRoom() called by PUN. Now this client is in a room.");
		if (testPlay && testPlayer != null)
		{
			//GameObject.Instantiate (testPlayer, new Vector3 (0, 200, 0), Quaternion.identity);
		}
		else
		{
			if (PhotonNetwork.IsMasterClient)
			{
				//int randomSeed = gameGenerator.GenerateSeed();
				//gameGenerator.SendRandomSeed(randomSeed);
				Debug.Log("Player is Master Client");
			}
			if (!joinAsSpectator)
			{
				Debug.Log("Player is Racer");
				PhotonNetwork.Instantiate("Base Racer", manager.racerSpots.GetChild(PhotonNetwork.LocalPlayer.ActorNumber - 1).position, manager.trackStart.rotation);
				//PhotonNetwork.SendRate = 7;
			}
			else
			{
                if (spectatorController)
                {
                    spectatorController.enabled = true;
                    if (!spectatorController.VerifyDefaults()) { return; }
                    Debug.Log("Player is Spectator");
                    StartCoroutine("SetSpectatorTarget");
                    //PhotonNetwork.SendRate = 10;
                }
            }
			CountPeopleInRoom();
			Debug.Log ("Player Joined Room");
			manager.guiActive = true;
			//PhotonNetwork.SendRate = 7;
		}
	}

	public override void OnPlayerEnteredRoom(Player other)
	{
		CountPeopleInRoom();
	}

	public override void OnPlayerLeftRoom(Player other)
	{
		CountPeopleInRoom();
	}

	void CountPeopleInRoom()
	{
		object value;
		totalPlayers = 0;
		totalSpectators = 0;
		Dictionary<int, Player> players = PhotonNetwork.CurrentRoom.Players;
		foreach (KeyValuePair<int, Player> playerItem in players)
		{
			if (playerItem.Value.CustomProperties.TryGetValue("IsSpectator", out value))
			{
				if ((bool)value)
				{
					totalSpectators++;
				}
				else
				{
					totalPlayers++;
				}
			}
		}

		if (PhotonNetwork.IsMasterClient)
		{
			UpdateRoomSlotProperties();
		}
	}

	void UpdateRoomSlotProperties()
	{
		PhotonHashtable update = new PhotonHashtable { { "player_slots_availabile", totalPlayers < maxPlayersPerRoom }, { "spectator_slots_availabile", totalSpectators < maxSpectatorsPerRoom } };
		PhotonNetwork.CurrentRoom.SetCustomProperties(update);
		Debug.LogError("players count: " + totalPlayers);
		Debug.LogError("spectators count: " + totalSpectators);
		Debug.LogError("total in room: " + (totalSpectators + totalPlayers));
	}

	public void Submit(bool joinAsSpectator)
	{
		ToggleJoinAsSpectator(joinAsSpectator);
		if (joinAsSpectator)
		{
			_gameVersion = spectateIDField.text.ToUpper() + "_Esports";
            if (!spectatorController)
            {
                spectatorController = FindObjectOfType<SpectatorController>();
            }
            if (spectatorController) { Debug.Log("Spectating!"); }
        }
		else
		{
			_gameVersion = classIDField.text.ToUpper() + "_Esports";
		}
		print (_gameVersion);
		if(_gameVersion.Length < 7) {
			joinErrorMessage.gameObject.SetActive(true);
			return;
		}
		joinErrorMessage.gameObject.SetActive(false);
		((ServerSettings)Resources.Load("PhotonServerSettings", typeof(ServerSettings))).AppSettings.AppVersion = _gameVersion;
		//PhotonNetwork.GameVersion = _gameVersion;
		Connect();
		if (joinAsSpectator)
		{
			// True because spectator exists
			handler.SetInLobbyCanvasActive(true);
		}
		else
		{
			handler.SetInLobbyCanvasActive(false);
		}
	}

	public void QuickplaySubmit()
	{
		_gameVersion = "RKR Random Match";
		joinErrorMessage.gameObject.SetActive(false);
		((ServerSettings)Resources.Load("PhotonServerSettings", typeof(ServerSettings))).AppSettings.AppVersion = _gameVersion;
		Connect();
		handler.SetInLobbyCanvasActive(false);
	}

	public float GetMatchStartTime() {
		return matchStartTime;
	}

	public void StartMatch() {
		if (PhotonNetwork.IsMasterClient) {
			SceneManager.LoadScene (manager.selectedLevel);
		}
	}

	// Should only happen if the host is by themselves
	public void ResetMatchStartTime () {
		matchStartTime = -1f;
		PhotonGameDebugger.IncrementNumberOfMessages("match start time");
		photonView.RPC("SetMatchStartTime", RpcTarget.AllBuffered, -1f, false);
		Debug.LogError ("hit reset");
	}

	[PunRPC]
	public void SetMatchStartTime (float sendTime, float startTime, bool addTime) {

		Debug.LogError ("Photon Time: " + PhotonNetwork.Time.ToString () + ", Start Time: " + startTime.ToString ());

		matchStartTime = startTime;

		if (startTime == -1f) {
			racerLobbyUI.cancelCountdown = true;
			Debug.LogError ("Canceled 01");
		} else if (addTime) {
			Debug.LogError ("Adding time");
		} else if (sendTime < startTime) {
			Debug.LogError ("Run Countdown 01");
			StartCoroutine (racerLobbyUI.CountdownToStart ());
		} else if (sendTime >= startTime) {
			Debug.LogError ("Failed");
		}
	}

	[PunRPC]	
	public void BeginMatch (float beginTime) {
		matchStartTime = beginTime;
		if (PhotonNetwork.Time < beginTime) {
			Debug.LogError ("Run Countdown 02");
			StartCoroutine (racerLobbyUI.BeginMatchCountdown ());
		}
	}

	public void ToggleJoinAsSpectator(bool _joinAsSpectator)
	{
		joinAsSpectator = _joinAsSpectator;
	}

	IEnumerator SetSpectatorTarget()
	{
		KartController[] karts = KartController.FindObjectsOfType<KartController>();

		while (karts.Length <= 0)
		{
			yield return null;

            karts = KartController.FindObjectsOfType<KartController>();
        }

		if (spectatorController)
        {
            spectatorController.SpectateAnyPlayer();
        }
	}
}

