using Cryville.EEW.Colors;
using Cryville.EEW.Core.Colors;
using Cryville.EEW.FERegion;
using Cryville.EEW.Map;
using Cryville.EEW.Report;
using Cryville.EEW.TTS;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text.Json;
using UnityEngine;
using Color = System.Drawing.Color;

namespace Cryville.EEW.Unity {
	sealed class SharedSettings : IRVMGeneratorContext, IMapGeneratorContext, ITTSMessageGeneratorContext {
		static SharedSettings s_instance;
		public static SharedSettings Instance => s_instance ??= new();

		public ISeverityScheme SeverityScheme { get; private set; } = DefaultSeverityScheme.Instance;
		public ISeverityColorMapping SeverityColorMapping { get; private set; } = DefaultSeverityColorMapping.Instance;
		public bool UseContinuousColor { get; private set; } = true;
		public IColorScheme ColorScheme { get; private set; } = new SeverityBasedColorScheme(DefaultSeverityScheme.Instance, DefaultSeverityColorMapping.Instance);
		public ISubColorScheme BorderColorScheme { get; private set; } = new WrappedColorScheme(new SeverityBasedColorScheme(DefaultSeverityScheme.Instance, DefaultSeverityColorMapping.SecondaryInstance));
		public ISubColorScheme TextColorScheme { get; private set; } = new DefaultTextColorScheme(Color.White, Color.Black);
		public ILocationConverter LocationConverter => new FERegionLongConverter(); // TODO TTS
		public TimeSpan NowcastWarningDelayTolerance => TimeSpan.FromMinutes(60); // TODO TTS

		public TimeZoneInfo OverrideTimeZone { get; private set; }
		public bool DoDisplayTimeZone { get; private set; } = true;
		public bool DoSwitchBackToHistory { get; private set; } = true;

		public IReadOnlyCollection<EventSourceConfig> EventSources { get; private set; }

		public string Id { get; private set; }
		public byte[] IdBytes { get; } = new byte[32];
		public string UnityUserAgent { get; private set; }

		public void Init() {
			Id = SystemInfo.deviceUniqueIdentifier;
			using var hash = SHA256.Create();
			hash.ComputeHash(EEW.SharedSettings.Encoding.GetBytes(Id)).CopyTo(IdBytes, 0);
#if UNITY_STANDALONE_WIN
			UnityUserAgent = string.Format(
				CultureInfo.InvariantCulture,
				"{0} CysTerraUnity/{1} (Windows NT {2}; {3})",
				EEW.SharedSettings.UserAgent,
				Application.version,
				Environment.OSVersion.Version,
				RuntimeInformation.OSArchitecture.ToString().ToLowerInvariant()
			);
#else
#error No Unity User Agent
#endif

			var file = new FileInfo(Path.Combine(PlatformConfig.ConfigPath, "config.json"));
			if (!file.Exists) {
				using var stream1 = file.OpenWrite();
				using var writer = new StreamWriter(stream1, EEW.SharedSettings.Encoding);
				writer.Write(JsonSerializer.Serialize(Config.Default, ConfigSerializationContext.Default.Config));
			}
			using var stream = file.OpenRead();
			var config = JsonSerializer.Deserialize(stream, ConfigSerializationContext.Default.Config) ?? throw new InvalidOperationException("Null config.");

			SeverityScheme = config.SeverityScheme switch {
				"Default" => new DefaultSeverityScheme(),
				"Legacy" => new LegacySeverityScheme(),
				_ => throw new InvalidOperationException("Unknown severity scheme."),
			};
			SeverityColorMapping = config.ColorScheme switch {
				"Default" => new DefaultSeverityColorMapping(config.SeverityColorMappingLuminanceMultiplier),
				"SREV" => new SREVSeverityColorMapping(config.SeverityColorMappingLuminanceMultiplier),
				"DichromaticYB" => new DichromaticSeverityColorMapping(0.62f, 0.20f, 90, config.SeverityColorMappingLuminanceMultiplier),
				"DichromaticRC" => new DichromaticSeverityColorMapping(0.62f, 0.25f, 30, config.SeverityColorMappingLuminanceMultiplier),
				"DichromaticPG" => new DichromaticSeverityColorMapping(0.62f, 0.30f, -30, config.SeverityColorMappingLuminanceMultiplier),
				"Monochromatic" => new MonochromaticSeverityColorMapping(config.SeverityColorMappingLuminanceMultiplier),
				_ => throw new InvalidOperationException("Unknown color scheme."),
			};
			UseContinuousColor = config.UseContinuousColor;
			ColorScheme = config.ColorScheme switch {
				"Default" => new SeverityBasedColorScheme(SeverityScheme, DefaultSeverityColorMapping.Instance),
				"SREV" => new SREVColorScheme(),
				"DichromaticYB" => new SeverityBasedColorScheme(SeverityScheme, new DichromaticSeverityColorMapping(0.62f, 0.20f, 90)),
				"DichromaticRC" => new SeverityBasedColorScheme(SeverityScheme, new DichromaticSeverityColorMapping(0.62f, 0.25f, 30)),
				"DichromaticPG" => new SeverityBasedColorScheme(SeverityScheme, new DichromaticSeverityColorMapping(0.62f, 0.30f, -30)),
				"Monochromatic" => new SeverityBasedColorScheme(SeverityScheme, new MonochromaticSeverityColorMapping()),
				_ => throw new InvalidOperationException("Unknown color scheme."),
			};
			BorderColorScheme = config.ColorScheme switch {
				"Default" => new WrappedColorScheme(new SeverityBasedColorScheme(SeverityScheme, DefaultSeverityColorMapping.SecondaryInstance)),
				"SREV" => new WrappedColorScheme(new SREVBorderColorScheme()),
				"DichromaticYB" => new WrappedColorScheme(new SeverityBasedColorScheme(SeverityScheme, new DichromaticSeverityColorMapping(0.62f, 0.20f, 90, 0.75f))),
				"DichromaticRC" => new WrappedColorScheme(new SeverityBasedColorScheme(SeverityScheme, new DichromaticSeverityColorMapping(0.62f, 0.25f, 30, 0.75f))),
				"DichromaticPG" => new WrappedColorScheme(new SeverityBasedColorScheme(SeverityScheme, new DichromaticSeverityColorMapping(0.62f, 0.30f, -30, 0.75f))),
				"Monochromatic" => new WrappedColorScheme(new SeverityBasedColorScheme(SeverityScheme, new MonochromaticSeverityColorMapping(0.75f))),
				_ => throw new InvalidOperationException("Unknown color scheme."),
			};
			TextColorScheme = config.ColorScheme switch {
				"SREV" => new DefaultTextColorScheme(Color.White, Color.FromArgb(28, 28, 28), 0.555f),
				_ => new DefaultTextColorScheme(Color.White, Color.Black),
			};
			OverrideTimeZone = ParseTimeZone(config.OverrideTimeZone);
			DoDisplayTimeZone = config.DoDisplayTimeZone;
			DoSwitchBackToHistory = config.DoSwitchBackToHistory;
			EventSources = config.EventSources;
		}

		TimeZoneInfo ParseTimeZone(string timeZone) {
			if (timeZone == null) return null;
			if (timeZone == "") return TimeZoneInfo.Local;
			return TimeZoneInfo.CreateCustomTimeZone("Custom", TimeSpan.Parse(timeZone, CultureInfo.InvariantCulture), null, null);
		}
	}
}
