using Cryville.Common;
using Cryville.Common.Unity;
using Cryville.Crtr.Extension;
using Cryville.Crtr.Extensions;
using Cryville.Crtr.Extensions.Umg;
using Cryville.Crtr.UI;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;
using Logger = Cryville.Common.Logging.Logger;

namespace Cryville.Crtr.Browsing {
	internal class LegacyResourceManager : IResourceManager<ChartDetail> {
		private readonly string _rootPath;
		private DirectoryInfo cd;
		private DirectoryInfo[] items = new DirectoryInfo[0];
		public string[] CurrentDirectory { get; private set; }

		static bool _init;

		public LegacyResourceManager(string rootPath) {
			_rootPath = rootPath;
			if (!_init) {
				_init = true;
				ExtensionManager.Init(rootPath);
			}
		}

		public int ChangeDirectory(string[] dir) {
			CurrentDirectory = dir;
			cd = new DirectoryInfo(_rootPath + "/charts/" + string.Join("/", dir));
			items = cd.GetDirectories();
			return items.Length;
		}

		public int OpenDirectory(int id) {
			string[] nd = new string[CurrentDirectory.Length + 1];
			Array.Copy(CurrentDirectory, nd, CurrentDirectory.Length);
			nd[CurrentDirectory.Length] = items[id].Name;
			return ChangeDirectory(nd);
		}

		public int ReturnToDirectory(int id) {
			string[] nd = new string[id + 1];
			Array.Copy(CurrentDirectory, nd, id + 1);
			return ChangeDirectory(nd);
		}

		public ResourceItemMeta GetItemMeta(int id) {
			var item = items[id];
			var meta = new ChartMeta();
			string name = item.Name;
			string desc = "(Unknown)";
			var metaFile = new FileInfo(item.FullName + "/.umgc");
			if (metaFile.Exists) {
				using (var reader = new StreamReader(metaFile.FullName)) {
					meta = JsonConvert.DeserializeObject<ChartMeta>(reader.ReadToEnd());
					name = meta.song.name;
					desc = meta.name;
				}
			}
			AsyncDelivery<Texture2D> cover = null;
			if (meta.cover != null && meta.cover != "") {
				var coverFile = item.GetFiles(meta.cover);
				if (coverFile.Length > 0) {
					cover = new AsyncDelivery<Texture2D>();
					var task = new LoadTextureTask(PlatformConfig.FileProtocolPrefix + coverFile[0].FullName, cover.Deliver);
					cover.CancelSource = task.Cancel;
					Game.NetworkTaskWorker.SubmitNetworkTask(task);
				}
			}
			return new ResourceItemMeta {
				IsDirectory = false,
				Icon = cover,
				Name = name,
				DescriptionMain = desc,
			};
		}

		public ChartDetail GetItemDetail(int id) {
			var item = items[id];
			var meta = new ChartMeta();
			var metaFile = new FileInfo(item.FullName + "/.umgc");
			if (metaFile.Exists) {
				using (var reader = new StreamReader(metaFile.FullName)) {
					meta = JsonConvert.DeserializeObject<ChartMeta>(reader.ReadToEnd());
				}
			}
			AsyncDelivery<Texture2D> cover = null;
			if (meta.cover != null && meta.cover != "") {
				var coverFile = item.GetFiles(meta.cover);
				if (coverFile.Length > 0) {
					cover = new AsyncDelivery<Texture2D>();
					var task = new LoadTextureTask(PlatformConfig.FileProtocolPrefix + coverFile[0].FullName, cover.Deliver);
					cover.CancelSource = task.Cancel;
					Game.NetworkTaskWorker.SubmitNetworkTask(task);
				}
			}
			return new ChartDetail {
				Cover = cover,
				Meta = meta,
			};
		}

		public string GetItemPath(int id) {
			var item = items[id];
			var meta = new ChartMeta();
			var metaFile = new FileInfo(item.FullName + "/.umgc");
			using (var reader = new StreamReader(metaFile.FullName)) {
				meta = JsonConvert.DeserializeObject<ChartMeta>(reader.ReadToEnd());
			}
			return string.Format("{0}/{1}.json", items[id].Name, meta.data);
		}

