using System;
using System.Collections.Generic;
using System.Text;
using QCRPG.Framework;

namespace QCRPG.TSL.BT.Packets;

public class LeAdvertisingReport : PacketBase
{
	public class UserFieldsWrapper
	{
		private const uint _SubeventCode = 2u;

		private uint _NumReports;

		private List<ReportsSubField> _ReportsList = new List<ReportsSubField>();

		public uint SubeventCode => 2u;

		public uint NumReports
		{
			get
			{
				return _NumReports;
			}
			set
			{
				if (value > 255)
				{
					throw new Exception("Maximum possible value for NumReports is 0xFF");
				}
				_NumReports = value;
				while (_ReportsList.Count < _NumReports)
				{
					_ReportsList.Add(new ReportsSubField());
				}
				while (_ReportsList.Count > _NumReports)
				{
					_ReportsList.RemoveAt(_ReportsList.Count - 1);
				}
			}
		}

		public List<ReportsSubField> ReportsList => _ReportsList;

		public NumberOfReportsEnum GetNumReportsAsNumberOfReportsEnum()
		{
			return NumberOfReportsInfo.ValueToNumberOfReports(_NumReports);
		}

		public void SetNumReportsAsNumberOfReportsEnum(NumberOfReportsEnum enumItem)
		{
			NumReports = NumberOfReportsInfo.NumberOfReportsToValue(enumItem);
		}
	}

	public class ReportsSubField
	{
		public enum EventTypeEnum
		{
			Connectable_and_scannable_undirected_advertising_ADV_IND,
			Connectable_directed_advertising_ADV_DIRECT_IND,
			Scannable_undirected_advertising_ADV_SCAN_IND,
			Non_connectable_undirected_advertising_ADV_NONCONN_IND,
			Scan_Response_SCAN_RSP,
			Reserved_for_future_use,
			InvalidEventTypeValue
		}

		public static class EventTypeInfo
		{
			public static EventTypeEnum ValueToEventType(uint value)
			{
				switch (value)
				{
				case 0u:
					return EventTypeEnum.Connectable_and_scannable_undirected_advertising_ADV_IND;
				case 1u:
					return EventTypeEnum.Connectable_directed_advertising_ADV_DIRECT_IND;
				case 2u:
					return EventTypeEnum.Scannable_undirected_advertising_ADV_SCAN_IND;
				case 3u:
					return EventTypeEnum.Non_connectable_undirected_advertising_ADV_NONCONN_IND;
				case 4u:
					return EventTypeEnum.Scan_Response_SCAN_RSP;
				default:
					if (value >= 5 && value <= 255)
					{
						return EventTypeEnum.Reserved_for_future_use;
					}
					return EventTypeEnum.InvalidEventTypeValue;
				}
			}

			public static uint EventTypeToValue(EventTypeEnum enumItem)
			{
				return enumItem switch
				{
					EventTypeEnum.Connectable_and_scannable_undirected_advertising_ADV_IND => 0u, 
					EventTypeEnum.Connectable_directed_advertising_ADV_DIRECT_IND => 1u, 
					EventTypeEnum.Scannable_undirected_advertising_ADV_SCAN_IND => 2u, 
					EventTypeEnum.Non_connectable_undirected_advertising_ADV_NONCONN_IND => 3u, 
					EventTypeEnum.Scan_Response_SCAN_RSP => 4u, 
					EventTypeEnum.Reserved_for_future_use => 5u, 
					_ => uint.MaxValue, 
				};
			}

			public static string EventTypeToString(EventTypeEnum enumItem)
			{
				return enumItem switch
				{
					EventTypeEnum.Connectable_and_scannable_undirected_advertising_ADV_IND => "Connectable and scannable undirected advertising (ADV_IND)", 
					EventTypeEnum.Connectable_directed_advertising_ADV_DIRECT_IND => "Connectable directed advertising (ADV_DIRECT_IND)", 
					EventTypeEnum.Scannable_undirected_advertising_ADV_SCAN_IND => "Scannable undirected advertising (ADV_SCAN_IND)", 
					EventTypeEnum.Non_connectable_undirected_advertising_ADV_NONCONN_IND => "Non connectable undirected advertising (ADV_NONCONN_IND)", 
					EventTypeEnum.Scan_Response_SCAN_RSP => "Scan Response (SCAN_RSP)", 
					EventTypeEnum.Reserved_for_future_use => "Reserved for future use", 
					_ => "Invalid EventType Value", 
				};
			}

