using log4net;
using MimeKit;
using skyscraper5.Skyscraper.IO;
using skyscraper8.Ietf.FLUTE;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;

namespace skyscraper8.DvbNip
{
	public class DvbNipUtilities
	{
		private DvbNipUtilities() { }

		private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
		private static XmlSerializer multicastGatewayConfigurationXmlSerializer;

		public static bool IsXml(Stream s)
		{
			byte byteA = s.ReadUInt8();
			byte byteB = s.ReadUInt8();
			byte byteC = s.ReadUInt8();
			byte byteD = s.ReadUInt8();
			byte byteE = s.ReadUInt8();
			byte byteF = s.ReadUInt8();
			s.Position -= 6;

			if (byteA != '<')
				return false;

			if (byteB != '?')
				return false;

			if (byteC != 'x')
				return false;

			if (byteD != 'm')
				return false;

			if (byteE != 'l')
				return false;

			if (byteF != ' ')
				return false;

			return true;
		}

		private static readonly byte[] MimeVersionMagic = new byte[]
		{
			0x4D, 0x49, 0x4D, 0x45, 0x2D, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x3A, 0x20
		};

		public static bool IsMime(Stream s)
		{
			if (s is GZipStream)
				return false;

			if (s.GetAvailableBytes() <= MimeVersionMagic.Length)
			{
				return false;
			}

			byte[] buffer = new byte[MimeVersionMagic.Length];
			int bufferReadResult = s.Read(buffer, 0, buffer.Length);
			bool isMime = Enumerable.SequenceEqual(buffer, MimeVersionMagic);
			s.Position -= bufferReadResult;
			return isMime;
		}


		public static MulticastGatewayConfigurationType UnpackMulticastGatewayConfiguration(Stream stream)
		{
			if (multicastGatewayConfigurationXmlSerializer == null)
			{
				multicastGatewayConfigurationXmlSerializer = new XmlSerializer(typeof(MulticastGatewayConfigurationType));
			}

			object v = multicastGatewayConfigurationXmlSerializer.Deserialize(stream);
			MulticastGatewayConfigurationType result = (MulticastGatewayConfigurationType)v;
			return result;
		}

		public static string MakeFilename(string originalLocation)
		{
			string newFilename = originalLocation.Replace("://", "/");
			newFilename = originalLocation.Replace(":", "/");

			while (newFilename.Contains("//"))
				newFilename = newFilename.Replace("//", "/");

			if (string.IsNullOrEmpty(Path.GetExtension(newFilename)))
				newFilename += ".dat";

			newFilename = newFilename.Replace("%3D", "-3D-");

			return newFilename;
		}

		public static bool IsContinuousFileType(string extension)
		{
			switch(extension)
			{
				case ".mpd":
				case ".m4s":
				case ".m3u8":
				case ".ts":
					return true;
				default:
					return false;
			}
		}

		public static bool IsContinuousFileType(FileType filteType)
		{
			return IsContinuousFileType(Path.GetExtension(filteType.ContentLocation));
		}

		private static XmlSerializer privateDataSignallingManifestXmlSerializer;
		public static PrivateDataSignallingManifestType UnpackPrivateDataSignallingManifest(Stream toStream)
		{
			if (privateDataSignallingManifestXmlSerializer == null)
			{
				privateDataSignallingManifestXmlSerializer = new XmlSerializer(typeof(PrivateDataSignallingManifestType));
			}

			byte[] buffer = new byte[toStream.Length];
			toStream.Read(buffer, 0, (int)toStream.Length);
			string bufferString = Encoding.UTF8.GetString(buffer, 0, (int)toStream.Length);
			if (bufferString.Contains("urn:dvb:metadata:nativeip:2023"))
			{
				bufferString = bufferString.Replace("urn:dvb:metadata:nativeip:2023", "urn:dvb:metadata:nativeip:2024");
			}

			object v = privateDataSignallingManifestXmlSerializer.Deserialize(new StringReader(bufferString));
			PrivateDataSignallingManifestType result = (PrivateDataSignallingManifestType)v;
			return result;
		}

		private static XmlSerializer timeOffsetFileXmlSerializer;

