using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using QCRPG.Framework;
using QCRPG.TSL.BT.Packets;

namespace QCRPG.TSL.BT;

public class AclDataSender
{
	private const int ACLFlagFlushable = 32;

	private const int ACLFlagNonFlushable = 0;

	private const int ACLFlagContinue = 16;

	public int ChannelId = 64;

	public int StartDelay;

	public bool ContinuousDataEnable = true;

	public int MaxACLLength = 1028;

	public bool FlushEnable = true;

	public bool NonFlushEnable;

	public bool BothEnable;

	public bool L2CAPSequenceNumberingEnable = true;

	public bool CycleL2CAPSizeEnable;

	public int DataPattern = 85;

	public bool SequentialDataPatternEnable = true;

	public bool CheckReceiveDataIntegrityEnable = true;

	public int PacketDelay;

	public int L2CAPMaxLength = 65535;

	private readonly SpecificPacketNotifier specificPacketNotifier;

	private readonly BTAPI btApi;

	private readonly Logger logger;

	private int eventTimeout = 2000;

	public bool AbortTxDataTransfer { get; set; }

	public bool Start { get; set; }

	public bool SendLastPacket { get; set; }

	public int RemoteL2CAPMaxLength { get; set; }

	public int RemoteChannelId { get; set; }

	public int RemoteDataPattern { get; set; }

	public bool RemoteCycleL2CAPSizeEnable { get; set; }

	public bool RemoteL2CAPSequenceNumberingEnable { get; set; }

	public bool RemoteCheckReceiveDataIntegrityEnable { get; set; }

	public string DataFileName { get; set; }

	public int ConnectionHandler { get; private set; }

	public double TransmitL2CAPByteCount { get; set; }

	public double TransmitL2CAPPktCount { get; set; }

	public double TransmitHCIByteCount { get; set; }

	public int NumberOfL2CAPPackets { get; set; }

	public int EventTimeout
	{
		get
		{
			return eventTimeout;
		}
		set
		{
			eventTimeout = value;
		}
	}

	public ACLDataQueue AclDataQueue { get; private set; }

	public event DataTransferTransmitHandler TransmitDataTransferHandler;

	public event FileTransferTransmitHandler TransmitFileTransferHandler;

	public AclDataSender(SpecificPacketNotifier specificPacketNotifier, BTAPI btApi, int connectionHandler, Logger logger, ACLDataQueue aclDataQueue)
	{
		this.specificPacketNotifier = specificPacketNotifier;
		this.btApi = btApi;
		ConnectionHandler = connectionHandler;
		this.logger = logger;
		AclDataQueue = aclDataQueue;
	}

	public void StartFileTransferHandler()
	{
		specificPacketNotifier.SetPacketNotification(typeof(NumberOfCompletedPacketsEvent), AclDataQueue.UpdateACLInfo, enabled: true);
		AclDataQueue.Startup();
		AclDataQueue.Clear();
		AclData aclData = FileNameACLDataPacket();
		if (aclData == null)
		{
			return;
		}
		AclDataQueue.Enqueue(aclData);
		try
		{
			AssembleFilePacketData();
		}
		catch (Exception ex)
		{
			string text = string.Format("File can not be opened for reading: " + ex);
			logger.Enqueue(new LogMessage(LogMessageType.Error, text));
		}
		finally
		{
			AclDataQueue.Shutdown();
			specificPacketNotifier.SetPacketNotification(typeof(NumberOfCompletedPacketsEvent), AclDataQueue.UpdateACLInfo, enabled: false);
		}
	}

	public void StopRemoteDataTransferHandler()
	{
		try
		{
			AclData aclDataPacket = EndRemoteACLDataPacket();
			specificPacketNotifier.SetPacketNotification(typeof(NumberOfCompletedPacketsEvent), AclDataQueue.UpdateACLInfo, enabled: true);
			AclDataQueue.Startup();
			AclDataQueue.Enqueue(aclDataPacket);
			AclDataQueue.Shutdown();
			specificPacketNotifier.SetPacketNotification(typeof(NumberOfCompletedPacketsEvent), AclDataQueue.UpdateACLInfo, enabled: false);
			btApi.SendSleepByteAfterDataTransfer();
		}
		catch (Exception ex)
		{
			logger.Enqueue(new LogMessage(LogMessageType.Error, "Stop Remote Data Transfer Handler: " + ex));
		}
	}

