using skyscraper5.Ietf.Rfc768;
using skyscraper5.Ietf.Rfc971;
using skyscraper5.Skyscraper.Plugins;
using skyscraper8.Ietf.FLUTE;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace skyscraper8.DvbNip
{
	[SkyscraperPlugin]
	internal class DvbNipReceiver : ISkyscraperMpePlugin, IFluteEventHandler
	{
		private DateTime? contextDate;
		private object[] contextConnector;
		private DvbNipPhase phase;
		private FluteListener flute;


		public bool CanHandlePacket(InternetHeader internetHeader, byte[] ipv4Packet)
		{
			byte[] destinationBytes = internetHeader.DestinationAddress.GetAddressBytes();
			int multicastTemp = destinationBytes[0];
			multicastTemp &= 0b11100000;
			multicastTemp >>= 4;
			if (multicastTemp != 0b1110)
				return false;

			if (internetHeader.Protocol != 17)
				return false;

			return true;
		}

		public void ConnectToStorage(object[] connector)
		{
			contextConnector = connector;
		}

		public void HandlePacket(InternetHeader internetHeader, byte[] ipv4Packet)
		{
			UserDatagram udpPacket = new UserDatagram(ipv4Packet);
			flute.OnFlutePacket(internetHeader.DestinationAddress, udpPacket.DestinationPort, udpPacket.Payload);
		}

		public void InformToi(IPAddress destinationAddress, ushort destinationPort, ulong transportObjectIdentifier, ulong transportSessionIdentifier)
		{
			if (phase == DvbNipPhase.DownloadMode)
			{
				flute.AddPlay(destinationAddress, destinationPort, transportSessionIdentifier, 0);
			}
		}

		public bool IpFilterEnabled()
		{
			if (phase == DvbNipPhase.DownloadMode)
				return false;
			else
				return true;
		}

		public void OnCompletePlay(FluteListener.FlutePlay flutePlay)
		{
			if (phase == DvbNipPhase.WaitingForSignalling)
			{
				phase = DvbNipPhase.DownloadMode;
				return;
			}
			else if (phase == DvbNipPhase.DownloadMode)
			{
				if (!flutePlay.Validate())
				{
					flutePlay.SetIgnore(true);
					flutePlay.SetIgnore(false);
					return;
				}
				if (flutePlay.TOI == 0)
				{
					FDTInstanceType fdtInstance = FluteUtilities.UnpackFluteFdt(flutePlay.GetStream());
					foreach(FileType filetype in fdtInstance.File)
					{
						flute.SetFileAssociation(flutePlay.IPAddress, flutePlay.Port, flutePlay.TSI, filetype.TOI, filetype);
					}
					return;
				}
				else
				{
					if (flutePlay.FileAssociation.ContentLocation.Equals("urn:dvb:metadata:cs:MulticastTransportObjectTypeCS:2021:gateway-configuration"))
					{
						MulticastGatewayConfigurationType multicastGatewayConfigurationType = DvbNipUtilities.UnpackMulticastGatewayConfiguration(flutePlay.GetStream());
						//TODO: Handle Multicast Gateway configuration
						flutePlay.SetIgnore(true);
						return;
					}
					flutePlay.WriteToFile("test.xml");
				}
				flutePlay.SetIgnore(true);
			}
			else
			{
				throw new NotImplementedException(phase.ToString());
			}
		}

		public void OnNipCarrierInformation(NipActualCarrierInformation nipActualCarrierInformation)
		{
			if (phase == DvbNipPhase.WaitingForCarrierInformation)
			{
				phase = DvbNipPhase.WaitingForSignalling;
				flute.RemoveAllPlays();
				flute.AddPlay(IPAddress.Parse("224.0.23.14"), 3937, 0, 0);
			}
		}

		public void SetContext(DateTime? currentTime)
		{
			contextDate = currentTime;
			switch(phase)
			{
				case DvbNipPhase.JustPoweredUp:
					flute = new FluteListener(this);
					flute.AddPlay(IPAddress.Parse("224.0.23.14"), 3937, 1, 0);
					phase = DvbNipPhase.WaitingForCarrierInformation;
					break;
				case DvbNipPhase.WaitingForCarrierInformation:
					break;
				case DvbNipPhase.WaitingForSignalling:
					break;
				case DvbNipPhase.DownloadMode:
					break;
				default:
					throw new NotImplementedException(phase.ToString());
			}
		}

		public bool StopProcessingAfterThis()
		{
			return false;
		}

		private enum DvbNipPhase
		{
			JustPoweredUp,
			WaitingForCarrierInformation,
			WaitingForSignalling,
			DownloadMode,
		}
	}
}
