using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Mono.Data.Sqlite;
using System.Data;
using System;
using UnityEngine.Networking;
using System.IO;
using System.Text;

public class OnlineDatabase : MonoBehaviour {
    private const string onlineQuestionsUrl = "nordh.xyz/narKampen/dbFiles/Question.php";
    private const string serverUrl = "nordh.xyz/narKampen/dbFiles/";
    string gameMode;
    int winAmount = -1;

    public GameObject questionCardPrefab;

    private static OnlineDatabase instance;
    public static OnlineDatabase Instance { get { return instance; } }

    private void Awake() {
        if (instance == null) {
            instance = this;
        }
    }

    internal List<CategoryPanel.Category> GetCategories(List<CategoryPanel.Category> list, int gameId) {
        WWWForm form = new WWWForm();
        form.AddField(Constants.GAME_ID, gameId);

        string response = CallOnlineDatabaseWithResponse("Categories.php", form);

        response = "{\"categoryList\" :  " + response + " }";

        Categories categories = new Categories();
        JsonUtility.FromJsonOverwrite(response, categories);

        foreach (Category c in categories.categoryList) {
            CategoryPanel.Category cat = new CategoryPanel.Category();

            cat.color = new Color32((byte)c.r, (byte)c.g, (byte)c.b, (byte)c.a);
            cat.name = c.name;
            cat.id = c.id;
            cat.questionCount = c.count;

            list.Add(cat);
        }
        return list;
    }

    internal int SetupNewOnlineGame(int limitPerQuestion, int limitPerPlayer, int toWin, List<InviteSearchResult> inviteUsers, List<int> selectedCategories) {
        List<int> playerIds = new List<int>();
        inviteUsers.ForEach(i => playerIds.Add(i.GetId()));

        List<string> playerNames = new List<String>();
        inviteUsers.ForEach(i => playerNames.Add(i.GetName()));

        int currentUser = Database.Instance.GetSignedInUser().Key;
        playerIds.Add(currentUser);

        var form = new WWWForm();
        form.AddField("currentUser", currentUser);
        form.AddField("winNumber", toWin);
        form.AddField("limitPerQuestion", limitPerQuestion);
        form.AddField("limitPerPlayer", limitPerPlayer);
        form.AddField("playerIds", String.Join(",", playerIds));
        form.AddField("categoryIds", String.Join(",", selectedCategories));

        string response = CallOnlineDatabaseWithResponse("NewOnlineGame.php", form);

        if (response.Equals("")) {
            Debug.Log("Expected gameId in response for creating new game but did not get one");
        }

        if (Int32.TryParse(response, out int newGameId)) {
            SendInviteForNewGame(newGameId, playerNames, Database.Instance.GetSignedInUser().Value);
            return newGameId; 
        } else {
            Debug.Log("Failed to get new game id with response " + response);
            return -1;
        }

    }

    [Serializable]
    public class Question {
        public string question;
        public string answer;
        public string id;
        public string category;
        public string categoryName;
        public int r;
        public int g;
        public int b;
        public int a;
    }

    [Serializable]
    public class Questions {
        public List<Question> questionsList = new List<Question>();
    }

    [Serializable]
    public class PlayerInfo {
        public string username;
        public string status;
        public int playerRound;
    }

    [Serializable]
    public class PlayerInfos {
        public List<PlayerInfo> playerInfoList = new List<PlayerInfo>();
    }

    [Serializable]
    public class GamePlayerInfo {
        public string username;
        public string userLockedQuestions;
        public string playerRound;
    }

    [Serializable]
    public class GamePlayerInfos {
        public List<GamePlayerInfo> gamePlayerInfoList = new List<GamePlayerInfo>();
    }



    [Serializable]
    public class Category {
        public int r;
        public int g;
        public int b;
        public int a;
        public int id;
        public string name;
        public int count;
    }

    [Serializable]
    public class Categories {
        public List<Category> categoryList = new List<Category>();
    }

    [Serializable]
    public class OnlineGame {
        public string id;
        public string winNumber;
        public string answerTimer;
        public string roundTimeLimit;
        public string currentPlayer;
        public string round;
        public string startDate;
        public string lastPlayedDate;
        public string finishedDate;
        public string status;
        public string userId;
        public string username;
        public string userLockedQuestions;
        public string playerStatus;
        public string playerRound;
    }