			public static string EventTypeToDescription()
			{
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.AppendLine("0x0 = Connectable and scannable undirected advertising (ADV_IND)");
				stringBuilder.AppendLine("0x1 = Connectable directed advertising (ADV_DIRECT_IND)");
				stringBuilder.AppendLine("0x2 = Scannable undirected advertising (ADV_SCAN_IND)");
				stringBuilder.AppendLine("0x3 = Non connectable undirected advertising (ADV_NONCONN_IND)");
				stringBuilder.AppendLine("0x4 = Scan Response (SCAN_RSP)");
				stringBuilder.AppendLine("0x5-0xFF = Reserved for future use");
				return stringBuilder.ToString();
			}
		}

		private uint _EventType;

		private uint _AddressType;

		private ulong _Address;

		private uint _LengthData;

		private List<uint> _DataList = new List<uint>();

		private uint _Rssi;

		public uint EventType
		{
			get
			{
				return _EventType;
			}
			set
			{
				if (value > 255)
				{
					throw new Exception("Maximum possible value for EventType is 0xFF");
				}
				_EventType = value;
			}
		}

		public uint AddressType
		{
			get
			{
				return _AddressType;
			}
			set
			{
				if (value > 255)
				{
					throw new Exception("Maximum possible value for AddressType is 0xFF");
				}
				_AddressType = value;
			}
		}

		public ulong Address
		{
			get
			{
				return _Address;
			}
			set
			{
				_Address = value;
			}
		}

		public uint LengthData
		{
			get
			{
				return _LengthData;
			}
			set
			{
				if (value > 255)
				{
					throw new Exception("Maximum possible value for LengthData is 0xFF");
				}
				_LengthData = value;
				while (_DataList.Count < _LengthData)
				{
					_DataList.Add(0u);
				}
				while (_DataList.Count > _LengthData)
				{
					_DataList.RemoveAt(_DataList.Count - 1);
				}
			}
		}

		public List<uint> DataList => _DataList;

		public uint Rssi
		{
			get
			{
				return _Rssi;
			}
			set
			{
				if (value > 255)
				{
					throw new Exception("Maximum possible value for Rssi is 0xFF");
				}
				_Rssi = value;
			}
		}

		public void SetDefaultValues()
		{
			LengthData = 0u;
		}

		public EventTypeEnum GetEventTypeAsEventTypeEnum()
		{
			return EventTypeInfo.ValueToEventType(_EventType);
		}

		public void SetEventTypeAsEventTypeEnum(EventTypeEnum enumItem)
		{
			EventType = EventTypeInfo.EventTypeToValue(enumItem);
		}

		public PeerResolvableAddressTypeEnum GetAddressTypeAsPeerResolvableAddressTypeEnum()
		{
			return PeerResolvableAddressTypeInfo.ValueToPeerResolvableAddressType(_AddressType);
		}

		public void SetAddressTypeAsPeerResolvableAddressTypeEnum(PeerResolvableAddressTypeEnum enumItem)
		{
			AddressType = PeerResolvableAddressTypeInfo.PeerResolvableAddressTypeToValue(enumItem);
		}

		public AdvertisingRssiEnum GetRssiAsAdvertisingRssiEnum()
		{
			return AdvertisingRssiInfo.ValueToAdvertisingRssi(_Rssi);
		}

		public void SetRssiAsAdvertisingRssiEnum(AdvertisingRssiEnum enumItem)
		{
			Rssi = AdvertisingRssiInfo.AdvertisingRssiToValue(enumItem);
		}

		public bool FromBitBuilder(ref BitBuilder bitBuilder)
		{
			EventType = bitBuilder.RemoveBitsAsUint(8);
			AddressType = bitBuilder.RemoveBitsAsUint(8);
			Address = bitBuilder.RemoveBitsAsUlongLittleEndian(48);
			LengthData = bitBuilder.RemoveBitsAsUint(8);
			for (int i = 0; i < DataList.Count; i++)
			{
				DataList[i] = bitBuilder.RemoveBitsAsUint(8);
			}
			Rssi = bitBuilder.RemoveBitsAsUint(8);
			return true;
		}

