using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.src.InteractionChannel;
using skyscraper5.src.InteractionChannel.Model;
using skyscraper5.src.InteractionChannel.Model2;
using skyscraper8.GSE.GSE;
using skyscraper8.InteractionChannel;
using skyscraper8.InteractionChannel.Model;
using skyscraper8.InteractionChannel.Model2;

namespace skyscraper8.GS.GSE_BFBS
{
	internal class GseL2SHandler
	{
		public GsContextDto Context { get; }

		public GseL2SHandler(GsContextDto context)
		{
			Context = context;
		}

		public void PushPacket(byte[] buffer, GseLabel label)
		{
			GseTableStructure gseTableStructure = new GseTableStructure(buffer);
			if (!gseTableStructure.Valid)
			{
				Context.Rcs2Output.OnInteractionChannelError(InteractionChannelErrorState.GseTableStructureInvalid);
				return;
			}

			MemoryStream ms = new MemoryStream(gseTableStructure.TableContent, false);
			switch (gseTableStructure.TableId)
			{
				case 0x40:
					RcsNit nit = new RcsNit(ms);
					if (!nit.Valid)
					{
						Context.Rcs2Output.OnInteractionChannelError(InteractionChannelErrorState.NitInvalid);
						return;
					}

					Context.Rcs2Output.OnRcs2Nit(gseTableStructure.InteractiveNetworkId, nit);
					return;
				case 0x41:
					Rmt rmt = new Rmt(ms, true, gseTableStructure.InteractiveNetworkId);
					if (!rmt.Valid)
					{
						Context.Rcs2Output.OnInteractionChannelError(InteractionChannelErrorState.RmtInvalid);
						return;
					}

					Context.Rcs2Output.OnRcsMap(rmt);
					return;
				case 0x70:
					Rcs2Tdt tdt = new Rcs2Tdt(ms);
					if (!tdt.Valid)
					{
						Context.Rcs2Output.OnInteractionChannelError(InteractionChannelErrorState.TdtInvalid);
						return;
					}

					Context.Rcs2Output.OnRcs2Tdt(gseTableStructure.InteractiveNetworkId, tdt);
					return;
				case 0xa0:
					Sct sct = new Sct(ms);
					if (!sct.Valid)
					{
						Context.Rcs2Output.OnInteractionChannelError(InteractionChannelErrorState.SctInvalid);
						return;
					}

					Context.Rcs2Output.OnSuperframeComposition(gseTableStructure.InteractiveNetworkId, sct);
					return;
				case 0xa3:
					Spt spt = new Spt(ms);
					if (!spt.Valid)
					{
						Context.Rcs2Output.OnInteractionChannelError(InteractionChannelErrorState.SptInvalid);
						return;
					}

					Context.Rcs2Output.OnSatellitePosition(gseTableStructure.InteractiveNetworkId, spt);
					return;
				case 0xa4:
					Cmt cmt = new Cmt(ms);
					if (!cmt.Valid)
					{
						Context.Rcs2Output.OnInteractionChannelError(InteractionChannelErrorState.CmtInvalid);
						return;
					}
					Context.Rcs2Output.OnCorrectionMessage(gseTableStructure.InteractiveNetworkId, cmt);
					return;
				case 0xab:
					Fct2 fct2 = new Fct2(ms);
					if (!fct2.Valid)
					{
						Context.Rcs2Output.OnInteractionChannelError(InteractionChannelErrorState.Fct2Invalid);
						return;
					}
					Context.Rcs2Output.OnFrameComposition2(gseTableStructure.InteractiveNetworkId, fct2);
					return;
				case 0xac:
					Bct bct = new Bct(ms);
					if (!bct.Valid)
					{
						Context.Rcs2Output.OnInteractionChannelError(InteractionChannelErrorState.BctInvalid);
						return;
					}
					Context.Rcs2Output.OnBroadcastConfiguration(gseTableStructure.InteractiveNetworkId, bct);
					return;
				case 0xad:
					Tbtp2 tbtp2 = new Tbtp2(ms);
					if (!tbtp2.Valid)
					{
						Context.Rcs2Output.OnInteractionChannelError(InteractionChannelErrorState.Tbtp2Invalid);
						return;
					}
					Context.Rcs2Output.OnTerminalBurstTimePlan2(gseTableStructure.InteractiveNetworkId, tbtp2);
					return;
				case 0xae:
					int rmtTransmissionStandard = Context.Rcs2Output.GetRmtTransmissionStandard(gseTableStructure.InteractiveNetworkId);
					Tmst2 tmst2 = new Tmst2(ms, rmtTransmissionStandard);
					if (!tmst2.Valid)
					{
						Context.Rcs2Output.OnInteractionChannelError(InteractionChannelErrorState.Tmst2Invalid);
						return;
					}
					Context.Rcs2Output.OnTransmissionModeSupport2(gseTableStructure.InteractiveNetworkId, tmst2);
					return;
				case 0xb0:
					Tim tim = new Tim(ms, label.IsBroadcast());
					if (!tim.Valid)
					{
						Context.Rcs2Output.OnInteractionChannelError(InteractionChannelErrorState.TimInvalid);
						return;
					}

					PhysicalAddress physicalAddress;
					if (label.IsBroadcast())
					{
						physicalAddress = _6byteLabel.BROADCAST;
					}
					else if (label.Length == 6)
					{
						physicalAddress = ((_6byteLabel)label).MAC;
					}
					else
					{
						throw new NotImplementedException("Found a TIM-U Table with a 3-byte GSE Label in this stream. This is not supported yet, please share a sample of this stream.");
					}

					Context.Rcs2Output.OnTerminalInformationMessage(gseTableStructure.InteractiveNetworkId, physicalAddress, tim);
					return;
				case 0xc0:
					//User defined, we have no way of knowing what this is.
					return;
				default:
					//See en_30154502v010401p.pdf
					//page 49
					throw new NotImplementedException(String.Format(
						"Unknown DVB-RCS2 Table Id: 0x{0:X2}\nIf this is reproducible on this stream, please consider submitting me a sample.",
						gseTableStructure.TableId));
			}
		}
	}
}