	public void StopDataTransfer()
	{
		Start = false;
		SendLastPacket = true;
	}

	public void StartDataTransferHandler()
	{
		Thread.Sleep(StartDelay * 1000);
		Start = true;
		specificPacketNotifier.SetPacketNotification(typeof(NumberOfCompletedPacketsEvent), AclDataQueue.UpdateACLInfo, enabled: true);
		AclDataQueue.Clear();
		AclDataQueue.Startup();
		btApi.SetAclDataDisplay();
		AclData aclDataPacket = FirstACLDataPacket();
		AclDataQueue.Enqueue(aclDataPacket);
		Thread thread = new Thread(DataTransferThread);
		thread.Name = "AclDataSender-DataTransfer";
		thread.IsBackground = true;
		thread.Start();
	}

	private void AssembleFilePacketData()
	{
		BinaryReader binaryReader = null;
		try
		{
			FileInfo fileInfo = new FileInfo(DataFileName);
			long num = fileInfo.Length;
			long totalFileData = num;
			if (num > uint.MaxValue)
			{
				logger.Enqueue(new LogMessage(LogMessageType.Error, "File is bigger than supported, the biggest file supported is 4G"));
				return;
			}
			try
			{
				binaryReader = new BinaryReader(File.Open(DataFileName, FileMode.Open));
			}
			catch (Exception ex)
			{
				string text = string.Format("File can not be opened for reading: " + ex);
				logger.Enqueue(new LogMessage(LogMessageType.Error, text));
				return;
			}
			int num2 = (int)(num / 65531);
			long num3 = 0L;
			for (int i = 0; i <= num2; i++)
			{
				int num4 = 0;
				int num5 = 0;
				if (num > 65531)
				{
					num4 = 63;
					num5 = 65531;
				}
				else
				{
					num4 = (int)((num + 4) / 1024);
					num5 = (int)num;
				}
				int num6 = num5;
				int num7 = 0;
				for (int j = 0; j <= num4; j++)
				{
					List<byte> list = new List<byte>();
					List<byte> list2 = new List<byte>();
					int num8 = 0;
					num8 = ((num6 <= 1024) ? num6 : 1024);
					if (j == 0 && num6 > 1020)
					{
						num8 = 1020;
					}
					num7 = num8;
					if (j == 0)
					{
						list.Add(218);
						list.Add(218);
						binaryReader.BaseStream.Seek(num3, SeekOrigin.Begin);
						byte[] collection = binaryReader.ReadBytes(num7);
						list.AddRange(collection);
						list2.Add((byte)((uint)num5 & 0xFFu));
						list2.Add((byte)(num5 >> 8));
						list2.AddRange(list);
					}
					else
					{
						binaryReader.BaseStream.Seek(num3, SeekOrigin.Begin);
						byte[] collection2 = binaryReader.ReadBytes(num7);
						list.AddRange(collection2);
						list2.AddRange(list);
					}
					byte[] array = list2.ToArray();
					AclData aclData = new AclData();
					aclData.UserFields.Handle = (uint)ConnectionHandler;
					if (j == 0)
					{
						if (FlushEnable)
						{
							aclData.UserFields.Flags = 32u;
						}
						else if (NonFlushEnable)
						{
							aclData.UserFields.Flags = 0u;
						}
						else if (BothEnable)
						{
							aclData.UserFields.Flags = 32u;
						}
						if (num6 > 1020)
						{
							aclData.UserFields.DataLength = 1024u;
						}
						else
						{
							aclData.UserFields.DataLength = (uint)array.Length;
						}
					}
					else
					{
						aclData.UserFields.Flags = 16u;
						aclData.UserFields.DataLength = (uint)array.Length;
					}
					for (int k = 0; k < array.Length; k++)
					{
						aclData.UserFields.DataList[k] = array[k];
					}
					AclDataQueue.Enqueue(aclData);
					num3 += num8;
					num6 -= num8;
					if (this.TransmitFileTransferHandler != null)
					{
						this.TransmitFileTransferHandler(this, ConnectionHandler, totalFileData, num7);
					}
				}
				num -= num5;
			}
		}
		finally
		{
			binaryReader?.Close();
		}
	}

