using System;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Subsurface.Networking;
using FarseerPhysics;
using FarseerPhysics.Factories;
using FarseerPhysics.Dynamics;
using System.IO;
using System.Collections.Generic;
using RestSharp;

namespace Subsurface
{
    class ServerListScreen : Screen
    {
        //how often the client is allowed to refresh servers
        private TimeSpan AllowedRefreshInterval = new TimeSpan(0,0,3);

        private GUIFrame menu;

        private GUIListBox serverList;

        private GUIButton joinButton;

        private GUITextBox clientNameBox, ipBox;

        //private RestRequestAsyncHandle restRequestHandle;
        private bool masterServerResponded;

        private int[] columnX;

        //a timer for 
        private DateTime refreshDisableTimer;
        private bool waitingForRefresh;

        public ServerListScreen()
        {
            int width = Math.Min(GameMain.GraphicsWidth - 160, 1000);
            int height = Math.Min(GameMain.GraphicsHeight - 160, 700);

            Rectangle panelRect = new Rectangle(0, 0, width, height);

            menu = new GUIFrame(panelRect, null, Alignment.Center, GUI.Style);

            new GUITextBlock(new Rectangle(0, -25, 0, 30), "Join Server", GUI.Style, Alignment.CenterX, Alignment.CenterX, menu, false, GUI.LargeFont);

            new GUITextBlock(new Rectangle(0, 30, 0, 30), "Your Name:", GUI.Style, menu);
            clientNameBox = new GUITextBox(new Rectangle(0, 60, 200, 30), GUI.Style, menu);

            new GUITextBlock(new Rectangle(0, 100, 0, 30), "Server IP:", GUI.Style, menu);
            ipBox = new GUITextBox(new Rectangle(0, 130, 200, 30), GUI.Style, menu);

            int middleX = (int)(width * 0.4f);

            serverList = new GUIListBox(new Rectangle(middleX,60,0,(int)(height*0.7f)), GUI.Style, menu);
            serverList.OnSelected = SelectServer;

            float[] columnRelativeX = new float[] { 0.15f, 0.55f, 0.15f, 0.15f };
            columnX = new int[columnRelativeX.Length];
            for (int n = 0; n < columnX.Length; n++)
            {
                columnX[n] = (int)(columnRelativeX[n] * serverList.Rect.Width);
                if (n > 0) columnX[n] += columnX[n - 1];
            }

            new GUITextBlock(new Rectangle(middleX, 30, 0, 30), "Password", GUI.Style, menu);

            new GUITextBlock(new Rectangle(middleX + columnX[0], 30, 0, 30), "Name", GUI.Style, menu);
            new GUITextBlock(new Rectangle(middleX + columnX[1], 30, 0, 30), "Players", GUI.Style, menu);
            new GUITextBlock(new Rectangle(middleX + columnX[2], 30, 0, 30), "Running", GUI.Style, menu);

            joinButton = new GUIButton(new Rectangle(-170, 0, 150, 30), "Refresh", Alignment.BottomRight, GUI.Style, menu);
            joinButton.OnClicked = RefreshServers;

            joinButton = new GUIButton(new Rectangle(0,0,150,30), "Join", Alignment.BottomRight, GUI.Style, menu);
            joinButton.OnClicked = JoinServer;

            GUIButton button = new GUIButton(new Rectangle(-20, -20, 100, 30), "Back", Alignment.TopLeft, GUI.Style, menu);
            button.UserData = 0;
            button.OnClicked = GameMain.MainMenuScreen.SelectTab;
            

            refreshDisableTimer = DateTime.Now;
        }

        public override void Select()
        {
            base.Select();


            //RefreshServers(null, null);
            //UpdateServerList();
        }

        private bool SelectServer(GUIComponent component, object obj)
        {
            string ip = obj as string;
            if (string.IsNullOrWhiteSpace(ip)) return false;

            ipBox.Text = ip;

            return true;
        }

        private bool RefreshServers(GUIButton button, object obj)
        {
            if (waitingForRefresh) return false;
            serverList.ClearChildren();

            new GUITextBlock(new Rectangle(0, 0, 0, 20), "Refreshing server list...", GUI.Style, serverList);
            
            CoroutineManager.StartCoroutine(WaitForRefresh());

            return true;
        }

        private IEnumerable<object> WaitForRefresh()
        {
            waitingForRefresh = true;
            if (refreshDisableTimer > DateTime.Now)
            {
                yield return new WaitForSeconds((float)(refreshDisableTimer - DateTime.Now).TotalSeconds);
            }

            //CoroutineManager.StartCoroutine(UpdateServerList());
            CoroutineManager.StartCoroutine(SendMasterServerRequest());

            waitingForRefresh = false;

            refreshDisableTimer = DateTime.Now + AllowedRefreshInterval;

            yield return CoroutineStatus.Success;
        }

