using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Mpeg2;
using skyscraper5.Skyscraper.IO;
using skyscraper5.Skyscraper.Plugins;
using skyscraper5.Skyscraper.Scraper;
using skyscraper5.Skyscraper.Scraper.StreamAutodetection;
using skyscraper8.Mpeg2.Psi;

namespace skyscraper8.Experimentals.OtvSsu
{
	[SkyscraperPlugin]
	internal class OtvSsuDetector : Contestant, OtvSsuHandler
	{
		private const int MAXIMUM_FILE_SIZE = 33554432;	//32MB.
														//Let's use a limit here to avoid detecting too many large files and false positives.

		private OtvSsuDetectorParser parser;
		public OtvSsuDetector(int pid) : base("OpenTV SSU", pid)
		{
			parser = new OtvSsuDetectorParser(this, pid);
			PacketProcessor = new PsiDecoder(pid, parser);
		}

		public override void Dispose()
		{
			parser = null;
			PacketProcessor = null;
		}

		public override void DeclareWinner(SkyscraperContext skyscraperContext, int pid, ProgramContext programContext)
		{
			skyscraperContext.DvbContext.RegisterPacketProcessor(pid, new PsiDecoder(pid,new OtvSsuParser(skyscraperContext)));
		}

		public override void Introduce(ProgramContext programContext)
		{
		}

		class OtvSsuDetectorParser : PrivateSectionParser
		{
			private readonly OtvSsuHandler _handler;
			private readonly int _pid;
			private List<Coordinate> _coordinates;

			public OtvSsuDetectorParser(OtvSsuHandler handler, int pid)
			{
				_handler = handler;
				_pid = pid;
				_coordinates = new List<Coordinate>();
			}

			class Coordinate
			{
				public ushort tableIdExtension;
				public uint fileId;
				public uint unknown1;
				public uint length;

				public Coordinate(ushort tableIdExtension1, uint u, uint unknown2, uint length1)
				{
					this.tableIdExtension = tableIdExtension1;
					this.fileId = u;
					this.unknown1 = unknown2;
					this.length = length1;
				}

				public long hits;
			}

			protected override void HandleTable(byte tableId, int sourcePid, byte sectionNumber, byte lastSectionNumber, ushort tableIdExtension,
				byte[] payload)
			{
				MemoryStream ms = new MemoryStream(payload, false);
				uint fileId = ms.ReadUInt32BE();
				uint unknown1 = ms.ReadUInt32BE();
				uint offset = ms.ReadUInt32BE();
				uint length = ms.ReadUInt32BE();
				if (length > MAXIMUM_FILE_SIZE)
				{
					_handler.OnOtvSsuError(_pid, OtvSsuError.FileTooLarge);
					return;
				}
				if (offset > length)
				{
					_handler.OnOtvSsuError(_pid,OtvSsuError.OffsetOutOfRange);
					return;
				}

				if (tableIdExtension == 1 && (fileId & 0x00ff0000) == 0x00ff0000)
				{
					_handler.OnOtvSsuError(_pid, OtvSsuError.NdsOverlap);
					return;
				}

				Coordinate coordinate = _coordinates.Find(x =>
					x.tableIdExtension == tableIdExtension && x.fileId == fileId && x.unknown1 == unknown1 &&
					x.length == length);
				if (coordinate == null)
				{
					coordinate = new Coordinate(tableIdExtension, fileId, unknown1, length);
					_handler.OnOtvSsuFileAnnouncement(_pid,tableIdExtension, fileId, unknown1, length);
					_coordinates.Add(coordinate);
				}
				else
				{
					coordinate.hits++;
					_handler.OnOtvSsuBlock(_pid, tableIdExtension, fileId, unknown1, length);
				}
			}

			protected override void HandleTable(byte tableId, int sourcePid, byte[] payload)
			{
				_handler.OnOtvSsuError(_pid, OtvSsuError.SectionSyntaxIndicatorMissing);
			}
		}

		public void OnOtvSsuError(int pid, OtvSsuError offsetOutOfRange)
		{
			Score--;
		}

		
		public void OnOtvSsuFileAnnouncement(int pid, ushort tableIdExtension, uint fileId, uint unknown1, uint length)
		{
			Score--;
		}

		public void OnOtvSsuBlock(int pid, ushort tableIdExtension, uint fileId, uint unknown1, uint length)
		{
			Score++;
		}

		public void OnOtvSsuComplete(int sourcePid, Stream getStream, ushort tableIdExtension, uint fileId, uint unknown1,
			uint length)
		{
			throw new NotImplementedException();
		}

		public bool OnOtvCheckFileAlreadyKnown(int sourcePid, ushort tableIdExtension, uint fileId, uint unknown1, uint length)
		{
			throw new NotImplementedException();
		}
	}
}