	private AclData FileNameACLDataPacket()
	{
		FileInfo fileInfo = new FileInfo(DataFileName);
		long length = fileInfo.Length;
		if (length > uint.MaxValue)
		{
			logger.Enqueue(new LogMessage(LogMessageType.Error, "File is bigger than supported, the biggest file supported is 4G"));
			return null;
		}
		string name = fileInfo.Name;
		List<byte> list = new List<byte>();
		List<byte> list2 = new List<byte>();
		list2.Add(218);
		list2.Add(218);
		list2.Add((byte)(length & 0xFF));
		list2.Add((byte)((length >> 8) & 0xFF));
		list2.Add((byte)((length >> 16) & 0xFF));
		list2.Add((byte)((length >> 24) & 0xFF));
		char[] array = name.ToCharArray();
		try
		{
			for (int i = 0; i < array.Length; i++)
			{
				list2.Add(Convert.ToByte(array[i]));
			}
		}
		catch (Exception ex)
		{
			logger.Enqueue(new LogMessage(LogMessageType.Error, "File name contains illegal character. " + ex));
			return null;
		}
		list2.Add(0);
		int count = list2.Count;
		list.Add((byte)((uint)count & 0xFFu));
		list.Add((byte)(count >> 8));
		list.AddRange(list2);
		byte[] array2 = list.ToArray();
		AclData aclData = new AclData();
		aclData.UserFields.Handle = (uint)ConnectionHandler;
		if (FlushEnable)
		{
			aclData.UserFields.Flags = 32u;
		}
		else if (NonFlushEnable)
		{
			aclData.UserFields.Flags = 0u;
		}
		else if (BothEnable)
		{
			aclData.UserFields.Flags = 32u;
		}
		aclData.UserFields.DataLength = (uint)array2.Length;
		for (int j = 0; j < array2.Length; j++)
		{
			aclData.UserFields.DataList[j] = array2[j];
		}
		return aclData;
	}

	private AclData FirstACLDataPacket()
	{
		byte b = 0;
		List<byte> list = new List<byte>();
		List<byte> list2 = new List<byte>();
		list2.Add(204);
		list2.Add(204);
		list2.Add(0);
		list2.Add(0);
		list2.Add(0);
		list2.Add(0);
		list2.Add((byte)((uint)L2CAPMaxLength & 0xFFu));
		list2.Add((byte)(L2CAPMaxLength >> 8));
		list2.Add((byte)((uint)ChannelId & 0xFFu));
		list2.Add((byte)(ChannelId >> 8));
		b = (byte)(CycleL2CAPSizeEnable ? 1u : 0u);
		list2.Add(b);
		b = (byte)(L2CAPSequenceNumberingEnable ? 1u : 0u);
		list2.Add(b);
		list2.Add((byte)((uint)DataPattern & 0xFFu));
		if (SequentialDataPatternEnable)
		{
			list2.Add(1);
		}
		else
		{
			list2.Add(0);
		}
		b = (byte)(CheckReceiveDataIntegrityEnable ? 1u : 0u);
		list2.Add(b);
		list2.Add(1);
		list2.Add(0);
		list2.Add(0);
		int count = list2.Count;
		list.Add((byte)((uint)count & 0xFFu));
		list.Add((byte)(count >> 8));
		list.AddRange(list2);
		byte[] array = list.ToArray();
		AclData aclData = new AclData();
		aclData.UserFields.Handle = (uint)ConnectionHandler;
		if (FlushEnable)
		{
			aclData.UserFields.Flags = 32u;
		}
		else if (NonFlushEnable)
		{
			aclData.UserFields.Flags = 0u;
		}
		else if (BothEnable)
		{
			aclData.UserFields.Flags = 32u;
		}
		aclData.UserFields.DataLength = (uint)array.Length;
		for (int i = 0; i < array.Length; i++)
		{
			aclData.UserFields.DataList[i] = array[i];
		}
		return aclData;
	}

