using Apewer;
using Apewer.Internals.Interop;
using Apewer.Network;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;

namespace Apewer
{

    /// <summary></summary>
    public class NetworkUtility
    {

        #region UDP

        /// <summary>唤醒局域网中拥有指定 MAC 地址的设备。</summary>
        /// <param name="mac">被唤醒设备的 MAC 地址,必须是长度为 6 的字节数组。</param>
        public static void WakeOnLan(byte[] mac)
        {
            if (mac.Length != 6) return;

            var uc = new System.Net.Sockets.UdpClient();
            uc.Connect(IPAddress.Broadcast, 65535);

            var pack = new List<byte>();

            // 前 6 字节为 0xFF。
            for (int i = 0; i < 6; i++) pack.Add(255);

            // 目标 MAC 地址重复 16 次。
            for (int i = 0; i < 16; i++)
            {
                for (int j = 0; j < 6; j++) pack.Add(mac[j]);
            }

            // 发送 102 字节数据。
            uc.Send(pack.ToArray(), pack.Count);

            uc.Close();
        }

        /// <summary>从 NTP 服务器获取 UTC 时间。</summary>
        public static DateTime GetUtcFromNtp(string server = "pool.ntp.org", int port = 123, int timeout = 1000)
        {
            try
            {
                var addresses = Dns.GetHostEntry(server).AddressList;
                if (addresses.Length > 0)
                {
                    var endpoint = new IPEndPoint(addresses[0], port);
                    return GetUtcFromNtp(endpoint, timeout);
                }
            }
            catch { }
            return new DateTime(1, 0, 0, 0, 0, 0, 0, DateTimeKind.Utc);
        }

