﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace Tap.Tilt
{
    // Use to control a collection of player objects in the scene
    [RequireComponent(typeof(SocketIONetwork))]
    [RequireComponent(typeof(GameData))]
    public class GameController : MonoBehaviour
    {
        // a static reference to this object
        public static GameController instance;

        // The objects to use as the representative player
        public GameObject player;

        // The available player colors
        public Color[] playerColors;

        // The available player symbols
        public string[] playerSymbols;

        // The container to spawn the users into
        public GameObject spawnContainer;

        // The Canvas representation of the player
        public PlayerCanvasController playerCanvas;

        // The winner slot
        public PlayerCanvasController winnerCanvas;

        // The canvas container to spawn the playerCanvas into
        public GameObject playerList;

        // Post Game Delay to show scores before reset occurs
        public float postGameDelay = 5f;

        // The players in the game
        private List<PlayerController> players;

        // The current color index assigned
        private int colorIndex = 0;

        // The current symbol index assigned
        private int symbolIndex = 0;

        // The SocketIO Network Module
        private SocketIONetwork sion;

        // The GameData to use for this instance
        [HideInInspector]
        public GameData gameData;

        // For startup
        private void Awake()
        {
            // Assign instance to this if it's null
            instance = instance ? instance : this;

            // Create color collection if none exists
            if (playerColors.Length == 0)
                playerColors = new Color[] { Color.cyan, Color.yellow, Color.red, Color.green, Color.magenta };

            // Create symbol collection if none exists
            if (playerSymbols.Length == 0)
                playerSymbols = new string[] { "@", "#", "$", "%", "&", "!", "?", "*" };

            players = new List<PlayerController>();

            sion = GetComponent<SocketIONetwork>();
            gameData = GetComponent<GameData>();
        }

        // Call to join a user to the scene
        private PlayerController UserJoin(string socketId)
        {
            // Create a new user object for the new user
            GameObject thisPlayer = Instantiate(player, spawnContainer.transform) as GameObject;

            // Also create a new canvas object for the new user
            PlayerCanvasController canvasPlayer = Instantiate(playerCanvas, playerList.transform) as PlayerCanvasController;
            canvasPlayer.SetId(socketId);

            // Assign the PlayerController in the player to the player list
            PlayerController pc = thisPlayer.GetComponent<PlayerController>();
            if (pc)
            {
                // Identify this user
                pc.socketId = socketId;
                pc.playerCanvas = canvasPlayer;
                players.Add(pc);
            }

            // Get a color for the user
            ColorThings ct = thisPlayer.GetComponent<ColorThings>();
            if (ct != null)
                ct.DoColor(playerColors[colorIndex]);

            // Record the color which was set
            pc.color = playerColors[colorIndex];

            // Increment the color
            colorIndex++;
            if (colorIndex == playerColors.Length)
                colorIndex = 0;

            // Get a symbol for the user
            pc.symbolContainer.text = pc.symbol = playerSymbols[symbolIndex];

            // increment the symbols
            symbolIndex++;
            if (symbolIndex == playerSymbols.Length)
                symbolIndex = 0;

            // Send the event to the user telling them the color they are
            sion.SendEvent(pc.socketId, "color", "#" + ColorUtility.ToHtmlStringRGB(pc.color));

            // Send the event to the user telling them their symbol
            sion.SendEvent(pc.socketId, "symbol", pc.symbol);

            // Send color and symbol to the canvas
            canvasPlayer.SetColor(pc.color);
            canvasPlayer.SetSymbol(pc.symbol);

            return pc;
        }

        // User is calling to reset their alignment
        public void UserReset(string socketId)
        {
            // find the user in the List
            PlayerController thisPlayer = players.Find(rec => {
                Debug.Log("User Reset" + rec.ToString() + rec.socketId + " / " + socketId);
                return rec.Equals(socketId);
            });

            if (thisPlayer == null)
            {
                Debug.Log("exit thisPlayer is null " + socketId);
                return;

            }

            thisPlayer.Recenter();
        }

        // User is calling to find their icon
        public void UserPing(string socketId)
        {
            // find the user in the List
            PlayerController thisPlayer = players.Find(rec => {
                Debug.Log("User Ping" + rec.ToString() + rec.socketId + " / " + socketId);
                return rec.Equals(socketId);
            });

            if (thisPlayer == null)
            {
                Debug.Log("exit thisPlayer is null " + socketId);
                return;

            }

            thisPlayer.Ping();
        }

        // Call to exit a user from the scene
        public void UserExit(string socketId)
        {
            // find the user in the List
            PlayerController thisPlayer = players.Find(rec => {
                // Debug.Log("rec" + rec.ToString() + rec.socketId + " / " + socketId);
                return rec.Equals(socketId);
            });

            if (thisPlayer == null)
            {
                Debug.Log("exit thisPlayer is null " + socketId);
                return;

            }

            // remove the user from the list
            players.Remove(thisPlayer);

            // Remove the canvas button
            GameObject.Destroy(thisPlayer.playerCanvas.gameObject);

            // Remove the player object
            Destroy(thisPlayer.gameObject);
        }

        // Call to control a user in the scene
        public void UserControl(ControlData controlData)
        {
            // Debug.Log("UserControl " + JsonUtility.ToJson(controlData).ToString());
            // Get the player in the list with that controller id
            PlayerController thisPlayer = players.Find(rec => rec.socketId == controlData.i);

            // If there is no player in the list with that id, then add one
            if (thisPlayer == null)
            {
                thisPlayer = UserJoin(controlData.i);
            }

            // Apply the controls to that player
            if (thisPlayer != null)

                if (controlData.type == "tilt")
                {
                    thisPlayer.Tilt(Quaternion.Euler(
                        -controlData.tilt.x,
                        -controlData.tilt.z,
                        -controlData.tilt.y));
                }
                else
                    thisPlayer.Touch(controlData);
        }

        // Send all the users a forward command
        public void ForwardUsers()
        {
            foreach (PlayerController player in players)
            {
                UserVibe(player.socketId, 500);
                UserUrl(player.socketId, gameData.redirectUrl);
            }
        }

        // Send user to url
        public void UserUrl(string ConnectionId, string url)
        {
            if (url == "")
                url = gameData.redirectUrl;

            sion.SendEvent(ConnectionId, "forward", url);
        }

        // Send user into timeout
        public void UserTimeout(string ConnectionId, string url)
        {
            if (url == "")
                url = gameData.timeoutUrl;
            Debug.Log("Timeout User: " + ConnectionId);
            UserVibe(ConnectionId, 50);
            sion.SendEvent(ConnectionId, "timeout", "30000");
        }
        public void UserVibe(string ConnectionId, float vibeTime)
        {
            sion.SendEvent(ConnectionId, "vibe", vibeTime.ToString());
        }

        public void UserPoints(string socketId, int points)
        {
            foreach(PlayerController thisPlayer in players)
                if (thisPlayer.socketId == socketId)
                    thisPlayer.playerCanvas.SendPoints(points);
        }

        public void Reset()
        {
            foreach (PlayerController thisPlayer in players)
                thisPlayer.playerCanvas.Reset();
        }

        public void EndGame()
        {
            if (winnerCanvas == null)
                return;

            winnerCanvas.Reset();

            // Find the winner
            PlayerController winner = null;
            int highScore = 0;

            foreach (PlayerController player in players)
                if (player.playerCanvas.totalPoints > highScore)
                {
                    winner = player;
                    highScore = player.playerCanvas.totalPoints;
                }


            // Show the winner in the endgame panel
            if (winner != null)
            {
                winnerCanvas.SetSymbol(winner.symbol);
                winnerCanvas.SendPoints(winner.playerCanvas.totalPoints);
                winnerCanvas.SetColor(winner.color);
            }

            StartCoroutine(ResetAfter());
        }

        // Wait postGameDelay seconds and then reset everyone's point totals
        private IEnumerator ResetAfter ()
        {
            yield return new WaitForSeconds(postGameDelay);
            Reset();
        }
    }

}