	private AclData EndACLDataPacket()
	{
		byte b = 0;
		List<byte> list = new List<byte>();
		List<byte> list2 = new List<byte>();
		list2.Add(204);
		list2.Add(204);
		list2.Add(0);
		list2.Add(0);
		list2.Add(0);
		list2.Add(0);
		list2.Add((byte)((uint)L2CAPMaxLength & 0xFFu));
		list2.Add((byte)(L2CAPMaxLength >> 8));
		list2.Add((byte)((uint)ChannelId & 0xFFu));
		list2.Add((byte)(ChannelId >> 8));
		b = (byte)(CycleL2CAPSizeEnable ? 1u : 0u);
		list2.Add(b);
		b = (byte)(L2CAPSequenceNumberingEnable ? 1u : 0u);
		list2.Add(b);
		list2.Add((byte)((uint)DataPattern & 0xFFu));
		list2.Add(1);
		b = (byte)(CheckReceiveDataIntegrityEnable ? 1u : 0u);
		list2.Add(b);
		list2.Add(0);
		list2.Add(0);
		list2.Add(0);
		int count = list2.Count;
		list.Add((byte)((uint)count & 0xFFu));
		list.Add((byte)(count >> 8));
		list.AddRange(list2);
		byte[] array = list.ToArray();
		AclData aclData = new AclData();
		aclData.UserFields.Handle = (uint)ConnectionHandler;
		if (FlushEnable)
		{
			aclData.UserFields.Flags = 32u;
		}
		else if (NonFlushEnable)
		{
			aclData.UserFields.Flags = 0u;
		}
		else if (BothEnable)
		{
			aclData.UserFields.Flags = 32u;
		}
		aclData.UserFields.DataLength = (uint)array.Length;
		for (int i = 0; i < array.Length; i++)
		{
			aclData.UserFields.DataList[i] = array[i];
		}
		return aclData;
	}

	private AclData EndRemoteACLDataPacket()
	{
		byte b = 0;
		List<byte> list = new List<byte>();
		List<byte> list2 = new List<byte>();
		list2.Add(204);
		list2.Add(204);
		list2.Add(0);
		list2.Add(0);
		list2.Add(0);
		list2.Add(0);
		list2.Add((byte)((uint)RemoteL2CAPMaxLength & 0xFFu));
		list2.Add((byte)(RemoteL2CAPMaxLength >> 8));
		list2.Add((byte)((uint)RemoteChannelId & 0xFFu));
		list2.Add((byte)(RemoteChannelId >> 8));
		b = (byte)(RemoteCycleL2CAPSizeEnable ? 1u : 0u);
		list2.Add(b);
		b = (byte)(RemoteL2CAPSequenceNumberingEnable ? 1u : 0u);
		list2.Add(b);
		list2.Add((byte)((uint)RemoteDataPattern & 0xFFu));
		list2.Add(1);
		b = (byte)(RemoteCheckReceiveDataIntegrityEnable ? 1u : 0u);
		list2.Add(b);
		list2.Add(2);
		list2.Add(0);
		list2.Add(0);
		int count = list2.Count;
		list.Add((byte)((uint)count & 0xFFu));
		list.Add((byte)(count >> 8));
		list.AddRange(list2);
		byte[] array = list.ToArray();
		AclData aclData = new AclData();
		aclData.UserFields.Handle = (uint)ConnectionHandler;
		if (FlushEnable)
		{
			aclData.UserFields.Flags = 32u;
		}
		else if (NonFlushEnable)
		{
			aclData.UserFields.Flags = 0u;
		}
		else if (BothEnable)
		{
			aclData.UserFields.Flags = 32u;
		}
		aclData.UserFields.DataLength = (uint)array.Length;
		for (int i = 0; i < array.Length; i++)
		{
			aclData.UserFields.DataList[i] = array[i];
		}
		return aclData;
	}

	private void DataTransferThread()
	{
		if (!Start)
		{
			return;
		}
		int num = 0;
		while (ContinuousDataEnable || TransmitL2CAPPktCount < (double)NumberOfL2CAPPackets)
		{
			AssembleACLPacketData(num);
			if (L2CAPSequenceNumberingEnable)
			{
				num = ((num != 255) ? (num + 1) : 0);
			}
			if (!Start)
			{
				break;
			}
			if (AbortTxDataTransfer)
			{
				AclDataQueue.Clear();
				AclDataQueue.abort = true;
				AclDataQueue.Shutdown();
				AbortTxDataTransfer = false;
				return;
			}
		}
		Start = false;
		if (!Start && SendLastPacket)
		{
			if (ContinuousDataEnable)
			{
				AclDataQueue.Clear();
			}
			AclData aclDataPacket = EndACLDataPacket();
			AclDataQueue.Enqueue(aclDataPacket);
			AclDataQueue.Shutdown();
			if (this.TransmitDataTransferHandler != null)
			{
				this.TransmitDataTransferHandler(AbortTxDataTransfer ? null : this, ConnectionHandler, TransmitL2CAPByteCount, TransmitL2CAPPktCount, TransmitHCIByteCount, AclDataQueue.CurrentAvailable, AclDataQueue.MaximumBufferAvailable);
			}
			specificPacketNotifier.SetPacketNotification(typeof(NumberOfCompletedPacketsEvent), AclDataQueue.UpdateACLInfo, enabled: false);
			btApi.SendSleepByteAfterDataTransfer();
		}
	}

