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 readonly Dictionary<string, (int, int)> markers = new Dictionary<string, (int, int)> ();

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

				markers.Add (WebUtils.GenerateGuid (), (lat, lng));
			}
		}

		private static readonly byte[] jsonKeyId = JsonWriter.GetEncodedPropertyNameWithBeginObject ("id");
		private static readonly byte[] jsonKeyLat = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("lat");
		private static readonly byte[] jsonKeyLng = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("lng");

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

				bool first = true;
				foreach ((string markerId, (int, int) coordinates) in markers) {
					if (!first) {
						writer.WriteValueSeparator ();
					}
					
					first = false;
					
					writeMarkerJson (ref writer, markerId, coordinates);
				}

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

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

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

		private void writeMarkerJson (ref JsonWriter _writer, string _markerId, (int, int) _coordinates) {
			_writer.WriteRaw (jsonKeyId);
			_writer.WriteString (_markerId);
			_writer.WriteRaw (jsonKeyLat);
			_writer.WriteInt32 (_coordinates.Item1);
			_writer.WriteRaw (jsonKeyLng);
			_writer.WriteInt32 (_coordinates.Item2);
			_writer.WriteEndObject ();
		}

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

			if (!TryGetJsonField (_jsonInput, "lng", out int lng)) {
				SendErrorResult (_context, HttpStatusCode.BadRequest, _jsonInputData, "NO_OR_INVALID_LNG");
				return;
			}

			string newId = WebUtils.GenerateGuid ();
			markers.Add (newId, (lat, lng));

			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 (!TryGetJsonField (_jsonInput, "lat", out int lat)) {
				SendErrorResult (_context, HttpStatusCode.BadRequest, _jsonInputData, "NO_OR_INVALID_LAT");
				return;
			}

			if (!TryGetJsonField (_jsonInput, "lng", out int lng)) {
				SendErrorResult (_context, HttpStatusCode.BadRequest, _jsonInputData, "NO_OR_INVALID_LNG");
				return;
			}

			string id = _context.RequestPath;

			if (!markers.TryGetValue (id, out _)) {
				SendErrorResult (_context, HttpStatusCode.NotFound, _jsonInputData, "ID_NOT_FOUND");
				return;
			}
			
			markers [id] = (lat, lng);

			PrepareEnvelopedResult (out JsonWriter writer);
			writer.WriteRaw (jsonKeyId);
			writer.WriteString (id);
			writer.WriteRaw (jsonKeyLat);
			writer.WriteInt32 (lat);
			writer.WriteRaw (jsonKeyLng);
			writer.WriteInt32 (lng);
			writer.WriteEndObject ();
			SendEnvelopedResult (_context, ref writer);
		}

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

			PrepareEnvelopedResult (out JsonWriter writer);
			writer.WriteRaw (JsonEmptyData);
			SendEnvelopedResult (_context, ref writer, markers.Remove (id) ? HttpStatusCode.NoContent : HttpStatusCode.NotFound);
		}
		
		public override int[] DefaultMethodPermissionLevels () => new[] {
			AdminWebModules.MethodLevelNotSupported,
			AdminWebModules.PermissionLevelGuest,
			AdminWebModules.PermissionLevelUser,
			AdminWebModules.MethodLevelInheritGlobal,
			AdminWebModules.MethodLevelInheritGlobal
		};
	}
}