using Cryville.EEW.Core.Map;
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Networking;

namespace Cryville.EEW.Unity.Map {
	[RequireComponent(typeof(SpriteRenderer))]
	sealed class MapTile : MonoBehaviour {
		static readonly SemaphoreSlim _semaphore = new(2);

		static readonly HttpClient _httpClient = new() { Timeout = TimeSpan.FromSeconds(10) };

		[SerializeField] Transform _idView;

		public MapTileIndex Index { get; set; }
		public bool IsEmpty { get; private set; }

		Action<MapTile> _callback;

		SpriteRenderer _renderer;

		UnityWebRequest _req;
		DownloadHandlerTexture _texHandler;
		Texture2D _tex;
		Sprite _sprite;

		void Awake() {
			_renderer = GetComponent<SpriteRenderer>();
		}

		FileInfo _localFile;
		bool _downloadDone;
		public void Load(MapTileIndex index, string cacheDir, Action<MapTile> onUpdated) {
			Index = index;
			_callback = onUpdated;
			_localFile = new(Path.Combine(cacheDir, $"map/{Index.Z}/{Index.NX}/{Index.NY}"));
			float z = 1 << index.Z;
			transform.localPosition = new(index.X / z, -(index.Y + 1) / z, -index.Z / 100f);
			transform.localScale = new Vector3(1 / z, 1 / z, 1);
			_idView.gameObject.SetActive(true);
			byte e = SharedSettings.Instance.IdBytes[((index.X << 2) + index.Y) & 0x1f];
			int ex = e >> 4, ey = e & 0xf;
			_idView.localPosition = new(ex / 16f, 1 - ey / 16f, -1 / 200f);
			if (_localFile.Exists) {
				_downloadDone = true;
			}
			else {
				Task.Run(() => RunAsync($"https://server.arcgisonline.com/ArcGIS/rest/services/Ocean/World_Ocean_Base/MapServer/tile/{Index.Z}/{Index.NY}/{Index.NX}"));
			}
		}
		async Task RunAsync(string url) {
			await _semaphore.WaitAsync().ConfigureAwait(true);
			try {
				Directory.CreateDirectory(_localFile.DirectoryName);
				using var webStream = await _httpClient.GetStreamAsync(new Uri(url)).ConfigureAwait(true);
				using var fileStream = new FileStream(_localFile.FullName, FileMode.Create, FileAccess.Write);
				await webStream.CopyToAsync(fileStream).ConfigureAwait(true);
			}
			finally {
				_semaphore.Release();
			}
			_downloadDone = true;
		}

		[SuppressMessage("CodeQuality", "IDE0051", Justification = "Unity message")]
		void Update() {
			if (_downloadDone) {
				try {
					_texHandler = new DownloadHandlerTexture();
					_req = new UnityWebRequest($"file:///{_localFile}") {
						downloadHandler = _texHandler,
						disposeDownloadHandlerOnDispose = true,
					};
					_req.SendWebRequest();
				}
				catch (Exception ex) {
					Debug.LogException(ex);
				}
				_downloadDone = false;
			}
			if (_req == null || !_req.isDone) return;
			if (_texHandler.isDone) {
				_tex = _texHandler.texture;
				_tex.wrapMode = TextureWrapMode.Clamp;
				_sprite = Sprite.Create(_tex, new Rect(0, 0, _tex.width, _tex.height), Vector2.zero, _tex.height, 0, SpriteMeshType.FullRect, Vector4.zero, false);
				_renderer.sprite = _sprite;
			}
			else {
				Debug.LogError(_req.error);
				_localFile.Delete();
				IsEmpty = true;
			}
			_req.Dispose();
			_req = null;
			_callback?.Invoke(this);
		}

		[SuppressMessage("CodeQuality", "IDE0051", Justification = "Unity message")]
		void OnDestroy() {
			if (_req != null) {
				_req.Abort();
				_req.Dispose();
				_texHandler.Dispose();
			}
			if (_sprite) Destroy(_sprite);
			if (_tex) Destroy(_tex);
			IsEmpty = true;
			_callback?.Invoke(this);
		}
	}
}