    [Serializable]
    public class OnlineGames {
        public List<OnlineGame> onlineGamesList = new List<OnlineGame>();
    }

    [Serializable]
    public class UserName {
        public string id;
        public string username;
    }

    [Serializable]
    public class UserNames {
        public List<UserName> usernamesList = new List<UserName>();
    }

    private void CallDatabase(string filename, WWWForm formData) {
        string postUrl = serverUrl + filename;

        UnityWebRequest www = UnityWebRequest.Post(postUrl, formData);
        www.SendWebRequest();

        if (www.isNetworkError || www.isHttpError) {
            Debug.Log(www.error);
        } else {
            while (!www.isDone) {
            }
        }
    }

    private string CallOnlineDatabaseWithResponse(string filename, WWWForm formData) {
        string postUrl = serverUrl + filename;

        UnityWebRequest www = UnityWebRequest.Post(postUrl, formData);
        www.SendWebRequest();

        if (www.isNetworkError || www.isHttpError) {
            Debug.Log(www.error);
        } else {
            while (!www.isDone) {
            }
        }

        return www.downloadHandler.text;
    }

    string questionString = "";
    string answerString = "";
    string idString = "";
    string categoryString = "";

    public string QuestionString { get => questionString; set => questionString = value; }

    internal void SendGameOverMessage(int gameId, List<KeyValuePair<string, int>> players, string currentPlayer, int rounds)
    {
        string message = String.Format(LocalizationManager.Instance.GetText("GAME_OVER_MESSAGE"), currentPlayer, rounds.ToString());
        string title = LocalizationManager.Instance.GetText("GAME_OVER_TITLE"); 

        WWWForm form = new WWWForm();
        form.AddField(Constants.GAME_ID, gameId);
        form.AddField("message", message);
        form.AddField("title", title);
        form.AddField("winningPlayer", currentPlayer);
        form.AddField("type", "gameFinishedMessage");

        int index = 0;
        foreach (KeyValuePair<String, int> player in players) {
            form.AddField("player" + index++, player.Key);
        }

        CallOnlineDatabaseWithResponse("FirebaseCaller.php", form);
    }

    private void Start() {
        if (instance == null) {
            instance = this;
        }
    }

    internal void SetLastPlayedDate(int gameId) {

        WWWForm form = new WWWForm();
        form.AddField(Constants.GAME_ID, gameId);
        form.AddField("f", "SetLastPlayed");
        string response = CallOnlineDatabaseWithResponse("OnlineGames.php", form);

        if (!response.Equals("")) {
            Debug.Log(response);
        }
    }

    internal void SetGameFinished(int gameId, string gameMode)
    {
        WWWForm formData = new WWWForm();
        formData.AddField(Constants.GAME_ID, gameId);
        formData.AddField("f", "GameFinished");

        CallDatabase("OnlineGames.php", formData);
    }

    internal void DeclineOnlineGame(string userName, int gameId) {
        WWWForm formData = new WWWForm();
        formData.AddField("userId", -1);
        formData.AddField("f", "decline");
        formData.AddField(Constants.GAME_ID, gameId);
        formData.AddField("userName", userName);

        CallDatabase("OnlineGames.php", formData);
    }