		public bool ImportItemFrom(string path) {
			var file = new FileInfo(path);
			IEnumerable<ResourceConverter> converters;
			if (!ExtensionManager.TryGetConverters(file.Extension, out converters)) return false;
			foreach (var converter in converters) {
				var resources = new List<Resource>();
				var ses = new ConversionSession {
					OnResourceAdd = res => resources.Add(res)
				};
				try {
					converter.Convert(file, ses);
				}
				catch (Exception ex) {
					LogAndPopupExtra(4, ex, "Failed to import resource: {0}", ex.Message);
					return false;
				}
				foreach (var res in resources) {
					if (!res.Valid) {
						LogAndPopup(3, "Attempt to import invalid resource: {0}", res);
					}
					else if (res is RawChartResource) {
						var tres = (RawChartResource)res;
						var dir = new DirectoryInfo(_rootPath + "/charts/" + res.Name);
						if (!dir.Exists) dir.Create();
						using (var writer = new StreamWriter(dir.FullName + "/.json")) {
							writer.Write(JsonConvert.SerializeObject(ConvertChartData(tres.Main), Game.GlobalJsonSerializerSettings));
						}
						using (var writer = new StreamWriter(dir.FullName + "/.umgc")) {
							tres.Meta.data = "";
							writer.Write(JsonConvert.SerializeObject(tres.Meta, Game.GlobalJsonSerializerSettings));
						}
						if (tres.Meta.cover != null) {
							var coverFile = new FileInfo(Path.Combine(file.Directory.FullName, tres.Meta.cover));
							if (coverFile.Exists)
								coverFile.CopyTo(Path.Combine(dir.FullName, tres.Meta.cover), true);
						}
					}
					else if (res is FileResource) {
						var tres = (FileResource)res;
						DirectoryInfo dest;
						bool singleFileFlag = false;
						if (res is ChartResource)
							dest = new DirectoryInfo(_rootPath + "/charts/" + res.Name);
						else if (res is ExtensionResource) {
							dest = new DirectoryInfo(_rootPath + "/extensions");
							singleFileFlag = true;
							Popup.Create("Please restart the game to reload the extensions");
						}
						else if (res is RulesetResource)
							dest = new DirectoryInfo(_rootPath + "/rulesets/" + res.Name);
						else if (res is SkinResource)
							dest = new DirectoryInfo(_rootPath + "/skins/" + (res as SkinResource).RulesetName + "/" + res.Name);
						else if (res is SongResource)
							dest = new DirectoryInfo(_rootPath + "/songs/" + res.Name);
						else {
							LogAndPopup(3, "Attempt to import unsupported file resource: {0}", res);
							continue;
						}
						if (singleFileFlag || !dest.Exists) {
							dest.Create();
							tres.Master.CopyTo(Path.Combine(dest.FullName, singleFileFlag ? tres.Master.Name : tres.Master.Extension));
							foreach (var attachment in tres.Attachments) {
								if (singleFileFlag) throw new InvalidOperationException("Internal error");
								attachment.CopyTo(Path.Combine(dest.FullName, attachment.Name));
							}
						}
						else LogAndPopup(1, "Resource already exists: {0}", res);
					}
					else {
						LogAndPopup(3, "Attempt to import unsupported resource: {0}", res);
					}
				}
				return true;
			}
			return false;
		}

		void LogAndPopup(int level, string format, params object[] args) {
			var msg = string.Format(format, args);
			Logger.Log("main", level, "Resource", msg);
			Popup.Create(msg);
		}

		void LogAndPopupExtra(int level, object extraLog, string format, params object[] args) {
			var msg = string.Format(format, args);
			Logger.Log("main", level, "Resource", "{0}\n{1}", msg, extraLog);
			Popup.Create(msg);
		}

		public string[] GetSupportedFormats() {
			return ExtensionManager.GetSupportedFormats().ToArray();
		}

		public IReadOnlyDictionary<string, string> GetPresetPaths() {
			return ExtensionManager.GetLocalResourcePaths();
		}

		static Chart ConvertChartData(ChartData i) {
			return new Chart {
				endtime = ConvertBeatTime(i.endtime),
				format = i.format,
				groups = ConvertGroups(i.groups),
				motions = ConvertMotions(i.motions),
				ruleset = i.ruleset,
				sigs = ConvertSignatures(i.sigs),
				sounds = ConvertSounds(i.sounds),
				time = ConvertBeatTime(i.time),
			};
		}
		static BeatTime? ConvertBeatTime(Extension.BeatTime? value) {
			if (value == null) return null;
			return new BeatTime(value.Value.b, value.Value.n, value.Value.d);
		}
		static List<Chart.Group> ConvertGroups(List<ChartData.Group> l) {
			return l.Select(i => new Chart.Group {
				endtime = ConvertBeatTime(i.endtime),
				motions = ConvertMotions(i.motions),
				notes = ConvertNotes(i.notes),
				time = ConvertBeatTime(i.time),
				tracks = ConvertTracks(i.tracks),
			}).ToList();
		}
		static List<Chart.Judge> ConvertJudges(List<ChartData.Judge> l) {
			return l.Select(i => new Chart.Judge {
				endtime = ConvertBeatTime(i.endtime),
				name = i.name,
				time = ConvertBeatTime(i.time),
			}).ToList();
		}
		static List<Chart.Motion> ConvertMotions(List<ChartData.Motion> l) {
			return l.Select(i => new Chart.Motion {
				endtime = ConvertBeatTime(i.endtime),
				motion = i.motion,
				sumfix = i.sumfix,
				time = ConvertBeatTime(i.time),
			}).ToList();
		}
		static List<Chart.Note> ConvertNotes(List<ChartData.Note> l) {
			return l.Select(i => new Chart.Note {
				endtime = ConvertBeatTime(i.endtime),
				judges = ConvertJudges(i.judges),
				motions = ConvertMotions(i.motions),
				time = ConvertBeatTime(i.time),
			}).ToList();
		}
		static List<Chart.Signature> ConvertSignatures(List<ChartData.Signature> l) {
			return l.Select(i => new Chart.Signature {
				endtime = ConvertBeatTime(i.endtime),
				tempo = i.tempo,
				time = ConvertBeatTime(i.time),
			}).ToList();
		}
		static List<Chart.Sound> ConvertSounds(List<ChartData.Sound> l) {
			return l.Select(i => new Chart.Sound {
				endtime = ConvertBeatTime(i.endtime),
				id = i.id,
				offset = i.offset,
				time = ConvertBeatTime(i.time),
			}).ToList();
		}
		static List<Chart.Track> ConvertTracks(List<ChartData.Track> l) {
			return l.Select(i => new Chart.Track {
				endtime = ConvertBeatTime(i.endtime),
				motions = ConvertMotions(i.motions),
				time = ConvertBeatTime(i.time),
			}).ToList();
		}
	}
}