	private void AssembleACLPacketData(int sequentialIndex)
	{
		List<byte> list = new List<byte>();
		int maxACLLength = MaxACLLength;
		int num = -999;
		if (L2CAPMaxLength - 1 - maxACLLength + 5 > 0)
		{
			num = DivideRoundingUp(L2CAPMaxLength - 1 - maxACLLength + 5, maxACLLength);
			num++;
		}
		else
		{
			num = 1;
		}
		byte b = 0;
		int num2 = 0;
		for (int i = 0; i < num; i++)
		{
			if (AbortTxDataTransfer)
			{
				AclDataQueue.Clear();
				break;
			}
			int flags = 0;
			if (i == 0)
			{
				if (!Start)
				{
					break;
				}
				TransmitL2CAPPktCount += 1.0;
				if (FlushEnable)
				{
					flags = 32;
				}
				else if (NonFlushEnable)
				{
					flags = 0;
				}
				else if (BothEnable)
				{
					flags = 32;
				}
				list.Clear();
				list.Add((byte)((uint)L2CAPMaxLength & 0xFFu));
				list.Add((byte)(L2CAPMaxLength >> 8));
				list.Add((byte)((uint)ChannelId & 0xFFu));
				list.Add((byte)(ChannelId >> 8));
				if (L2CAPSequenceNumberingEnable)
				{
					list.Add((byte)((uint)sequentialIndex & 0xFFu));
					num2++;
				}
				TransmitL2CAPByteCount += list.Count;
				TransmitHCIByteCount += list.Count + 2;
				int num3 = maxACLLength - list.Count;
				for (int j = 0; j < num3; j++)
				{
					if (!SequentialDataPatternEnable)
					{
						b = (byte)DataPattern;
					}
					list.Add(b);
					if (SequentialDataPatternEnable)
					{
						b++;
					}
					num2++;
					TransmitL2CAPByteCount += 1.0;
					TransmitHCIByteCount += 1.0;
					if (num2 >= L2CAPMaxLength)
					{
						break;
					}
					if (b > byte.MaxValue)
					{
						b = 0;
					}
				}
			}
			else
			{
				if (!Start)
				{
					break;
				}
				TransmitHCIByteCount += 2.0;
				flags = 16;
				list.Clear();
				for (int k = 0; k < maxACLLength; k++)
				{
					if (!SequentialDataPatternEnable)
					{
						b = (byte)DataPattern;
					}
					list.Add(b);
					if (SequentialDataPatternEnable)
					{
						b++;
					}
					num2++;
					TransmitL2CAPByteCount += 1.0;
					TransmitHCIByteCount += 1.0;
					if (num2 >= L2CAPMaxLength)
					{
						break;
					}
					if (b > byte.MaxValue)
					{
						b = 0;
					}
				}
			}
			byte[] array = list.ToArray();
			AclData aclData = new AclData();
			aclData.UserFields.Handle = (uint)ConnectionHandler;
			aclData.UserFields.Flags = (uint)flags;
			aclData.UserFields.DataLength = (uint)array.Length;
			for (int l = 0; l < array.Length; l++)
			{
				aclData.UserFields.DataList[l] = array[l];
			}
			AbortTxDataTransfer = !AclDataQueue.Enqueue(aclData);
			Thread.Sleep(PacketDelay);
			if (this.TransmitDataTransferHandler != null)
			{
				this.TransmitDataTransferHandler(AbortTxDataTransfer ? null : this, ConnectionHandler, TransmitL2CAPByteCount, TransmitL2CAPPktCount, TransmitHCIByteCount, AclDataQueue.CurrentAvailable, AclDataQueue.MaximumBufferAvailable);
			}
		}
	}

	private static int DivideRoundingUp(int x, int y)
	{
		int result;
		int num = Math.DivRem(x, y, out result);
		if (result != 0)
		{
			return num + 1;
		}
		return num;
	}
}