    internal void AcceptOnlineGame(string userName, int gameId) {
        WWWForm formData = new WWWForm();
        formData.AddField("userId", -1);
        formData.AddField("f", "accept");
        formData.AddField(Constants.GAME_ID, gameId);
        formData.AddField("userName", userName);

        string response = CallOnlineDatabaseWithResponse("OnlineGames.php", formData);
        if (!response.Equals("")) {
            Debug.Log(response);
        }
    }

//TODO Försök att ta bort alla utom 1 databas anrop i denna
    internal List<OnlineGameScript> GetOnlineGames(int userId, string userName, GameObject prefab) {

        WWWForm formData = new WWWForm();
        formData.AddField("userId", userId);
        formData.AddField("f", "list");
        formData.AddField(Constants.GAME_ID, -1);
        formData.AddField("userName", userName);

        string response = CallOnlineDatabaseWithResponse("OnlineGames.php", formData);

        if (response.Equals("No games found for user") || response.Equals("")) {
            return null;
        }

        response = "{\"onlineGamesList\" :  " + response + " }";

        OnlineGames og = new OnlineGames();
        JsonUtility.FromJsonOverwrite(response, og);

        GameObject onlineGameObject;
        OnlineGameScript ogs = null;
        List<OnlineGameScript> games = new List<OnlineGameScript>();
        int gameId = -1;

        OnlineGame prevGame = null;
        KeyValuePair<int, string> signedInUser = Database.Instance.GetSignedInUser();
        foreach (OnlineGame game in og.onlineGamesList) {
            Int32.TryParse(game.id, out int currentGameId);
            if (gameId != currentGameId) { // Spel ej i listan
                if (ogs != null) { // lägg till spel i listan, inte första gången
                    SetGlobalGameInfo(userName, ogs, gameId, prevGame);
                    if (ogs.GameStatus == null || ogs.GameStatus.Equals("")) {
                        ogs.SetGameStatus(prevGame.status);
                    }
                    games.Add(ogs);
                }
                onlineGameObject = Instantiate(prefab, new Vector2(0, 0), Quaternion.identity) as GameObject;
                ogs = onlineGameObject.GetComponent<OnlineGameScript>();
                gameId = currentGameId;

                if (game.currentPlayer.Equals(game.userId)) { // om första raden i spelinfo är annan spelare och det är dennes tur att agera
                    ogs.CurrentPlayer = game.username;
                }
                ogs.addPlayer(game.username);
                ogs.addPlayerInfo(game.username, game.playerStatus);
            } else { // Spel redan i listan, fyll på med info
                ogs.addPlayer(game.username);
                ogs.addPlayerInfo(game.username, game.playerStatus);
                if (game.currentPlayer.Equals(game.userId)) {
                    ogs.CurrentPlayer = game.username;
                }
            }
            SetGameStatus(ogs, signedInUser, game);
            prevGame = game;
        }

        SetGameStatus(ogs, signedInUser, og.onlineGamesList[og.onlineGamesList.Count - 1]);
        SetGlobalGameInfo(userName, ogs, gameId, og.onlineGamesList[og.onlineGamesList.Count - 1]);
        if (ogs.GameStatus == null || ogs.GameStatus.Equals("")) {
            ogs.SetGameStatus(og.onlineGamesList[og.onlineGamesList.Count - 1].status);
        }
        games.Add(ogs);
        
        return games;
    }

    private void SetGameStatus(OnlineGameScript ogs, KeyValuePair<int, string> signedInUser, OnlineGame game) {
        if (game.currentPlayer.Equals(signedInUser.Key.ToString())) {
            if (game.status.Equals(OnlineGameScript.GAME_STATUS_ACTIVE)) {
                ogs.SetGameStatus(OnlineGameScript.GAME_STATUS_YOUR_TURN);
            } else if (game.status.Equals(OnlineGameScript.GAME_STATUS_PENDING)) {
                if (game.userId.Equals(signedInUser.Key.ToString()) && game.playerStatus.Equals("WAITING")) {
                    ogs.SetGameStatus(OnlineGameScript.GAME_STATUS_INVITED);
                }
            }
        } else {
            if (game.status.Equals("ACTIVE")) {
                if (game.currentPlayer.Equals(game.userId)) {
                    ogs.CurrentPlayer = game.username;
                }
                ogs.SetGameStatus(OnlineGameScript.GAME_STATUS_OTHERS_TURN);
            } else if (game.status.Equals(OnlineGameScript.GAME_STATUS_PENDING)) {
                if (game.userId.Equals(signedInUser.Key.ToString()) && game.playerStatus.Equals("WAITING")) {
                    ogs.SetGameStatus(OnlineGameScript.GAME_STATUS_INVITED);
                } 
            }
        }
    }

    private void SetGlobalGameInfo(string userName, OnlineGameScript ogs, int gameId, OnlineGame game) {
        ogs.SetId(game.id);
        ogs.SetWinNumber(game.winNumber);
        ogs.SetAnswerTimer(game.answerTimer);
        ogs.SetRoundTimeLimit(game.roundTimeLimit);
        ogs.SetRound(game.round);
        ogs.StartDate = game.startDate;
        ogs.LastPlayedDate = game.lastPlayedDate;
    }