        private void UpdateServerList(string masterServerData)
        {
            serverList.ClearChildren();
            
            //string masterServerData = GetMasterServerData();

            if (string.IsNullOrWhiteSpace(masterServerData))
            {
                var nameText = new GUITextBlock(new Rectangle(0, 0, 0, 20), "Couldn't find any servers", GUI.Style, serverList);

                return;
            }

            if (masterServerData.Substring(0,5).ToLower()=="error")
            {
                DebugConsole.ThrowError("Error while connecting to master server ("+masterServerData+")!");

                return;
            }

            string[] lines = masterServerData.Split('\n');

            for (int i = 0; i<lines.Length; i++)
            {
                string[] arguments = lines[i].Split('|');
                if (arguments.Length < 3) continue;

                string IP = arguments[0];
                string port = arguments[1];
                string serverName = arguments[2];
                string gameStarted = (arguments.Length > 3) ? arguments[3] : "";
                string playerCountStr = (arguments.Length > 4) ? arguments[4] : "";

                string hasPassWordStr = (arguments.Length > 5) ? arguments[5] : "";

                var serverFrame = new GUIFrame(new Rectangle(0,0,0,20), (i%2 == 0) ? Color.Transparent : Color.White*0.2f, null, serverList);
                serverFrame.UserData = IP+":"+port;
                serverFrame.HoverColor = Color.Gold * 0.2f;
                serverFrame.SelectedColor = Color.Gold * 0.5f;

                var passwordBox = new GUITickBox(new Rectangle(columnX[0]/2, 0, 20, 20), "", Alignment.TopLeft, serverFrame);
                passwordBox.Selected = hasPassWordStr == "1";
                passwordBox.Enabled = false;
                passwordBox.UserData = "password";

                var nameText = new GUITextBlock(new Rectangle(columnX[0], 0, 0, 0), serverName, GUI.Style, serverFrame);

                int playerCount, maxPlayers;
                playerCount = GameClient.ByteToPlayerCount((byte)int.Parse(playerCountStr), out maxPlayers);

                var playerCountText = new GUITextBlock(new Rectangle(columnX[1], 0, 0, 0), playerCount + "/" + maxPlayers, GUI.Style, serverFrame);

                var gameStartedBox = new GUITickBox(new Rectangle(columnX[2] + (columnX[3] - columnX[2])/ 2, 0, 20, 20), "", Alignment.TopLeft, serverFrame);
                gameStartedBox.Selected = gameStarted == "1";
                gameStartedBox.Enabled = false;
            
            }
        }

        private IEnumerable<object> SendMasterServerRequest()
        {
            RestClient client = null;
            try
            {
                client = new RestClient(NetConfig.MasterServerUrl);
            }
            catch (Exception e)
            {
                DebugConsole.ThrowError("Error while connecting to master server", e);                
            }

            if (client == null) yield return CoroutineStatus.Success;


            var request = new RestRequest("masterserver.php", Method.GET);
            request.AddParameter("gamename", "subsurface"); // adds to POST or URL querystring based on Method
            request.AddParameter("action", "listservers"); // adds to POST or URL querystring based on Method


            // easily add HTTP Headers
            //request.AddHeader("header", "value");

            //// add files to upload (works with compatible verbs)
            //request.AddFile(path);

            // execute the request
            masterServerResponded = false;
            var restRequestHandle = client.ExecuteAsync(request, response => MasterServerCallBack(response));

            DateTime timeOut = DateTime.Now + new TimeSpan(0, 0, 8);
            while (!masterServerResponded)
            {
                if (DateTime.Now > timeOut)
                { 
                    serverList.ClearChildren();
                    restRequestHandle.Abort();
                    DebugConsole.ThrowError("Couldn't connect to master server (request timed out)");
                }
                yield return CoroutineStatus.Running;
            }

            yield return CoroutineStatus.Success;

        }

        private void MasterServerCallBack(IRestResponse response)
        {
            masterServerResponded = true;

            if (response.ErrorException!=null)
            {
                serverList.ClearChildren();  
                DebugConsole.ThrowError("Error while connecting to master server", response.ErrorException);
                return;
            }

            if (response.StatusCode!= System.Net.HttpStatusCode.OK)
            {
                serverList.ClearChildren();  
                DebugConsole.ThrowError("Error while connecting to master server (" +response.StatusCode+": "+response.StatusDescription+")");
                return;
            }

            UpdateServerList(response.Content);
        }

        private bool JoinServer(GUIButton button, object obj)
        {
            if (string.IsNullOrWhiteSpace(clientNameBox.Text))
            {
                clientNameBox.Flash();
                return false;
            }

            string ip = ipBox.Text;

            if (string.IsNullOrWhiteSpace(ip))
            {
                ipBox.Flash();
                return false;
            }

            CoroutineManager.StartCoroutine(JoinServer(ip));


            return true;
        }

        private IEnumerable<object> JoinServer(string ip)
        {
            string selectedPassword = "";

            if (serverList.Selected!=null && (serverList.Selected.GetChild("password") as GUITickBox).Selected)
            {
                var msgBox = new GUIMessageBox("Password required", "");
                var passwordBox = new GUITextBox(new Rectangle(0,0,150,20), Alignment.BottomCenter, GUI.Style, msgBox);
                passwordBox.UserData = "password";

                var okButton = msgBox.GetChild<GUIButton>();                

                while (GUIMessageBox.MessageBoxes.Contains(msgBox))
                {
                    okButton.Enabled = !string.IsNullOrWhiteSpace(passwordBox.Text);
                    yield return CoroutineStatus.Running;
                }

                selectedPassword = passwordBox.Text;
            }

            GameMain.NetworkMember = new GameClient(clientNameBox.Text);
            GameMain.Client.ConnectToServer(ip, selectedPassword);

            yield return CoroutineStatus.Success;
        }

        public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
        {
            graphics.Clear(Color.CornflowerBlue);

            GameMain.GameScreen.DrawMap(graphics, spriteBatch);

            spriteBatch.Begin();

            menu.Draw(spriteBatch);

            //if (previewPlayer!=null) previewPlayer.Draw(spriteBatch);

            GUI.Draw((float)deltaTime, spriteBatch, null);

            spriteBatch.End();
        }

        public override void Update(double deltaTime)
        {


            menu.Update((float)deltaTime);

            GUI.Update((float)deltaTime);
        }
    }
}
