using log4net;
using moe.yo3explorer.skyscraper8.DVBI.Model;
using skyscraper5.Skyscraper.Plugins;
using skyscraper8.yo3explorer;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
using Tsubasa.IO;

namespace skyscraper8.DvbI
{
	/// <summary>
	/// See https://www.tara-systems.de/wp-content/uploads/2024/08/DVB-I_Instant_Channel_List_Setup_IBC2024.pdf
	/// There is no official specification for this.
	/// This is a best guess reverse engineering of the DVB-I link found on 12,604/H on Astra 19,2°
	/// </summary>
	[SkyscraperPlugin]
	internal class DvbIFilesystemProcessor : FilesystemProcessorPlugin
	{
		private const string INDEX_FILENAME = "\\DVB-I_slep.xml";
		private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
		private XmlSerializer serviceListEntryPointSerializer;

		public void ProcessFilesystem(IFilesystem vfs, yo3explorerToSkyscraperContextBridge context, yo3explorerState state)
		{
			if (!vfs.FileExists(INDEX_FILENAME))
				return;

			if (context.Source == ServiceListEntryPointSource.TransportStream)
			{
				if (!context.TransportStreamId.HasValue)
					return;

				if (!context.OriginalNetworkId.HasValue)
					return;
			}
			else
			{
				throw new NotImplementedException(context.Source.ToString());
			}

			if (context.CurrentTime == DateTime.MinValue)
				return;

			DvbIDataStorage dataStorage = (DvbIDataStorage)context.DataStorage;
			long sourceHash = context.GetSourceHash();
			
			if (dataStorage.TestForDvbiServiceListEntryPoints(sourceHash))
			{
				DateTime lastChecked = dataStorage.GetLastDvbiServiceListEntryPointUpdateDate(sourceHash);
				TimeSpan sinceLastChecked = context.CurrentTime - lastChecked;
				if (sinceLastChecked.TotalDays < 1.0)
					return;
			}
			else
			{
				logger.InfoFormat("New DVB-I Service List Entry Point: {0}", context.ToHumanReadableSourceString());
				dataStorage.InsertDvbiServiceListEntryPoint(sourceHash);
			}

			byte[] slepBytes = vfs.GetFile(INDEX_FILENAME);
			ServiceListEntryPoints serviceListEntryPoints = DvbIUtils.UnpackServiceListEntryPoints(slepBytes);
			List<DvbiServiceList> enumerable = DvbIUtils.FlattenServiceListEntryPoints(serviceListEntryPoints).ToList();
			foreach(DvbiServiceList serviceList in enumerable)
			{
				DateTime serviceListLastChecked;
				if (dataStorage.TestForDvbiServiceList(serviceList.Id))
				{
					serviceListLastChecked = dataStorage.GetDvbiServiceListLastUpdateDate(serviceList.Id);
				}
				else
				{
					logger.InfoFormat("New DVB-I Service List: {0}", serviceList.Name);
					dataStorage.InsertDvbiServiceList(serviceList);
					dataStorage.AddDvbiServiceListToServiceListEntryPoint(serviceList, sourceHash);
					serviceListLastChecked = new DateTime(1970, 1, 1);
				}

				TimeSpan sinceLastServiceListCheck = context.CurrentTime - serviceListLastChecked;
				if (sinceLastServiceListCheck.TotalDays < 1.0)
					continue;

				if (vfs.FileExists(serviceList.URI))
				{
					byte[] serviceListBytes = vfs.GetFile(serviceList.URI);
					ServiceListType serviceListData = DvbIUtils.UnpackServiceList(serviceListBytes);
					HandleServiceList(serviceListData, dataStorage, serviceList.Id);
					if (!dataStorage.TestForDvbiServiceList(serviceList.Id))
					{
						dataStorage.InsertDvbiServiceList(serviceList);
					}
					else
					{
						dataStorage.UpdateDvbiServiceListLastCheckedDate(serviceList.Id, context.CurrentTime);
					}
				}
			}

			dataStorage.UpdateDvbiServiceListEntryPointUpdateDate(sourceHash, context.CurrentTime);
		}

		private void HandleServiceList(ServiceListType serviceListData, DvbIDataStorage dataStorage, string serviceListId)
		{
			IEnumerable<DvbIService> services = DvbIUtils.FlattenServiceList(serviceListData);
			foreach(DvbIService service in services)
			{
				if (dataStorage.TestForDvbiService(service.Id))
				{
					int versionInDb = dataStorage.GetDvbiServiceVersion(service.Id);
					if (service.Version > versionInDb)
					{
						dataStorage.UpdateDvbiService(service);
					}
				}
				else
				{
					logger.InfoFormat("New DVB-I Service: {0}", service.ServiceName);
					dataStorage.InsertDvbiService(service);
					dataStorage.AddDvbiServiceToServiceList(service.Id, serviceListId);
				}
			}
		}
	}
}