		public static TimeOffsetFileType UnpackTimeOffsetFile(Stream toStream)
		{
			if (timeOffsetFileXmlSerializer == null)
			{
				timeOffsetFileXmlSerializer = new XmlSerializer(typeof(TimeOffsetFileType));
			}

			byte[] buffer = new byte[toStream.Length];
			toStream.Read(buffer, 0, (int)toStream.Length);
			string bufferString = Encoding.UTF8.GetString(buffer, 0, (int)toStream.Length);
			if (bufferString.Contains("urn:dvb:metadata:nativeip:2023"))
			{
				bufferString = bufferString.Replace("urn:dvb:metadata:nativeip:2023", "urn:dvb:metadata:nativeip:2024");
			}


			object v = timeOffsetFileXmlSerializer.Deserialize(new StringReader(bufferString));
			TimeOffsetFileType result = (TimeOffsetFileType)v;
			return result;
		}

		private static XmlSerializer networkInformationFileXmlSerializer;
		public static NetworkInformationFileType UnpackNetworkInformationFile(Stream toStream)
		{
			if (networkInformationFileXmlSerializer == null)
			{
				networkInformationFileXmlSerializer = new XmlSerializer(typeof(NetworkInformationFileType));
			}

			byte[] buffer = new byte[toStream.Length];
			toStream.Read(buffer, 0, (int)toStream.Length);
			string bufferString = Encoding.UTF8.GetString(buffer, 0, (int)toStream.Length);
			if (bufferString.Contains("urn:dvb:metadata:nativeip:2023"))
			{
				bufferString = bufferString.Replace("urn:dvb:metadata:nativeip:2023", "urn:dvb:metadata:nativeip:2024");
			}
			else if (bufferString.Contains("urn:dvb:metadata:nativeip:2022"))
			{
				bufferString = bufferString.Replace("urn:dvb:metadata:nativeip:2022", "urn:dvb:metadata:nativeip:2024");
			}

			object v = networkInformationFileXmlSerializer.Deserialize(new StringReader(bufferString));
			NetworkInformationFileType result = (NetworkInformationFileType)v;
			return result;
		}

		private static XmlSerializer serviceInformationFileXmlSerializer;
		public static ServiceInformationFileType UnpackServiceInformationFile(Stream toStream)
		{
			if (serviceInformationFileXmlSerializer == null)
			{
				serviceInformationFileXmlSerializer = new XmlSerializer(typeof(ServiceInformationFileType));
			}
			
			byte[] buffer = new byte[toStream.Length];
			toStream.Read(buffer, 0, (int)toStream.Length);
			string bufferString = Encoding.UTF8.GetString(buffer, 0, (int)toStream.Length);
			if (bufferString.Contains("urn:dvb:metadata:nativeip:2023"))
			{
				bufferString = bufferString.Replace("urn:dvb:metadata:nativeip:2023", "urn:dvb:metadata:nativeip:2024");
			}
			else if (bufferString.Contains("urn:dvb:metadata:nativeip:2022"))
			{
				bufferString = bufferString.Replace("urn:dvb:metadata:nativeip:2022", "urn:dvb:metadata:nativeip:2024");
			}

			object v = serviceInformationFileXmlSerializer.Deserialize(new StringReader(bufferString));
			ServiceInformationFileType result = (ServiceInformationFileType)v;
			return result;
		}

		public static Stream GetFirstMimeBodyPart(Stream stream)
		{
			MimeMessage message = MimeMessage.Load(stream);
			foreach (MimeEntity bodyPart in message.BodyParts)
			{
				MimePart part = bodyPart as MimePart;
				if (part.ContentType.MimeType.Equals("application/mbms-envelope+xml"))
					continue;
				if (part.ContentType.MimeType.Equals("application/mbms-user-service-description+xml"))
					continue;
				if (part.ContentType.MimeType.Equals("application/mbms-schedule+xml"))
					continue;
				return part.Content.Stream;
			}
			throw new NotImplementedException();
		}

		public static FluteListener PatchMpegDashSegment(FluteListener listener)
		{
			Stream stream = listener.ToStream();
			stream.Position = stream.Length - 1;
			byte readUInt8 = stream.ReadUInt8();
			if (readUInt8 != 0)
				return listener;

			logger.WarnFormat("MPEG-DASH segment {0} is overdumped. It will be patched to become playable.", listener.FileAssociation.ContentLocation);
			stream.Position = stream.Length - 1;
			while (stream.ReadUInt8() == 0)
			{
				stream.Position -= 2;
			}

			listener.TrimmedLength = stream.Position;
			return listener;
		}

	}
}
