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

namespace QCRPG.TSL.BT.Packets;

public class SetConnectionlessSlaveBroadcastData : PacketBase
{
	public class UserFieldsWrapper
	{
		public enum LtAddrEnum
		{
			Reserved_for_future_use,
			LT_ADDR_on_which_to_send_Connectionless_Slave_Broadcast_data,
			InvalidLtAddrValue
		}

		public static class LtAddrInfo
		{
			public static LtAddrEnum ValueToLtAddr(uint value)
			{
				if (value == 0)
				{
					return LtAddrEnum.Reserved_for_future_use;
				}
				if (value >= 1 && value <= 7)
				{
					return LtAddrEnum.LT_ADDR_on_which_to_send_Connectionless_Slave_Broadcast_data;
				}
				if (value >= 8 && value <= 255)
				{
					return LtAddrEnum.Reserved_for_future_use;
				}
				return LtAddrEnum.InvalidLtAddrValue;
			}

			public static uint LtAddrToValue(LtAddrEnum enumItem)
			{
				switch (enumItem)
				{
				case LtAddrEnum.Reserved_for_future_use:
					return 0u;
				case LtAddrEnum.LT_ADDR_on_which_to_send_Connectionless_Slave_Broadcast_data:
					return 1u;
				default:
					if (enumItem == LtAddrEnum.Reserved_for_future_use)
					{
						return 8u;
					}
					return uint.MaxValue;
				}
			}

			public static string LtAddrToString(LtAddrEnum enumItem)
			{
				switch (enumItem)
				{
				case LtAddrEnum.Reserved_for_future_use:
					return "Reserved for future use";
				case LtAddrEnum.LT_ADDR_on_which_to_send_Connectionless_Slave_Broadcast_data:
					return "LT_ADDR on which to send Connectionless Slave Broadcast data";
				default:
					if (enumItem == LtAddrEnum.Reserved_for_future_use)
					{
						return "Reserved for future use";
					}
					return "Invalid LtAddr Value";
				}
			}

			public static string LtAddrToDescription()
			{
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.AppendLine("0x0 = Reserved for future use");
				stringBuilder.AppendLine("0x1-0x7 = LT_ADDR on which to send Connectionless Slave Broadcast data");
				stringBuilder.AppendLine("0x8-0xFF = Reserved for future use");
				return stringBuilder.ToString();
			}
		}

		public enum FragmentEnum
		{
			Continuation_fragment,
			Starting_fragment,
			End_fragment,
			No_fragmentation,
			Reserved_for_future_use,
			InvalidFragmentValue
		}

		public static class FragmentInfo
		{
			public static FragmentEnum ValueToFragment(uint value)
			{
				switch (value)
				{
				case 0u:
					return FragmentEnum.Continuation_fragment;
				case 1u:
					return FragmentEnum.Starting_fragment;
				case 2u:
					return FragmentEnum.End_fragment;
				case 3u:
					return FragmentEnum.No_fragmentation;
				default:
					if (value >= 4 && value <= 255)
					{
						return FragmentEnum.Reserved_for_future_use;
					}
					return FragmentEnum.InvalidFragmentValue;
				}
			}

			public static uint FragmentToValue(FragmentEnum enumItem)
			{
				return enumItem switch
				{
					FragmentEnum.Continuation_fragment => 0u, 
					FragmentEnum.Starting_fragment => 1u, 
					FragmentEnum.End_fragment => 2u, 
					FragmentEnum.No_fragmentation => 3u, 
					FragmentEnum.Reserved_for_future_use => 4u, 
					_ => uint.MaxValue, 
				};
			}

			public static string FragmentToString(FragmentEnum enumItem)
			{
				return enumItem switch
				{
					FragmentEnum.Continuation_fragment => "Continuation fragment", 
					FragmentEnum.Starting_fragment => "Starting fragment", 
					FragmentEnum.End_fragment => "End fragment", 
					FragmentEnum.No_fragmentation => "No fragmentation (single fragment)", 
					FragmentEnum.Reserved_for_future_use => "Reserved for future use", 
					_ => "Invalid Fragment Value", 
				};
			}