    internal List<KeyValuePair<string, string>> GetGameInfo(int gameId) { // TODO Return new class GameInfo
        List<KeyValuePair<string, string>> returnList = new List<KeyValuePair<string, string>>();

        WWWForm form = new WWWForm();
        form.AddField("f", "GetGameInfo");
        form.AddField(Constants.GAME_ID, gameId);

        string response = CallOnlineDatabaseWithResponse("OnlineGameInfo.php", form);

        response = "{\"playerInfoList\" : " + response + " }";
        PlayerInfos pi = new PlayerInfos();
        JsonUtility.FromJsonOverwrite(response, pi);

        foreach (PlayerInfo p in pi.playerInfoList) {
            KeyValuePair<string, string> player = new KeyValuePair<string, string>(p.username, p.status);
            returnList.Add(player);
        }

        return returnList;
    }

    internal void SetQuestionsLost(int gameId, string playerName, int questionsLost) {
        WWWForm form = new WWWForm();
        form.AddField("f", "SetQuestionsLost");
        form.AddField("questionsLost", questionsLost);
        form.AddField("userName", playerName);
        form.AddField(Constants.GAME_ID, gameId);

        string response = CallOnlineDatabaseWithResponse("OnlineGames.php", form);

        if (!response.Equals("")) {
            Debug.Log(response);
        }
    }
    internal void IncreasePlayerRound(int gameId, string currentPlayer) {
        WWWForm form = new WWWForm();
        form.AddField("player",currentPlayer);
        form.AddField(Constants.GAME_ID, gameId);
        form.AddField("f", "IncreasePlayerRound");

        CallDatabase("OnlineGames.php", form);
    }

    internal void RemoveGame(int gameId) {
        WWWForm form = new WWWForm();
        form.AddField("f", "DeleteGame");
        form.AddField(Constants.GAME_ID, gameId);

        string response = CallOnlineDatabaseWithResponse("OnlineGames.php", form);

        if (!response.Equals("")) {
            Debug.Log(response);
        }
    }

    public string GetCurrentPlayer(int gameId) {
        WWWForm form = new WWWForm();
        form.AddField("f", "CurrentPlayer");
        form.AddField(Constants.GAME_ID, gameId);

        string response = CallOnlineDatabaseWithResponse("OnlineGames.php", form);
        if (response.Equals("")) {
            Debug.Log("Something wrong with current player for game with id: " + gameId);
        }
        return response;
    }

    public void SetCurrentPlayer(int gameId, string currentPlayer) {
        WWWForm form = new WWWForm();
        form.AddField("f", "SetCurrentPlayer");
        form.AddField(Constants.GAME_ID, gameId);
        form.AddField("userName", currentPlayer);

        string response = CallOnlineDatabaseWithResponse("OnlineGames.php", form);

        if (!response.Equals("")) {
            Debug.Log(response);
        }
        
    }

    internal int GetPlayerPoints(int gameId, string playerName) {
        WWWForm form = new WWWForm();
        form.AddField("f", "GetPlayerPoints");
        form.AddField(Constants.GAME_ID, gameId);
        form.AddField("userName", playerName);

        string response = CallOnlineDatabaseWithResponse("OnlineGames.php", form);
        if (response.Equals("")) {
            Debug.Log("Something wrong with current player for game with id: " + gameId);
        }
        Int32.TryParse(response, out int playerPoints);
        return playerPoints;
    }

    internal void SetFinishedDate(int gameId, string finishedDate) {
        WWWForm form = new WWWForm();
        form.AddField("f", "SetFinishedDate");
        form.AddField(Constants.GAME_ID, gameId);
        form.AddField("finishedDate", finishedDate);

        string response = CallOnlineDatabaseWithResponse("OnlineGames.php", form);

        if (!response.Equals("")) {
            Debug.Log(response);
        }
    }

    internal void SetRoundValue(int gameId, int round) {
        WWWForm form = new WWWForm();
        form.AddField("f", "SetRound");
        form.AddField(Constants.GAME_ID, gameId);
        form.AddField("round", round);

        string response = CallOnlineDatabaseWithResponse("OnlineGames.php", form);

        if (!response.Equals("")) {
            Debug.Log(response);
        }
    }

