using log4net;
using skyscraper5.Ietf.Rfc971;
using skyscraper5.Skyscraper.Plugins;
using skyscraper8.Skyscraper.Scraper;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Ietf.Rfc768;
using skyscraper5.Skyscraper;
using skyscraper5.Skyscraper.IO;
using skyscraper8.GS.SiminnRadiomidun;

namespace skyscraper8.Skyscraper
{
	[SkyscraperPlugin]
	internal class MpeEjectBfbs : ISkyscraperMpePlugin
	{
		private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);

		public void ConnectToStorage(object[] connector)
		{
			throw new NotImplementedException();
		}

		private ISubTsHandler subTsHandler;
		public void SetContext(DateTime? currentTime, object skyscraperContext)
		{
			subTsHandler = skyscraperContext as ISubTsHandler;
			if (subTsHandler == null)
			{
				logger.ErrorFormat("Couldn't get a handle for the SubTsHandler. Deencapsulation will not work.");
			}
		}

		class Session
		{
			public Session(IPEndPoint sourceAddress, IPEndPoint targetAddress, uint sessionCounter)
			{
				SourceAddress = sourceAddress;
				TargetAddress = targetAddress;
				SessionCounter = sessionCounter;
			}

			public IPEndPoint SourceAddress { get; private set; }
			public IPEndPoint TargetAddress { get; private set; }
			public uint SessionCounter { get; private set; }

			public int ContinuityCounter { get; private set; }

			public void Touch(uint nextValue)
			{
				if (nextValue == SessionCounter + 1)
				{
					ContinuityCounter++;
					SessionCounter = nextValue;
				}
				else
				{
					ContinuityCounter = 0;
					SessionCounter = nextValue;
				}
			}

			public override string ToString()
			{
				return String.Format("{0} -> {1}", SourceAddress, TargetAddress).SanitizeFileName();
			}
		}

		private List<Session> sessions;

		public bool CanHandlePacket(InternetHeader internetHeader, byte[] ipv4Packet)
		{
			if (internetHeader.Protocol != 17)
			{
				return false;
			}
			

			//check length
			int length = ipv4Packet.Length;
			length -= 14;
			if (length == 0)
			{
				return false;
			}

			length %= 188;
			if (length != 0)
			{
				return false;
			}

			//check magic
			if (ipv4Packet[12] != 0x00)
			{
				return false;
			}

			if (ipv4Packet[13] != 0x10)
			{
				return false;
			}

			if (ipv4Packet[14] != 0x47)
			{
				return false;
			}

			return true;
		}

		private bool sucessfulPass;
		public void HandlePacket(InternetHeader internetHeader, byte[] ipv4Packet)
		{
			sucessfulPass = false;
			UserDatagram userDatagram = new UserDatagram(ipv4Packet);
			IPEndPoint source = new IPEndPoint(internetHeader.SourceAddress, userDatagram.SourcePort);
			IPEndPoint target = new IPEndPoint(internetHeader.DestinationAddress, userDatagram.DestinationPort);

			MemoryStream ms = new MemoryStream(userDatagram.Payload, false);
			uint sequence = ms.ReadUInt32BE();
			if (ms.ReadUInt8() != 0x00)
				return;
			if (ms.ReadUInt8() != 0x10)
				return;

			if (sessions == null)
				sessions = new List<Session>();

			Session session = sessions.Find(x => x.SourceAddress.Equals(source) && x.TargetAddress.Equals(target));
			if (session != null)
			{
				if (session.ContinuityCounter >= 3)
				{
					while (ms.GetAvailableBytes() >= 188)
					{
						byte[] readBytes = ms.ReadBytes(188);
						subTsHandler.OnSubTsPacket(session, readBytes);
						sucessfulPass = true;
					}
					return;
				}
				else
				{
					session.Touch(sequence);
					return;
				}
			}
			else
			{
				session = new Session(source, target, sequence);
				sessions.Add(session);
				return;
			}
		}

		public bool StopProcessingAfterThis()
		{
			return sucessfulPass;
		}
	}
}