			public static string FragmentToDescription()
			{
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.AppendLine("0x0 = Continuation fragment");
				stringBuilder.AppendLine("0x1 = Starting fragment");
				stringBuilder.AppendLine("0x2 = End fragment");
				stringBuilder.AppendLine("0x3 = No fragmentation (single fragment)");
				stringBuilder.AppendLine("0x4-0xFF = Reserved for future use");
				return stringBuilder.ToString();
			}
		}

		private uint _LtAddr;

		private uint _Fragment;

		private uint _DataLength;

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

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

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

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

		public List<uint> DataList => _DataList;

		public LtAddrEnum GetLtAddrAsLtAddrEnum()
		{
			return LtAddrInfo.ValueToLtAddr(_LtAddr);
		}

		public void SetLtAddrAsLtAddrEnum(LtAddrEnum enumItem)
		{
			LtAddr = LtAddrInfo.LtAddrToValue(enumItem);
		}

		public FragmentEnum GetFragmentAsFragmentEnum()
		{
			return FragmentInfo.ValueToFragment(_Fragment);
		}

		public void SetFragmentAsFragmentEnum(FragmentEnum enumItem)
		{
			Fragment = FragmentInfo.FragmentToValue(enumItem);
		}
	}

	private const uint _Opcode = 3190u;

	private Type[] expectedResponseTypes = new Type[1] { typeof(CommandCompleteForSetConnectionlessSlaveBroadcastData) };

	private PacketInfoBase info;

	private uint _PayloadLength;

	public UserFieldsWrapper _UserFields = new UserFieldsWrapper();

	public override PacketType Type => PacketType.Command;

	public override Type[] ExpectedResponseTypes => expectedResponseTypes;

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

	public uint Opcode => 3190u;

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

	public UserFieldsWrapper UserFields => _UserFields;

	public override void SetDefaultValues()
	{
		UserFields.LtAddr = 1u;
		UserFields.Fragment = 0u;
	}

	protected override bool FromBitBuilder(ref BitBuilder bitBuilder)
	{
		if (bitBuilder.RemoveBitsAsUintLittleEndian(16) != Opcode)
		{
			return false;
		}
		_PayloadLength = bitBuilder.RemoveBitsAsUint(8);
		int numberOfBits = bitBuilder.GetNumberOfBits();
		if (numberOfBits > _PayloadLength * 8 + 7)
		{
			return false;
		}
		if (numberOfBits < _PayloadLength * 8)
		{
			return false;
		}
		UserFields.LtAddr = bitBuilder.RemoveBitsAsUint(8);
		UserFields.Fragment = bitBuilder.RemoveBitsAsUint(8);
		UserFields.DataLength = bitBuilder.RemoveBitsAsUint(8);
		for (int i = 0; i < UserFields.DataList.Count; i++)
		{
			UserFields.DataList[i] = bitBuilder.RemoveBitsAsUint(8);
		}
		if (bitBuilder.GetNumberOfBits() > 7)
		{
			return false;
		}
		return true;
	}

	public override string ToBitString()
	{
		BitBuilder bitBuilder = new BitBuilder();
		bitBuilder.AddBitsAsUintLittleEndian(Opcode, 16);
		int numberOfBits = bitBuilder.GetNumberOfBits();
		bitBuilder.AddBitsAsUint(0u, 8);
		bitBuilder.AddBitsAsUint(UserFields.LtAddr, 8);
		bitBuilder.AddBitsAsUint(UserFields.Fragment, 8);
		bitBuilder.AddBitsAsUint(UserFields.DataLength, 8);
		for (int i = 0; i < UserFields.DataList.Count; i++)
		{
			if (UserFields.DataList[i] > 255)
			{
				throw new Exception("UserFields.DataList[i] is > 0xFF");
			}
			bitBuilder.AddBitsAsUint(UserFields.DataList[i], 8);
		}
		_PayloadLength = (uint)((bitBuilder.GetNumberOfBits() - numberOfBits - 8) / 8);
		if ((bitBuilder.GetNumberOfBits() - numberOfBits - 8) % 8 != 0)
		{
			_PayloadLength++;
		}
		bitBuilder.ChangeBitsAsUint(numberOfBits, _PayloadLength, 8);
		return bitBuilder.ToString();
	}
}
