using System.Collections.Generic;
using System.Net;
using JetBrains.Annotations;
using Utf8Json;
using Webserver;
using Webserver.Permissions;
using Webserver.WebAPI;

namespace Examples {
	[UsedImplicitly]
	public class Markers : AbsRestApi {
		private const int numRandomMarkers = 5;

		private const string defaultIcon =
			"https://upload.wikimedia.org/wikipedia/commons/thumb/1/11/Blue_question_mark_icon.svg/1200px-Blue_question_mark_icon.svg.png";
		
		private readonly Dictionary<string, MarkerData> markers = new Dictionary<string, MarkerData> ();

		public Markers () {
			GameRandom random = GameRandomManager.Instance.CreateGameRandom ();
			
			for (int i = 0; i < numRandomMarkers; i++) {
				int x = random.RandomRange (-1000, 1001);
				int y = random.RandomRange (-1000, 1001);

				string guid = WebUtils.GenerateGuid ();
				markers.Add (guid, new MarkerData(guid, new Vector2i (x, y), "RandomMarker " + i, null));
			}
			
			GameRandomManager.Instance.FreeGameRandom (random);
		}

		private static readonly byte[] jsonKeyId = JsonWriter.GetEncodedPropertyNameWithBeginObject ("id");
		private static readonly byte[] jsonKeyX = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("x");
		private static readonly byte[] jsonKeyY = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("y");
		private static readonly byte[] jsonKeyName = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("name");
		private static readonly byte[] jsonKeyIcon = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("icon");

		protected override void HandleRestGet (RequestContext _context) {
			string id = _context.RequestPath;
			
			PrepareEnvelopedResult (out JsonWriter writer);
			
			if (string.IsNullOrEmpty (id)) {
				writer.WriteBeginArray ();

				bool first = true;
				foreach ((_, MarkerData iMarker) in markers) {
					if (!first) {
						writer.WriteValueSeparator ();
					}
					
					first = false;
					
					writeMarkerJson (ref writer, iMarker);
				}

				writer.WriteEndArray ();
				SendEnvelopedResult (_context, ref writer);
				return;
			}

			if (!markers.TryGetValue (id, out MarkerData singleMarker)) {
				writer.WriteRaw (WebUtils.JsonEmptyData);
				SendEnvelopedResult (_context, ref writer, HttpStatusCode.NotFound);
				return;
			}

			{
				writer.WriteBeginArray ();
				
				writeMarkerJson (ref writer, singleMarker);
				
				writer.WriteEndArray ();
				SendEnvelopedResult (_context, ref writer);
			}
		}

		private void writeMarkerJson (ref JsonWriter _writer, MarkerData _marker) {
			_writer.WriteRaw (jsonKeyId);
			_writer.WriteString (_marker.Id);
			_writer.WriteRaw (jsonKeyX);
			_writer.WriteInt32 (_marker.Position.x);
			_writer.WriteRaw (jsonKeyY);
			_writer.WriteInt32 (_marker.Position.y);
			_writer.WriteRaw (jsonKeyName);
			_writer.WriteString (_marker.Name);
			_writer.WriteRaw (jsonKeyIcon);
			_writer.WriteString (_marker.Icon ?? defaultIcon);
			_writer.WriteEndObject ();
		}

		protected override void HandleRestPost (RequestContext _context, IDictionary<string, object> _jsonInput, byte[] _jsonInputData) {
			if (!JsonCommons.TryGetJsonField (_jsonInput, "x", out int x)) {
				SendEmptyResponse (_context, HttpStatusCode.BadRequest, _jsonInputData, "NO_OR_INVALID_X");
				return;
			}

			if (!JsonCommons.TryGetJsonField (_jsonInput, "y", out int y)) {
				SendEmptyResponse (_context, HttpStatusCode.BadRequest, _jsonInputData, "NO_OR_INVALID_Y");
				return;
			}

			JsonCommons.TryGetJsonField (_jsonInput, "name", out string name);
			if (string.IsNullOrEmpty (name)) {
				name = null;
			}

			JsonCommons.TryGetJsonField (_jsonInput, "icon", out string icon);
			if (string.IsNullOrEmpty (icon)) {
				icon = null;
			}

			string newId = WebUtils.GenerateGuid ();
			markers.Add (newId, new MarkerData(newId, new Vector2i (x, y), name, icon));

			PrepareEnvelopedResult (out JsonWriter writer);
			writer.WriteString (newId);
			SendEnvelopedResult (_context, ref writer, HttpStatusCode.Created);
		}

		protected override void HandleRestPut (RequestContext _context, IDictionary<string, object> _jsonInput, byte[] _jsonInputData) {
			if (!JsonCommons.TryGetJsonField (_jsonInput, "x", out int x)) {
				SendEmptyResponse (_context, HttpStatusCode.BadRequest, _jsonInputData, "NO_OR_INVALID_X");
				return;
			}

			if (!JsonCommons.TryGetJsonField (_jsonInput, "y", out int y)) {
				SendEmptyResponse (_context, HttpStatusCode.BadRequest, _jsonInputData, "NO_OR_INVALID_Y");
				return;
			}

			bool keepName = !JsonCommons.TryGetJsonField (_jsonInput, "name", out string name);

			bool keepIcon = !JsonCommons.TryGetJsonField (_jsonInput, "icon", out string icon);

			string id = _context.RequestPath;

			if (!markers.TryGetValue (id, out MarkerData oldMarker)) {
				SendEmptyResponse (_context, HttpStatusCode.NotFound, _jsonInputData, "ID_NOT_FOUND");
				return;
			}

			if (keepName) {
				name = oldMarker.Name;
			}
			
			if (keepIcon) {
				icon = oldMarker.Icon;
			}

			MarkerData updatedMarker = new MarkerData(id, new Vector2i (x, y), name, icon);
			markers [id] = updatedMarker;

			PrepareEnvelopedResult (out JsonWriter writer);
			writeMarkerJson (ref writer, updatedMarker);
			SendEnvelopedResult (_context, ref writer);
		}

		protected override void HandleRestDelete (RequestContext _context) {
			string id = _context.RequestPath;

			SendEmptyResponse (_context, markers.Remove (id) ? HttpStatusCode.NoContent : HttpStatusCode.NotFound);
		}
		
		public override int[] DefaultMethodPermissionLevels () => new[] {
			AdminWebModules.MethodLevelNotSupported,
			AdminWebModules.PermissionLevelGuest,
			AdminWebModules.PermissionLevelUser,
			AdminWebModules.MethodLevelInheritGlobal,
			AdminWebModules.MethodLevelInheritGlobal
		};
	}
}