using System;
using System.Collections.Generic;
using System.Text;
using System.Globalization;
using System.Diagnostics;

namespace SE290A_BackOfficeSim_CoreApi
{

    /// <summary>
    /// Represents the class for handling 64-bit RF MAC addresses of ESLs and 48-bit Ethernet MAC addresses of base station
    /// </summary>
    [Serializable]
    public class MAC_Address
    {
        private UInt64 _MAC = 0;

        /// <summary>
        /// Constructor
        /// </summary>
        public MAC_Address()
        {
        }

        /// <summary>
        /// Constructor using a 64-bit long value (use for ESLs only)
        /// </summary>
        public MAC_Address(ulong mac)
        {
            _MAC = mac;
        }

        /// <summary>
        /// Constructor using a byte array containing the 8 byte MAC address (use for ESLs only)
        /// </summary>
        public MAC_Address(Byte[] data, int offset)
        {
            StringBuilder sb = new StringBuilder(8);
            if (data != null && (data.Length - offset) >= 8)
            {
                for (int i = 0; i < 8; i++)
                {
                    sb.AppendFormat("{0:x2}", data[i + offset]);
                }

                _MAC = UInt64.Parse(sb.ToString(), NumberStyles.HexNumber);
            }
        }

        /// <summary>
        /// Constructor using a byte array containing the 8 byte MAC address (use for ESLs only)
        /// </summary>
        public MAC_Address(Byte[] data, int offset, int size)
        {
            StringBuilder sb = new StringBuilder(8);
            if (data != null)
            {
                if (size == 8)  // 64 bit address
                {
                    for (int i = 0; i < 8; i++)
                    {
                        sb.AppendFormat("{0:x2}", data[i + offset]);
                    }
                }
                else            // 48 bit address
                {
                    sb.AppendFormat("{0:x2}", data[0 + offset]);
                    sb.AppendFormat("{0:x2}", data[1 + offset]);
                    sb.AppendFormat("{0:x2}", data[2 + offset]);
                    sb.AppendFormat("0000");
                    sb.AppendFormat("{0:x2}", data[3 + offset]);
                    sb.AppendFormat("{0:x2}", data[4 + offset]);
                    sb.AppendFormat("{0:x2}", data[5 + offset]);
                }

                _MAC = UInt64.Parse(sb.ToString(), NumberStyles.HexNumber);
            }
        }

        /// <summary>
        /// Allows the folowing MAC formats
        ///		"2e:00:20:39:21:4e"
        ///		"2e-00-20-39-21-4e"
        ///		"2e.00.20.39.21.4e"
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        internal String Compress(String data)
        {
            string[] tmp = null;
            if (data.Length >= 10)
            {
                if (data.Contains(":"))
                {
                    data = data.Substring(0, 9) + "00:00:" + data.Substring(9);
                    tmp = data.Split(':');
                }
                else if (data.Contains("-"))
                {
                    data = data.Substring(0, 9) + "00-00-" + data.Substring(9);
                    tmp = data.Split('-');
                }
                else if (data.Contains("."))
                {
                    tmp = data.Split('.');
                }
            }
            if (tmp != null)
            {
                data = null;
                foreach (string s in tmp)
                {
                    data += s;
                }
            }
            return data;
        }

        /// <summary>
        /// Constructor using a string containing a 6 byte MAC address (use for Base stations only)
        /// Allows the folowing MAC formats
        ///		"2e:00:20:39:21:4e"
        ///		"2e-00-20-39-21-4e"
        ///		"2e.00.20.39.21.4e"
        /// </summary>
        public MAC_Address(String Input)
        {
            try
            {
                String MAC = null;

                Input = Compress(Input).ToUpper();  // Remove : - and .

                if (Input.Length == 12) // Ethernet address
                {
                    MAC = Input.Substring(0, 6) + "0000" + Input.Substring(6);
                }
                else
                {
                    if (Input.Length == 16)
                    {
                        MAC = Input;
                    }
                    else if (Input.Length == 11 || Input.Length == 8)
                    {
                        if (Input[0] >= 'A' && Input[0] <= 'C')
                        {
                            if (Input[0] == 'A')
                                MAC = "00124B" + ((Input.Length == 8) ? "000" : "") + Input.Substring(1);
                            else
                                MAC = "3889DC" + ((Input.Length == 8) ? "000" : "") + Input.Substring(1);
                        }
                    }
                }

                if (MAC == null)
                    return;

                UInt64.TryParse(MAC, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out _MAC);
            }
            catch
            {

            }
        }