		public string ToBitString()
		{
			BitBuilder bitBuilder = new BitBuilder();
			bitBuilder.AddBitsAsUint(EventType, 8);
			bitBuilder.AddBitsAsUint(AddressType, 8);
			bitBuilder.AddBitsAsUlongLittleEndian(Address, 48);
			bitBuilder.AddBitsAsUint(LengthData, 8);
			for (int i = 0; i < DataList.Count; i++)
			{
				if (DataList[i] > 255)
				{
					throw new Exception("DataList[i] is > 0xFF");
				}
				bitBuilder.AddBitsAsUint(DataList[i], 8);
			}
			bitBuilder.AddBitsAsUint(Rssi, 8);
			return bitBuilder.ToString();
		}

		public List<string> GetWarnings()
		{
			List<string> list = new List<string>();
			if (LengthData < 0)
			{
				list.Add("Minimum allowed value for Length_Data is 0x0");
			}
			if (LengthData > 31)
			{
				list.Add("Maximum allowed value for Length_Data is 0x1F");
			}
			return list;
		}
	}

	private const uint _EventCode = 62u;

	private Type[] expectedResponseTypes = new Type[0];

	private PacketInfoBase info;

	private uint _PayloadLength;

	public UserFieldsWrapper _UserFields = new UserFieldsWrapper();

	public override PacketType Type => PacketType.Notification;

	public override Type[] ExpectedResponseTypes => expectedResponseTypes;

	public override PacketInfoBase Info
	{
		get
		{
			if (info == null)
			{
				info = new LeAdvertisingReportInfo(this);
			}
			return info;
		}
	}

	public uint EventCode => 62u;

	public uint PayloadLength
	{
		get
		{
			ToBitString();
			return _PayloadLength;
		}
	}

	public UserFieldsWrapper UserFields => _UserFields;

	protected override bool FromBitBuilder(ref BitBuilder bitBuilder)
	{
		if (bitBuilder.RemoveBitsAsUint(8) != EventCode)
		{
			return false;
		}
		_PayloadLength = bitBuilder.RemoveBitsAsUint(8);
		int numberOfBits = bitBuilder.GetNumberOfBits();
		if (numberOfBits > _PayloadLength * 8 + 7)
		{
			return false;
		}
		if (numberOfBits < _PayloadLength * 8)
		{
			return false;
		}
		if (bitBuilder.RemoveBitsAsUint(8) != UserFields.SubeventCode)
		{
			return false;
		}
		UserFields.NumReports = bitBuilder.RemoveBitsAsUint(8);
		for (int i = 0; i < UserFields.ReportsList.Count; i++)
		{
			UserFields.ReportsList[i].FromBitBuilder(ref bitBuilder);
		}
		if (bitBuilder.GetNumberOfBits() > 7)
		{
			return false;
		}
		return true;
	}

	public override string ToBitString()
	{
		BitBuilder bitBuilder = new BitBuilder();
		bitBuilder.AddBitsAsUint(EventCode, 8);
		int numberOfBits = bitBuilder.GetNumberOfBits();
		bitBuilder.AddBitsAsUint(0u, 8);
		bitBuilder.AddBitsAsUint(UserFields.SubeventCode, 8);
		bitBuilder.AddBitsAsUint(UserFields.NumReports, 8);
		for (int i = 0; i < UserFields.ReportsList.Count; i++)
		{
			bitBuilder.AddBitsAsString(UserFields.ReportsList[i].ToBitString());
		}
		_PayloadLength = (uint)((bitBuilder.GetNumberOfBits() - numberOfBits - 8) / 8);
		if ((bitBuilder.GetNumberOfBits() - numberOfBits - 8) % 8 != 0)
		{
			_PayloadLength++;
		}
		bitBuilder.ChangeBitsAsUint(numberOfBits, _PayloadLength, 8);
		return bitBuilder.ToString();
	}
}
