using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Villain.Networking.Packets
{
	public class Packet : IPacket, IDisposable
	{
		protected MemoryStream _memoryStream;
		public BinaryReader BinaryReader;
		public BinaryWriter BinaryWriter;

		public Int64 MissingDataLength
		{
			get;
			protected set;
		}
		public byte[] ExtraData
		{
			get;
			set;
		}

		public Packet()
		{
			_memoryStream = new MemoryStream();
			BinaryWriter = new BinaryWriter(_memoryStream);
		}

		public Packet(byte[] data)
		{
			_memoryStream = new MemoryStream(data);
			BinaryReader = new BinaryReader(_memoryStream);
		}

		~Packet()
		{
			Dispose();
		}
		
		public void Dispose()
		{
			_memoryStream.Dispose();
			BinaryReader?.Dispose();
			BinaryWriter?.Dispose();
		}

		public virtual byte[] AppendMissingData(byte[] data)
		{
			byte[] remainingData;

			if (data.Length > MissingDataLength)
			{
				Int32 readableMissingDataLength;

				if (MissingDataLength > Int32.MaxValue)
				{
					readableMissingDataLength = Int32.MaxValue;
					MissingDataLength -= Int32.MaxValue;
				}
				else
				{
					readableMissingDataLength = (Int32)MissingDataLength;
					MissingDataLength = 0;
				}

				remainingData = new byte[(data.Length - readableMissingDataLength)];

				_memoryStream.Write(data, 0, readableMissingDataLength);

				Buffer.BlockCopy(data, readableMissingDataLength, remainingData, 0, remainingData.Length);
			}
			else
			{
				remainingData = new byte[0];

				_memoryStream.Write(data, 0, data.Length);

				MissingDataLength -= data.Length;
			}

			return remainingData;
		}

		protected virtual void HandleExtraData(UInt16 length)
		{
			Int16 lengthBytesLength = sizeof(byte);

			if (length > 0xFF)
			{
				lengthBytesLength += sizeof(UInt16);
			}

			if (_memoryStream.Length > (length + lengthBytesLength))
			{
				byte[] buffer = _memoryStream.ToArray();

				Int32 readExtraDataCount;
				Int64 remainingExtraDataInBufferLength = (_memoryStream.Length - (length + lengthBytesLength));

				ExtraData = new byte[remainingExtraDataInBufferLength];

				while (remainingExtraDataInBufferLength > 0)
				{
					if (remainingExtraDataInBufferLength > Int32.MaxValue)
					{
						readExtraDataCount = Int32.MaxValue;
					}
					else
					{
						readExtraDataCount = (Int32)remainingExtraDataInBufferLength;
					}

					Buffer.BlockCopy(buffer, (length + lengthBytesLength), ExtraData, (Int32)(ExtraData.Length - remainingExtraDataInBufferLength), readExtraDataCount);

					remainingExtraDataInBufferLength -= readExtraDataCount;
				}

				byte[] newBuffer = new byte[(length + lengthBytesLength)];

				Buffer.BlockCopy(buffer, 0, newBuffer, 0, (length + lengthBytesLength));

				_memoryStream = new MemoryStream(newBuffer);
			}
		}

		public virtual byte[] GetData()
		{
			return _memoryStream.ToArray();
		}

		public virtual byte[] ToArray()
		{
			return _memoryStream.ToArray();
		}
	}
}