        /// <summary>从 NTP 服务器获取 UTC 时间。</summary>
        public static DateTime GetUtcFromNtp(IPEndPoint endpoint, int timeout = 1000)
        {
            try
            {
                var request = new byte[48];
                request[0] = 0x1B;

                var response = new byte[48];
                response[0] = 0x1B;

                var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                socket.Connect(endpoint);
                socket.ReceiveTimeout = timeout;
                socket.Send(request);
                socket.Receive(response);
                socket.Close();

                const byte replytime = 40;
                ulong secondspart = BitConverter.ToUInt32(response, replytime);
                ulong secondsfraction = BitConverter.ToUInt32(response, replytime + 4);
                secondspart = SwapEndian(secondspart);
                secondsfraction = SwapEndian(secondsfraction);
                ulong milliseconds = (secondspart * 1000) + ((secondsfraction * 1000) / 0x100000000UL);

                var utc = (new DateTime(1900, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).AddMilliseconds(milliseconds);
                return utc;
            }
            catch { }
            return new DateTime(1, 0, 0, 0, 0, 0, 0, DateTimeKind.Utc);
        }

        private static uint SwapEndian(ulong x)
        {
            var a = ((x & 0x000000ff) << 24);
            var b = ((x & 0x0000ff00) << 8);
            var c = ((x & 0x00ff0000) >> 8);
            var d = ((x & 0xff000000) >> 24);
            return (uint)(a + b + c + d);
        }

        #endregion

        #region ARP

#if NET40 || NET461

        /// <summary>发送 ARP 请求,获取目标 IP 地址对应设备的 MAC 地址,MAC 格式为 01-23-45-67-89-AB。</summary>
        /// <param name="ip">目标 IP 地址。</param>
        /// <returns>MAC 地址。</returns>
        public static string SendARP(string ip)
        {
            if (!string.IsNullOrEmpty(ip))
            {
                try
                {
                    var ipa = IPAddress.Parse(ip);
                    uint dip = BitConverter.ToUInt32(ipa.GetAddressBytes(), 0);
                    ulong mac = 0;
                    uint len = 6;
                    uint err = IpHlpApi.SendARP(dip, 0, ref mac, ref len);
                    byte[] bytes = BitConverter.GetBytes(mac);
                    return BitConverter.ToString(bytes, 0, 6);
                }
                catch { }
            }
            return "";
        }

#endif

        #endregion

        #region Puny Code

        /// <summary></summary>
        public static string ToPunyCode(string chinese)
        {
            if (string.IsNullOrEmpty(chinese)) return "";
            try { return new IdnMapping().GetAscii(chinese); }
            catch { return chinese; }
        }

        /// <summary></summary>
        public static string FromPunyCode(string punycode)
        {
            if (string.IsNullOrEmpty(punycode)) return "";
            try { return new IdnMapping().GetUnicode(punycode); }
            catch { return punycode; }
        }

        #endregion

        #region IP

        /// <summary>获取本地计算机的计算机名。</summary>
        public static string LocalHost
        {
            get
            {
                var hn = Dns.GetHostName();
                return string.IsNullOrEmpty(hn) ? "" : hn;
            }
        }

        /// <summary>本地计算机的 IP 地址。</summary>
        public static List<string> LocalIP
        {
            get
            {
                var list = new List<string>();
                var he = Dns.GetHostEntry(Dns.GetHostName());
                foreach (var ip in he.AddressList)
                {
                    list.Add(ip.ToString());
                }
                return list;
            }
        }

        /// <summary>判断 IPv4 地址格式是否正确。</summary>
        public static bool IsIP(string ipv4)
        {
            try
            {
                if (string.IsNullOrEmpty(ipv4)) return false;

                var split = ipv4.Split('.');
                if (split.Length != 4) return false;

                for (int i = 0; i < 4; i++)
                {
                    var n = Convert.ToInt32(split[i]);
                    if (n < 0 || n > 255) return false;
                    if (n.ToString() != split[i]) return false;
                }
                return true;
            }
            catch { }
            return false;
        }

        /// <summary>对目标地址进行解析。</summary>
        public static string Resolve(string host)
        {
            try
            {
                if (IsIP(host))
                {
                    var ip = IPAddress.Parse(host);
                    var he = Dns.GetHostEntry(ip);
                    return he.HostName;
                }
                else
                {
                    var ip = "";
                    var dn = host.ToLower();
                    var he = Dns.GetHostEntry(dn);
                    var ts = "";
                    var on = he.Aliases;
                    he = Dns.GetHostEntry(dn);
                    for (int i = 0; i < on.Length; i++) ts = ts + on[i].ToString() + ",";
                    ts = "";
                    var al = he.AddressList;
                    for (int i = 0; i < al.Length; i++) ts = ts + al[i].ToString() + ",";
                    ip = ts;
                    if (ip.Length > 0)
                    {
                        if (ip.Substring(ip.Length - 1, 1) == ",") ip = ip.Substring(0, ip.Length - 1);
                    }
                    return ip;
                }
            }
            catch { }
            return "";
        }

        /// <summary>对目标地址进行解析。</summary>
        private static IPHostEntry InternalResolve(string hostName)
        {
            if (hostName.Length <= 255 && (hostName.Length != 255 || hostName[254] == '.'))
            {
                var intPtr = WS2_32.gethostbyname(hostName);
                if (intPtr != IntPtr.Zero) return NativeToHostEntry(intPtr);
            }
            return null;
        }

        private static IntPtr IntPtrAdd(IntPtr a, int b)
        {
            return (IntPtr)((long)a + b);
        }

        private static IPHostEntry NativeToHostEntry(IntPtr nativePointer)
        {
            hostent hostent = (hostent)Marshal.PtrToStructure(nativePointer, typeof(hostent));
            IPHostEntry iPHostEntry = new IPHostEntry();
            if (hostent.h_name != IntPtr.Zero)
            {
                iPHostEntry.HostName = Marshal.PtrToStringAnsi(hostent.h_name);
            }
            ArrayList arrayList = new ArrayList();
            IntPtr intPtr = hostent.h_addr_list;
            nativePointer = Marshal.ReadIntPtr(intPtr);
            while (nativePointer != IntPtr.Zero)
            {
                int newAddress = Marshal.ReadInt32(nativePointer);
                arrayList.Add(new IPAddress(newAddress));
                intPtr = IntPtrAdd(intPtr, IntPtr.Size);
                nativePointer = Marshal.ReadIntPtr(intPtr);
            }
            iPHostEntry.AddressList = new IPAddress[arrayList.Count];
            arrayList.CopyTo(iPHostEntry.AddressList, 0);
            arrayList.Clear();
            intPtr = hostent.h_aliases;
            nativePointer = Marshal.ReadIntPtr(intPtr);
            while (nativePointer != IntPtr.Zero)
            {
                string value = Marshal.PtrToStringAnsi(nativePointer);
                arrayList.Add(value);
                intPtr = IntPtrAdd(intPtr, IntPtr.Size);
                nativePointer = Marshal.ReadIntPtr(intPtr);
            }
            iPHostEntry.Aliases = new string[arrayList.Count];
            arrayList.CopyTo(iPHostEntry.Aliases, 0);
            return iPHostEntry;
        }

        /// <summary>将由字符串表示的 IPv4 地址转换为 32 位整数。</summary>
        public static int GetNumber(IPAddress ipv4)
        {
            try
            {
                var ba = ipv4.GetAddressBytes();
                return BitConverter.ToInt32(ba, 0);
            }
            catch { return 0; }
        }

        /// <summary>转换 IP 地址格式。</summary>
        public static string GetPlainAddress(IPAddress address)
        {
            try { return address.ToString(); }
            catch { return ""; }
        }

        /// <summary>转换 IP 地址格式。</summary>
        public static string GetPlainAddress(IPEndPoint endpoint)
        {
            try { return GetPlainAddress(endpoint.Address); }
            catch { return ""; }
        }

        /// <summary>转换 IP 地址格式。</summary>
        public static IPAddress GetIPAddress(string address)
        {
            try { return IPAddress.Parse(address); }
            catch { return new IPAddress(0); }
        }

        /// <summary>转换 IP 地址格式。</summary>
        public static IPEndPoint GetIPEndPoint(string address, int port)
        {
            try { return new IPEndPoint(IPAddress.Parse(address), NumberUtility.RestrictValue(port, 0, ushort.MaxValue)); }
            catch { return new IPEndPoint(0, 0); }
        }

        /// <summary>转换 IP 地址格式。</summary>
        public static IPEndPoint GetIPEndPoint(IPAddress address, int port)
        {
            try { return new IPEndPoint(address, NumberUtility.RestrictValue(port, 0, ushort.MaxValue)); }
            catch { return new IPEndPoint(0, 0); }
        }

        /// <summary>转换 IP 地址格式。</summary>
        public static IPEndPoint GetIPEndPoint(EndPoint endpoint)
        {
            try { return (IPEndPoint)endpoint; }
            catch { return new IPEndPoint(0, 0); }
        }

        /// <summary>判断私有 IP 地址。</summary>
        public static bool FromLAN(string ipv4)
        {
            if (ipv4.IsEmpty()) return false;

            // localhost
            if (ipv4 == "::1") return true;
            if (ipv4 == "127.0.0.1") return true;

            // IPv4
            var a = ipv4.Split('.');
            if (a.Length != 4) return false;
            switch (a[0])
            {
                case "10":
                    return true;
                case "172":
                    var a1 = TextUtility.GetInt32(a[1]);
                    if (a1 >= 16 && a1 <= 31) return true;
                    break;
                case "192":
                    if (a[1] == "168") return true;
                    break;
            }

            return false;
        }

        #endregion

        #region HTTP

        /// <summary>GET</summary>
        public static HttpClient HttpGet(string url, int timeout = 30000)
        {
            return HttpClient.SimpleGet(url, timeout);
        }

        /// <summary>POST</summary>
        public static HttpClient HttpPost(string url, byte[] data, int timeout = 30000, string type = "application/octet-stream")
        {
            return HttpClient.SimplePost(url, data, timeout, type);
        }

        /// <summary>POST text/plain</summary>
        public static HttpClient HttpPost(string url, string text, int timeout = 30000, string type = "text/plain")
        {
            return HttpClient.SimpleText(url, text, timeout, type);
        }

        /// <summary>POST application/x-www-form-urlencoded</summary>
        public static HttpClient HttpPost(string url, IDictionary<string, string> form, int timeout = 30000)
        {
            return HttpClient.SimpleForm(url, form, timeout);
        }

        /// <summary>POST application/x-www-form-urlencoded</summary>
        public static HttpClient HttpPost(string url, TextSet form, int timeout = 30000)
        {
            return HttpClient.SimpleForm(url, form, timeout);
        }

        #endregion

    }

}