    internal int GetRoundValue(int gameId) {
        WWWForm form = new WWWForm();
        form.AddField("f", "GetRound");
        form.AddField(Constants.GAME_ID, gameId);

        string response = CallOnlineDatabaseWithResponse("OnlineGames.php", form);
        if (response.Equals("")) {
            Debug.Log("Something wrong with getting round for game with id: " + gameId);
        }
        Int32.TryParse(response, out int round);
        return round;
    }

    internal List<KeyValuePair<string, int>> GetPlayersForGame(int gameId) { // TODO update with new class GamePlayers as return
        WWWForm form = new WWWForm();
        form.AddField("f", "GetPlayers");
        form.AddField(Constants.GAME_ID, gameId);

        string response = CallOnlineDatabaseWithResponse("OnlineGames.php", form);
        if (response.Equals("")) {
            Debug.Log("Something wrong with getting players from game with id: " + gameId);
        }

        response = response = "{\"gamePlayerInfoList\" : " + response + " }";
        GamePlayerInfos gpi = new GamePlayerInfos();
        JsonUtility.FromJsonOverwrite(response, gpi);

        List<KeyValuePair<string, int>> returnList = new List<KeyValuePair<string, int>>();
        foreach (GamePlayerInfo p in gpi.gamePlayerInfoList) {
            Int32.TryParse(p.userLockedQuestions, out int points);
            KeyValuePair<string, int> player = new KeyValuePair<string, int>(p.username, points);
            returnList.Add(player);
        }

        return returnList;
    }

    public string AnswerString { get => answerString; set => answerString = value; }
    public string IdString { get => idString; set => idString = value; }
    public string CategoryString { get => categoryString; set => categoryString = value; }

    internal int GetQuestionsLost(int gameId, string playerName) {
        WWWForm form = new WWWForm();
        form.AddField("f", "GetQuestionsLost");
        form.AddField("userName", playerName);
        form.AddField(Constants.GAME_ID, gameId);

        string response = CallOnlineDatabaseWithResponse("OnlineGames.php", form);
        if (response.Equals("")) {
            Debug.Log("Something wrong with getting questions lost from game with id: " + gameId + " and playername " + playerName);
        }
        Int32.TryParse(response, out int questionsLost);
        return questionsLost;
    }

    internal int GetWinCondition(int gameId) {
        if (winAmount == -1) {
            WWWForm form = new WWWForm();
            form.AddField(Constants.GAME_ID, gameId);
            form.AddField("f", "GetWinCondition");

            string response = CallOnlineDatabaseWithResponse("OnlineGames.php", form);

            Int32.TryParse(response, out this.winAmount);

        }
        return this.winAmount;
    }

    public NewQuestionData newQuestionData;

    public NewQuestionData GetNewQuestion(List<int> userAnsweredQuestions, int userId) {
        int gameId = GameObject.Find("GameManager").GetComponent<GameManagerScript>().GameId;

        Question q = GetQuestionData(gameId, userId);

        Color32 categoryColor = new Color32((byte)q.r, (byte)q.g, (byte)q.b, (byte)q.a);

        Int32.TryParse(q.category, out int categoryId);
        Int32.TryParse(q.id, out int questionId);

        NewQuestionData questionData = new NewQuestionData(q.answer, q.question, q.categoryName, categoryId, questionId, categoryColor, false);

        return questionData;
    }

    public void SavePlayersQuestion(List<int> questionsToSave, string playerNameValue, int gameId) {
        WWWForm form = new WWWForm();
        form.AddField("f", "SavePlayerQuestions");
        form.AddField(Constants.GAME_ID, gameId);
        form.AddField("questionsToSave", String.Join(",",questionsToSave));
        form.AddField("userName", playerNameValue);

        string response = CallOnlineDatabaseWithResponse("OnlineGames.php", form);

        if (!response.Equals("")) {
            Debug.Log(response);
        }
    }