        /// <summary>
        /// Returns a 8 hexadecimal digit string that represents the last 28 bit of the MAC address of an ESL
        /// </summary>
        public override String ToString()
        {
            if ((_MAC >> 40) == 0x3889DC)
                return String.Format("B{0:x07}", _MAC & 0xFFFFFFF).ToUpper();
            else if ((_MAC >> 40) == 0x00124B)
                return String.Format("A{0:x10}", _MAC & 0xFFFFFFFFFF).ToUpper();
            else
                return String.Format("C{0:x07}", _MAC & 0xFFFFFFF).ToUpper();
        }

        public String ToLongString()        // Legacy MAC address format
        {
            return String.Format("{0:x016}", _MAC).ToUpper();
        }

        /// <summary>
        /// Returns if the specified mac address is equal to this mac address
        /// </summary>
        public bool Equals(MAC_Address mac)
        {
            return this.Int64 == mac.Int64;
        }

        /// <summary>
        /// Returns the 48 bits MAC address of a base station in following the hexidecimal format: 12.34.56.78.9A.BC
        /// </summary>
        public String ToHex48String()
        {
            return String.Format("{0:x02}.{1:x02}.{2:x02}.{3:x02}.{4:x02}.{5:x02}", (_MAC >> 56) & 0xFF, (_MAC >> 48) & 0xFF,
                (_MAC >> 40) & 0xFF, (_MAC >> 16) & 0xFF, (_MAC >> 8) & 0xFF, _MAC & 0xFF).ToUpper();
        }

        internal void CopyTo(Byte[] b, int offset)
        {
            if ((b.Length - offset) >= 8)
            {
                int i = 7 + offset;

                b[i--] = (Byte)_MAC;
                b[i--] = (Byte)(_MAC >> 8);
                b[i--] = (Byte)(_MAC >> 16);
                b[i--] = (Byte)(_MAC >> 24);
                b[i--] = (Byte)(_MAC >> 32);
                b[i--] = (Byte)(_MAC >> 40);
                b[i--] = (Byte)(_MAC >> 48);
                b[i--] = (Byte)(_MAC >> 56);
            }
            else if ((b.Length - offset) == 6)      // 48 bit format
            {
                int i = 0;

                b[i++] = (Byte)(_MAC >> 56);
                b[i++] = (Byte)(_MAC >> 48);
                b[i++] = (Byte)(_MAC >> 40);
                b[i++] = (Byte)(_MAC >> 16);
                b[i++] = (Byte)(_MAC >> 8);
                b[i++] = (Byte)(_MAC);
            }
        }

        internal void CopyToRev(Byte[] b, ref int offset)
        {
            if ((b.Length - offset) >= 8)
            {
                b[offset++] = (Byte)_MAC;
                b[offset++] = (Byte)(_MAC >> 8);
                b[offset++] = (Byte)(_MAC >> 16);
                b[offset++] = (Byte)(_MAC >> 24);
                b[offset++] = (Byte)(_MAC >> 32);
                b[offset++] = (Byte)(_MAC >> 40);
                b[offset++] = (Byte)(_MAC >> 48);
                b[offset++] = (Byte)(_MAC >> 56);
            }
        }

        /// <summary>
        /// Returns the MAC address as 64-bit unsigned long
        /// </summary>
        public UInt64 Int64
        {
            get
            {
                return _MAC;
            }
            set
            {
                _MAC = value;
            }


        }

        /// <summary>
        /// Returns whether this current instance is a valid MAC-address
        /// </summary>
        public bool IsValid
        {
            get
            {
                return ((_MAC >> 40) == 0x3889DC || (_MAC >> 40) == 0x00124B);
            }
        }
    }
}
