// Copyright (c) 2013 Eugen Pechanec

using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Net.NetworkInformation;
using System.Security.Permissions;

namespace EugenPechanec.NativeWifi {
    //= BASIC =========================================================================================
    [StructLayout(LayoutKind.Sequential)]
    internal struct Dot11CountryOrRegionString {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
        public byte[] Value;

        public override String ToString() {
            return Encoding.Default.GetString(Value);
        }
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct Dot11MacAddress {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
        public byte[] Value;

        public static Dot11MacAddress Wildcard {
            get {
                Dot11MacAddress wildcard = new Dot11MacAddress();
                wildcard.Value = new byte[6] { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
                return wildcard;
            }
        }

        public static PhysicalAddress ToPhysicalAddress(Dot11MacAddress mac) {
            return new PhysicalAddress(mac.Value);
        }

        public static Dot11MacAddress FromPhysicalAddress(PhysicalAddress phy) {
            Dot11MacAddress mac = new Dot11MacAddress();
            mac.Value = phy.GetAddressBytes();
            return mac;
        }
    }
    //= DOT11 =========================================================================================
    [StructLayout(LayoutKind.Sequential)]
    public struct Dot11AuthCipherPair {
        public Dot11AuthAlgorithm AuthAlgo;
        public Dot11CipherAlgorithm CipherAlgo;
    }
    [StructLayout(LayoutKind.Sequential)]
    internal struct Dot11BssidList { // always process and pass as IntPtr, never interop directly
        private NdisObjectHeader header;
        private Dot11BssidListHeader listHeader;
        //[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
        private Dot11MacAddress[] bssids; //dynamic array Dot11MacAddress[]

        public static Dot11BssidList Build(Dot11MacAddress[] bssids) {
            Dot11BssidList list = new Dot11BssidList();
            int maxLength = CalculateMaxLength();
            list.listHeader.TotalNumOfEntries = (ushort)maxLength;
            list.header = NdisObjectHeader.CreateDefault();
            list.Entries = bssids;
            return list;
        }

        private static int CalculateSize(ref Dot11BssidList list) {
            int ndisHeaderSize = Marshal.SizeOf(typeof(NdisObjectHeader));
            int listHeaderSize = Marshal.SizeOf(typeof(Dot11BssidListHeader));
            int macAddressSize = Marshal.SizeOf(typeof(Dot11MacAddress));
            return ndisHeaderSize + listHeaderSize + list.bssids.Length * macAddressSize;
        }

        private static int CalculateMaxLength() {
            int maxSize = ushort.MaxValue;
            int ndisHeaderSize = Marshal.SizeOf(typeof(NdisObjectHeader));
            int listHeaderSize = Marshal.SizeOf(typeof(Dot11BssidListHeader));
            int macAddressSize = Marshal.SizeOf(typeof(Dot11MacAddress));
            int availableSpace = maxSize - ndisHeaderSize - listHeaderSize;
            return availableSpace / macAddressSize;
        }

        public NdisObjectHeader Header {
            get {
                return header;
            }
        }
        public Dot11BssidListHeader ListHeader {
            get {
                return listHeader;
            }
        }
        public Dot11MacAddress[] Entries {
            get {
                return bssids;
            }
            set {
                if (value == null) throw new ArgumentException();
                if (value.Length > listHeader.TotalNumOfEntries) throw new ArgumentException();
                listHeader.NumOfEntries = (uint)value.Length;
                bssids = value;
                int size = CalculateSize(ref this);
                header.Size = (ushort)size;
            }
        }
    }
    [StructLayout(LayoutKind.Sequential)]
    internal struct Dot11BssidListHeader {
        public uint NumOfEntries; //dataSize of dynamic array
        public uint TotalNumOfEntries;
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct Dot11Network {
        public Dot11Ssid Ssid;
        public Dot11BssType BssType;
    }
    [StructLayout(LayoutKind.Sequential)]
    internal struct Dot11NetworkList {
        public uint NumberOfItems; //dataSize of dynamic array
        public uint Index;
        //[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
        public IntPtr NetworkList; //dynamic array Dot11NetworkList[]
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct Dot11Ssid {
        private int ssidLength; //uint
        [MarshalAs(UnmanagedType.ByValArray, SizeConst=32)]
        private byte[] ssid;

        public string Ssid {
            get {
                return Encoding.Default.GetString(ssid, 0, ssidLength);
            }
            set {
                int length = value.Length > 31 ? 32 : value.Length;
                ssid = new byte[32];
                Array.Copy(Encoding.Default.GetBytes(value.ToCharArray(), 0, length), ssid, length);
                //TODO check last \0
                ssidLength = length;
            }
        }

        public Dot11Ssid(String SSID) : this() {
            this.Ssid = SSID;
        }

        public byte[] ToByteArray() {
            byte[] array = new byte[ssidLength];
            Array.Copy(ssid, array, ssidLength);
            return array;
        }

        public string ToHex() {
            return Util.ToHex(ssid);
        }
    }
    [StructLayout(LayoutKind.Sequential)]
    internal struct NdisObjectHeader {
        public byte Type;
        public byte Revision;
        public ushort Size;

        public static NdisObjectHeader CreateDefault() {
            NdisObjectHeader header = new NdisObjectHeader();
            header.Type = 0x80;
            header.Revision = 1;
            header.Size = (ushort)Marshal.SizeOf(header);
            return header;
        }
    }
    //= WLAN ==========================================================================================
    [StructLayout(LayoutKind.Sequential)]
    public struct WlanAssociationAttributes {
        public Dot11Ssid Ssid;
        public Dot11BssType BssType;
        public Dot11MacAddress MacAddress;
        public Dot11PhyType PhyType;
        public uint PhyIndex;
        public uint LanSignalQuality;
        public uint RxRate;
        public uint TxRate;
    }
    [StructLayout(LayoutKind.Sequential)]
    internal struct WlanAuthCipherPairList {
        public uint NumberOfItems; //dataSize of dynamic array
        //[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
        public IntPtr AuthCipherPairList; //dynamic array Dot11AuthCipherPair[]
    }
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct WlanAvailableNetwork {
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
        public string ProfileName;
        public Dot11Ssid Ssid;
        public Dot11BssType BssType;
        public uint NumberOfBssids;
        public bool NetworkConnectable;
        public WlanReasonCode NotConnectableReason;
        private uint numberOfPhyTypes;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
        private Dot11PhyType[] dot11PhyTypes;
        public Dot11PhyType[] Dot11PhyTypes {
            get {
                Dot11PhyType[] array = new Dot11PhyType[numberOfPhyTypes];
                Array.Copy(dot11PhyTypes, array, numberOfPhyTypes);
                return array;
            }
            set {
                if (value.Length > 8) throw new ArgumentException();
                dot11PhyTypes = new Dot11PhyType[8];
                Array.Copy(value, dot11PhyTypes, value.Length);
                numberOfPhyTypes = (uint) value.Length;
            }
        }
        [MarshalAs(UnmanagedType.Bool)]
        public bool MorePhyTypes;
        public uint SignalQuality;
        [MarshalAs(UnmanagedType.Bool)]
        public bool SecurityEnabled;
        public Dot11AuthAlgorithm DefaultAuthAlgo;
        public Dot11CipherAlgorithm DefaultCipherAlgo;
        public WlanAvailableNetworkFlags Flags;
        private uint reserved;
    }
    [StructLayout(LayoutKind.Sequential)]
    internal struct WlanAvailableNetworkList {
        public uint NumberOfItems; //dataSize of dynamic array
        public uint Index;
        //[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
        public IntPtr Network; //dynamic array WlanAvailableNetwork[]
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct WlanBssEntry {
        public Dot11Ssid Ssid;
        public uint PhyId;
        public Dot11MacAddress MacAddress;
        public Dot11BssType BssType;
        public Dot11PhyType PhyType;
        public int Rssi;
        public uint LinkQuality;
        public byte InRegDomain;
        public ushort BeaconPeriod;
        public ulong Timestamp;
        public ulong HostTimestamp;
        public ushort CapabilityInformation;
        public uint ChCenterFrequency;
        public WlanRateSet RateSet;
        public uint IeOffset;
        public uint IeSize;
    }
    [StructLayout(LayoutKind.Sequential)]
    internal struct WlanBssList {
        public uint TotalSize;
        public uint NumberOfItems;//dataSize of dynamic array
        //[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
        public IntPtr BssEntries;//dynamic array WlanBssEntry[]
    }
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct WlanConnectionAttributes {
        public WlanInterfaceState InterfaceState;
        public WlanConnectionMode ConnectionMode;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
        public string ProfileName;
        public WlanAssociationAttributes AssociationAttributes;
        public WlanSecurityAttributes SecurityAttributes;
    }
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct WlanConnectionNotificationData {
        public WlanConnectionMode ConnectionMode;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
        public string ProfileName;
        public Dot11Ssid Ssid;
        public Dot11BssType BssType;
        [MarshalAs(UnmanagedType.Bool)]
        public bool SecurityEnabled;
        public WlanReasonCode ReasonCode;
        public WlanConnectionNotificationFlags Flags;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1)]
        public string ProfileXml; //null-terminated wide character string
    }
    [StructLayout(LayoutKind.Sequential)]
    internal struct WlanConnectionParameters : IDisposable {
        public WlanConnectionMode ConnectionMode;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string Profile;
        internal IntPtr Ssid;
        internal IntPtr DesiredBssidList;
        //public Dot11Ssid? Ssid;
        //public Dot11BssidList? DesiredBssidList;
        public Dot11BssType BssType;
        public WlanConnectionFlags Flags;

        public void Dispose() {
            if (Ssid != IntPtr.Zero) {
                Marshal.FreeHGlobal(Ssid);
                Ssid = IntPtr.Zero;
            }
            if (DesiredBssidList != IntPtr.Zero) {
                Marshal.FreeHGlobal(DesiredBssidList);
                DesiredBssidList = IntPtr.Zero;
            }
        }
    }
    [StructLayout(LayoutKind.Sequential)]
    internal struct WlanCountryOrRegionStringList {
        public uint NumberOfItems;//dataSize of dynamic array
        //[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
        public IntPtr CountryOrRegionStringList;//dynamic array Dot11CountryOrRegionString[]
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct WlanInterfaceCapability {
        public WlanInterfaceType Type;
        [MarshalAs(UnmanagedType.Bool)]
        public bool DSupported;
        public uint MaxDesiredSsidListSize;
        public uint MaxDesiredBssidListSize;
        private uint numberOfSupportedPhys;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
        private Dot11PhyType[] phyTypes;

        public Dot11PhyType[] PhyTypes {
            get {
                Dot11PhyType[] array = new Dot11PhyType[numberOfSupportedPhys];
                Array.Copy(phyTypes, array, numberOfSupportedPhys);
                return array;
            }
            set {
                if (value.Length > 64) throw new ArgumentException();
                phyTypes = new Dot11PhyType[64];
                Array.Copy(value, phyTypes, value.Length);
                numberOfSupportedPhys = (uint)value.Length;
            }
        }
    }
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct WlanInterfaceInfo {
        public Guid Guid;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
        public string Description;
        public WlanInterfaceState State;
    }
    [StructLayout(LayoutKind.Sequential)]
    internal struct WlanInterfaceInfoList {
        public uint NumberOfItems;//dataSize of dynamic array
        public uint Index;
        //[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
        public IntPtr InterfaceInfo;//dynamic array WlanInterfaceInfo[]
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct WlanMacFrameStatistics {
        public ulong TransmittedFrameCount;
        public ulong ReceivedFrameCount;
        public ulong WEPExcludedCount;
        public ulong TKIPLocalMICFailures;
        public ulong TKIPReplays;
        public ulong TKIPICVErrorCount;
        public ulong CCMPReplays;
        public ulong CCMPDecryptErrors;
        public ulong WEPUndecryptableCount;
        public ulong WEPICVErrorCount;
        public ulong DecryptSuccessCount;
        public ulong DecryptFailureCount;
    }
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct WlanMsmNotificationData {
        public WlanConnectionMode ConnectionMode;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
        public string ProfileName;
        public Dot11Ssid SSID;
        public Dot11BssType BssType;
        public Dot11MacAddress MacAddress;
        [MarshalAs(UnmanagedType.Bool)]
        public bool SecurityEnabled;
        [MarshalAs(UnmanagedType.Bool)]
        public bool FirstPeer;
        [MarshalAs(UnmanagedType.Bool)]
        public bool LastPeer;
        public WlanReasonCode ReasonCode;
    }
    [StructLayout(LayoutKind.Sequential)]
    internal struct WlanNotificationData {
        public WlanNotificationSource NotificationSource;
        private uint notificationCode;
        public Guid InterfaceGuid;
        internal uint DataSize;
        internal IntPtr Data;

        public object NotificationCode {
            get {
                switch (NotificationSource) {
                    case WlanNotificationSource.Msm:
                        return (WlanMsmNotificationCode)notificationCode;
                    case WlanNotificationSource.Acm:
                        return (WlanAcmNotificationCode)notificationCode;
                    case WlanNotificationSource.HostedNetwork:
                        return (WlanHostedNetworkNotificationCode)notificationCode;
                    case WlanNotificationSource.OneX:
                        return (OneXNotificationType)notificationCode;
                    default:
                        return notificationCode;
                }
            }
        }
        //NOTE Data is internally accessible. Means it must be parsed probably by WlanClient class.
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct WlanPhyFrameStatistics {
        public ulong TransmittedFrameCount;
        public ulong MulticastTransmittedFrameCount;
        public ulong FailedCount;
        public ulong RetryCount;
        public ulong MultipleRetryCount;
        public ulong MaxTXLifetimeExceededCount;
        public ulong TransmittedFragmentCount;
        public ulong RTSSuccessCount;
        public ulong RTSFailureCount;
        public ulong ACKFailureCount;
        public ulong ReceivedFrameCount;
        public ulong MulticastReceivedFrameCount;
        public ulong PromiscuousReceivedFrameCount;
        public ulong MaxRXLifetimeExceededCount;
        public ulong FrameDuplicateCount;
        public ulong ReceivedFragmentCount;
        public ulong PromiscuousReceivedFragmentCount;
        public ulong FCSErrorCount;
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct WlanPhyRadioState {
        public uint PhyIndex;
        public Dot11RadioState SoftwareRadioState;
        public Dot11RadioState HardwareRadioState;
    }
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct WlanProfileInfo {
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
        public string ProfileName;
        public WlanProfileFlags Flags;
    }
    [StructLayout(LayoutKind.Sequential)]
    internal struct WlanProfileInfoList {
        public uint NumberOfItems;//dataSize of dynamic array
        public uint Index;
        //[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
        public IntPtr ProfileInfo;//dynamic array WlanProfileInfo[]
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct WlanRadioState {
        private uint numberOfPhys;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
        private WlanPhyRadioState[] phyRadioState;

        public WlanPhyRadioState[] PhyRadioState {
            get {
                WlanPhyRadioState[] array = new WlanPhyRadioState[numberOfPhys];
                Array.Copy(phyRadioState, array, numberOfPhys);
                return array;
            }
            set {
                if (value.Length > 64) throw new ArgumentException();
                phyRadioState = new WlanPhyRadioState[64];
                Array.Copy(value, phyRadioState, value.Length);
                numberOfPhys = (uint)value.Length;
            }
        }
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct WlanRateSet {
        private uint rateSetLength;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 126)]
        private ushort[] rateSet;

        public ushort[] RateSet {
            get {
                ushort[] array = new ushort[rateSetLength];
                Array.Copy(rateSet, array, rateSetLength);
                return array;
            }
            set {
                if (value.Length > 64) throw new ArgumentException();
                rateSet = new ushort[126];
                Array.Copy(value, rateSet, value.Length);
                rateSetLength = (uint)value.Length;
            }
        }
    }
    [StructLayout(LayoutKind.Sequential)]
    internal struct WlanRawData {
        internal uint DataSize;//dataSize of dynamic array
        //[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
        public IntPtr DataBlob;//dynamic array byte[]
    }
    [StructLayout(LayoutKind.Sequential)]
    internal struct WlanRawDataHeader {
        public uint DataOffset;
        public uint DataSize;
    }
    [StructLayout(LayoutKind.Sequential)]
    internal struct WlanRawDataListHeader { // originally WlanRawDataList
        public uint TotalSize;
        public uint NumberOfItems;
        // here follows dwNumberOfItems of type WlanRawDataHeader in memory.
        // here follows dwNumberOfItems of type WlanRawData in memory.
    }
    [StructLayout(LayoutKind.Sequential)] 
    internal struct WlanRawDataList {// always process and pass as IntPtr, never interop directly
        public WlanRawDataListHeader ListHeader;
        public WlanRawDataHeader[] DataHeaders;
        public WlanRawData[] Data;

        public uint TotalSize {
            get {
                return ListHeader.TotalSize;
            }
        }

        public uint NumberOfItems {
            get {
                return ListHeader.NumberOfItems;
            }
        }
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct WlanSecurityAttributes {
        [MarshalAs(UnmanagedType.Bool)]
        public bool SecurityEnabled;
        [MarshalAs(UnmanagedType.Bool)]
        public bool OneXEnabled;
        public Dot11AuthAlgorithm AuthAlgo;
        public Dot11CipherAlgorithm CipherAlgo;
    }
    [StructLayout(LayoutKind.Sequential)]
    internal struct WlanStatistics {
        public ulong FourWayHandshakeFailures;
        public ulong TKIPCounterMeasuresInvoked;
        public ulong Reserved;
        public WlanMacFrameStatistics MacUcastCounters;
        public WlanMacFrameStatistics MacMcastCounters;
        public uint NumberOfPhys;//dataSize of dynamic array
        //[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
        public IntPtr PhyCounters;//dynamic array WlanPhyFrameStatistics[]
    }
    //= HOSTED NETWORK ================================================================================

    [StructLayout(LayoutKind.Sequential)]
    public struct WlanHostedNetworkConnectionSettings {
        public Dot11Ssid Ssid;
        public uint MaxNumberOfPeers;

        public WlanHostedNetworkConnectionSettings(Dot11Ssid SSID, uint MaxNumberOfPeers) {
            this.Ssid = SSID;
            this.MaxNumberOfPeers = MaxNumberOfPeers;
        }
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct WlanHostedNetworkDataPeerStateChange {
        public WlanHostedNetworkPeerState OldState;
        public WlanHostedNetworkPeerState NewState;
        public WlanHostedNetworkReason StateChangeReason;
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct WlanHostedNetworkPeerState {
        public Dot11MacAddress macAddress;
        public WlanHostedNetworkPeerAuthState AuthState;
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct WlanHostedNetworkRadioState {
        public Dot11RadioState SoftwareRadioState;
        public Dot11RadioState HardwareRadioState;
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct WlanHostedNetworkSecuritySettings {
        public Dot11AuthAlgorithm AuthAlgo;
        public Dot11CipherAlgorithm CipherAlgo;
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct WlanHostedNetworkStateChange {
        public WlanHostedNetworkState OldState;
        public WlanHostedNetworkState NewState;
        public WlanHostedNetworkReason StateChangeReason;
    }
    [StructLayout(LayoutKind.Sequential)]
    internal struct WlanHostedNetworkStatus {
        public WlanHostedNetworkState State;
        public Guid IPDeviceID;
        public Dot11MacAddress MacAddress;
        public Dot11PhyType PhyType;
        public uint ChannelFrequency;
        public uint NumberOfPeers;//dataSize of dynamic array
        //[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
        public IntPtr PeerList;//dynamic array WlanHostedNetworkPeerState[]
    }

}