    public List<NewQuestionData> GetPlayerQuestions(int gameId, string playerName) {
        List<NewQuestionData> questions = new List<NewQuestionData>();
        WWWForm form = new WWWForm();
        form.AddField("f", "PlayerQuestions");
        form.AddField(Constants.GAME_ID, gameId);
        form.AddField("userName", playerName);

        string response = CallOnlineDatabaseWithResponse("OnlineGameInfo.php", form);
        response = "{\"questionsList\" : " + response + "}";
        
        Questions ql = new Questions();

        JsonUtility.FromJsonOverwrite(response, ql);

        foreach (Question q in ql.questionsList) {
            /*GameObject question = Instantiate(questionCardPrefab, new Vector2(0, 0), Quaternion.identity) as GameObject;
            NewQuestionData qc = question.GetComponent<NewQuestionData>();
            */
            Color32 categoryColor = new Color32((byte)q.r, (byte)q.g, (byte)q.b, (byte)q.a);

            Int32.TryParse(q.category, out int categoryId);
            Int32.TryParse(q.id, out int questionId);

            NewQuestionData questionData = new NewQuestionData(q.answer, q.question, q.categoryName, categoryId, questionId, categoryColor, true);

            questions.Add(questionData);
        }
        
        return questions;
    }

    private Question GetQuestionData(int gameId, int playerId) {
        WWWForm form = new WWWForm();
        form.AddField(Constants.GAME_ID, gameId);
        form.AddField("playerId", playerId);

        string response = CallOnlineDatabaseWithResponse("Question.php", form);
        
        // Show result

        response = "{\"questionsList\" : [ " + response + " ]}";

        Questions qe = new Questions();
        JsonUtility.FromJsonOverwrite(response, qe);
        
        return qe.questionsList[0];
    }

    public List<UserName> GetUsersToInvite(string searchString) {
        WWWForm form = new WWWForm();
        form.AddField("SearchString", UnityWebRequest.EscapeURL(searchString));
        form.AddField("f", "PlayerSearch");

        String response = CallOnlineDatabaseWithResponse("PlayerSearch.php", form);

        response = "{\"usernamesList\" :  " + response + " }";

        UserNames uNames = new UserNames();

        JsonUtility.FromJsonOverwrite(response, uNames);

        return uNames.usernamesList;
    }

    internal void SendNextPlayerMessage(int gameId, String nextPlayer)
    {
        WWWForm form = new WWWForm();
        form.AddField(Constants.GAME_ID, gameId);
        form.AddField("playerName", nextPlayer);
        form.AddField("title", LocalizationManager.Instance.GetText("FCM_NEXT_PLAYER_TITLE"));
        List<KeyValuePair<string, int>> players = OnlineDatabase.Instance.GetPlayersForGame(gameId);
        StringBuilder sb = new StringBuilder();
        foreach (KeyValuePair<String, int> player in players)
        {
            sb.AppendLine(player.Key + " (" + player.Value + ")");
        }
        String message = String.Format(LocalizationManager.Instance.GetText("FCM_NEXT_PLAYER_MESSAGE"), sb.ToString());
        form.AddField("message", message);
        form.AddField("type", "FCMNextPlayer");
        
        CallOnlineDatabaseWithResponse("FirebaseCaller.php", form);
    }

    internal void SendInviteForNewGame(int gameId, List<String> Players, String inviter) {
        WWWForm form = new WWWForm();
        form.AddField(Constants.GAME_ID, gameId);
        int index = 0;

        foreach(String player in Players) {
            form.AddField("player" + index++, player);
        }

        form.AddField("title", LocalizationManager.Instance.GetText("FCM_NEW_GAME_TITLE"));
        form.AddField("message", String.Format(LocalizationManager.Instance.GetText("FCM_NEW_GAME_MESSAGE"), inviter));
        form.AddField("type", "InviteMessage");
        CallOnlineDatabaseWithResponse("FirebaseCaller.php", form);
    }

    
    internal void UpdatePlayerToken(int userId, string myToken)
    {
        WWWForm form = new WWWForm();
        form.AddField("userId", userId);
        form.AddField("token", myToken);
        form.AddField("f", "UpdatePlayerToken");

        CallDatabase("OnlineGames.php", form);
    }

    
    internal List<UserName> FindRandomPlayer(int playerId) {
        List<string> returnValue = new List<string>();
        WWWForm form = new WWWForm();
        form.AddField("playerId", playerId);
        form.AddField("f", "FindRandomPlayers");

        String response = CallOnlineDatabaseWithResponse("PlayerSearch.php", form);
        response = "{\"usernamesList\" :  " + response + " }";
        UserNames uNames = new UserNames();

        JsonUtility.FromJsonOverwrite(response, uNames);

        return uNames.usernamesList;
    }


}
