using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.DNS.Protocol.Utils;

namespace skyscraper5.DNS.Protocol
{
	[Marshalling.Endian(Marshalling.Endianness.Big)]
	[StructLayout(LayoutKind.Sequential, Pack = 1)]
	public struct Header
	{
		public const int SIZE = 12;

		public static Header FromArray(byte[] header)
		{
			if (header.Length < SIZE)
			{
				Header dummy = new Header();
				dummy.Response = false;
				return dummy;
			}

			return Marshalling.Struct.GetStruct<Header>(header, 0, SIZE);
		}

		private ushort id;

		private byte flag0;
		private byte flag1;

		// Question count: number of questions in the Question section
		private ushort qdCount;

		// Answer record count: number of records in the Answer section
		private ushort anCount;

		// Authority record count: number of records in the Authority section
		private ushort nsCount;

		// Additional record count: number of records in the Additional section
		private ushort arCount;

		public int Id
		{
			get { return id; }
			set { id = (ushort)value; }
		}

		public int QuestionCount
		{
			get { return qdCount; }
			set { qdCount = (ushort)value; }
		}

		public int AnswerRecordCount
		{
			get { return anCount; }
			set { anCount = (ushort)value; }
		}

		public int AuthorityRecordCount
		{
			get { return nsCount; }
			set { nsCount = (ushort)value; }
		}

		public int AdditionalRecordCount
		{
			get { return arCount; }
			set { arCount = (ushort)value; }
		}

		public bool Response
		{
			get { return Qr == 1; }
			set { Qr = Convert.ToByte(value); }
		}

		public OperationCode OperationCode
		{
			get { return (OperationCode)Opcode; }
			set { Opcode = (byte)value; }
		}

		public bool AuthorativeServer
		{
			get { return Aa == 1; }
			set { Aa = Convert.ToByte(value); }
		}

		public bool Truncated
		{
			get { return Tc == 1; }
			set { Tc = Convert.ToByte(value); }
		}

		public bool RecursionDesired
		{
			get { return Rd == 1; }
			set { Rd = Convert.ToByte(value); }
		}

		public bool RecursionAvailable
		{
			get { return Ra == 1; }
			set { Ra = Convert.ToByte(value); }
		}

		public bool AuthenticData
		{
			get { return Ad == 1; }
			set { Ad = Convert.ToByte(value); }
		}

		public bool CheckingDisabled
		{
			get { return Cd == 1; }
			set { Cd = Convert.ToByte(value); }
		}

		public ResponseCode ResponseCode
		{
			get { return (ResponseCode)RCode; }
			set { RCode = (byte)value; }
		}

		public int Size
		{
			get { return Header.SIZE; }
		}

		public byte[] ToArray()
		{
			return Marshalling.Struct.GetBytes(this);
		}

		public override string ToString()
		{
			return ObjectStringifier.New(this)
				.AddAll()
				.Remove(nameof(Size))
				.ToString();
		}

		// Query/Response Flag
		private byte Qr
		{
			get { return Flag0.GetBitValueAt(7, 1); }
			set { Flag0 = Flag0.SetBitValueAt(7, 1, value); }
		}

		// Operation Code
		private byte Opcode
		{
			get { return Flag0.GetBitValueAt(3, 4); }
			set { Flag0 = Flag0.SetBitValueAt(3, 4, value); }
		}

		// Authorative Answer Flag
		private byte Aa
		{
			get { return Flag0.GetBitValueAt(2, 1); }
			set { Flag0 = Flag0.SetBitValueAt(2, 1, value); }
		}

		// Truncation Flag
		private byte Tc
		{
			get { return Flag0.GetBitValueAt(1, 1); }
			set { Flag0 = Flag0.SetBitValueAt(1, 1, value); }
		}

		// Recursion Desired
		private byte Rd
		{
			get { return Flag0.GetBitValueAt(0, 1); }
			set { Flag0 = Flag0.SetBitValueAt(0, 1, value); }
		}

		// Recursion Available
		private byte Ra
		{
			get { return Flag1.GetBitValueAt(7, 1); }
			set { Flag1 = Flag1.SetBitValueAt(7, 1, value); }
		}

		// Zero (Reserved)
		private byte Z
		{
			get { return Flag1.GetBitValueAt(6, 1); }
			set { }
		}

		// Authentic Data
		private byte Ad
		{
			get { return Flag1.GetBitValueAt(5, 1); }
			set { Flag1 = Flag1.SetBitValueAt(5, 1, value); }
		}

		// Checking Disabled
		private byte Cd
		{
			get { return Flag1.GetBitValueAt(4, 1); }
			set { Flag1 = Flag1.SetBitValueAt(4, 1, value); }
		}

		// Response Code
		private byte RCode
		{
			get { return Flag1.GetBitValueAt(0, 4); }
			set { Flag1 = Flag1.SetBitValueAt(0, 4, value); }
		}

		private byte Flag0
		{
			get { return flag0; }
			set { flag0 = value; }
		}

		private byte Flag1
		{
			get { return flag1; }
			set { flag1 = value; }
		}
	}
}
