using Cryville.Common.Network;
using System;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using Logger = Cryville.Common.Logger;

namespace Cryville.Crtr {
	public class Console : MonoBehaviour {
		public Text Logs;
		public InputField InputBox;
		CommandWorker worker;
		readonly Queue<string> _fallbackQueue = new Queue<string>();

		~Console() {
			worker.Deactivate();
		}

		void Start() {
			Game.Init();
			InputBox.Select();

			worker = new CommandWorker(MainThreadFallback);
			worker.Activate();
		}

		void OnApplicationQuit() {
			Game.Shutdown();
		}

		public void Submit() {
			worker.Issue(InputBox.text);
		}

		void Update() {
			if (
				Input.GetKeyDown(KeyCode.Return)
				|| Input.GetKeyDown(KeyCode.KeypadEnter)
			) {
				Submit();
				InputBox.text = "";
				InputBox.Select();
				InputBox.ActivateInputField();
			}
			if (_fallbackQueue.Count > 0) {
				switch (_fallbackQueue.Dequeue()) {
					case "play":
						SceneManager.LoadScene("Play");
						break;
					case "quit":
						Application.Quit();
						break;
					default:
						Logger.Log("main", 4, "Console", "Unknown command. Type \"help\" for help");
						break;
				}
			}
			string _logs = Logs.text;
			Game.MainLogger.Enumerate((level, module, msg) => {
				string color;
				switch (level) {
					case 0: color = "#888888"; break;
					case 1: color = "#bbbbbb"; break;
					case 2: color = "#0088ff"; break;
					case 3: color = "#ffff00"; break;
					case 4: color = "#ff0000"; break;
					case 5: color = "#bb0000"; break;
					default: color = "#ff00ff"; break;
				}
				_logs += string.Format(
					"\r\n\x02<color={1}ff>[{0}] <{2}> {3}</color>",
					DateTime.UtcNow.ToString("s"), color, module, msg
				);
			});
			Logs.text = _logs.Substring(Mathf.Max(0, _logs.IndexOf('\x02', Mathf.Max(0, _logs.Length - 8192))));
		}

		void MainThreadFallback(string cmd) {
			_fallbackQueue.Enqueue(cmd);
		}

		class CommandWorker {
			readonly Action<string> _fallback;
			Thread _thread;
			readonly Queue<string> _queue = new Queue<string>();

			public CommandWorker(Action<string> fallback) {
				_fallback = fallback;
			}

			public void Activate() {
				_thread = new Thread(ThreadLoop);
				_thread.Start();
			}

			public void Deactivate() {
				_thread.Abort();
			}

			public void Issue(string cmd) {
				lock (_queue) {
					_queue.Enqueue(cmd);
				}
			}

			void ThreadLoop() {
				while (true) {
					string cmd = null;
					lock (_queue) {
						if (_queue.Count > 0) cmd = _queue.Dequeue();
					}
					if (cmd != null) ProcessCommand(cmd);
					else Thread.Sleep(20);
				}
			}

			void ProcessCommand(string cmd) {
				Logger.Log("main", 2, "Console", "Command processing: {0}", cmd);
				var p = cmd.Split(' ');
				try {
					switch (p[0]) {
						case "!http":
							var httpcl = new HttpClient(new Uri(p[1]));
							httpcl.Connect();
							httpcl.Request("GET", new Uri(p[1])).MessageBody.ReadToEnd();
							httpcl.Close();
							break;
						case "!https":
							var httpscl = new HttpsClient(new Uri(p[1]));
							httpscl.Connect();
							httpscl.Request("GET", new Uri(p[1])).MessageBody.ReadToEnd();
							httpscl.Close();
							break;
						/*case "!lua":
							Logger.Log("main", 1, "Console", "{0}", Script.RunString(p[1]));
							break;*/
						/*case "!tls":
							var tlscl = new TlsTcpClient(p[1]);
							tlscl.Close();
							break;*/
						case "connect":
							Game.ConnectDatabase(p[1]);
							break;
						case "help": case "?":
							Logger.Log(
								"main", 1, "Console",
								"\n\tconnect <i>name</i>" +
								"\n\thelp" +
								"\n\tlist" +
								"\n\tplay" +
								"\n\tquit"
							);
							break;
						case "list":
							Logger.Log("main", 1, "Console", "Database list:");
							foreach (var i in Game.GetDatabaseList()) {
								Logger.Log("main", 1, "Console", "{0}", i.Name);
							}
							break;
						default:
							_fallback.Invoke(cmd);
							break;
					}
				}
				catch (Exception ex) {
					Logger.Log("main", 4, "Console", "{0}", ex);
				}
				Logger.Log("main", 2, "Console", "Command done: {0}", cmd);
			}
		}
	}
}
