using System;
using System.Collections.Generic;
using System.Text;
using System.Management;

namespace clinkAPIcallPlus
{
    /// <summary>
    /// 盾sdk
    /// </summary>
    public class SDK
    {
        /// <summary>
        /// 操作系统类型
        /// </summary>
        private static string sysType = "";

        /// <summary>
        /// 设置是否自动更换冲突的端口(本函数要在clinkStart前先调用才有效)
        /// </summary>
        /// <param name="val">0表示不自动更换、1表示当该端口冲突时自动更换成新的端口
        /// 如果设成1，sdk先尝试用转发规则里配置的端口进行侦听，如果该端口已被其它程序占用，将随机生成一个可用的新端口进行侦听，所以在连接时要先通过dunGetCurrentTCPPort或dunGetCurrentUDPPort获取当前对应的新端口，再使用这个新端口来连接
        /// </param>       
        public static void dunSetAutoChangePort(int val)
        {
            string sys = Distinguish64or32System();
            if (sys == "32")
            {
                SDK32.dunSetAutoChangePort(val);
            }
            else
            {
                SDK64.dunSetAutoChangePort(val);
            }
        }

        /// <summary>
        /// 启动(只需要调用一次，重复调用也不会出错，调用时内部会判断是否已启动过，如果已启动过直接返回第一次调用的结果。调用时因客户端网络不通返回了0，过了段时间网络正常后我们会自动重连，这时再调用会返回150，这时如果没有调用业务也可正常运行)	 
        /// </summary>
        /// <param name="key">sdk配置密钥(从自己的实例中可以获取该密钥)</param>
        /// <returns>返回150表示成功，其它的值均为失败。150：成功、0：网络不通、1：已外部停止(如调用了停止函数，该函数一般不需要调用)、2：已内部停止(如运行过程中密钥被删除等)、170：实例到期或密钥不存在</returns>
        public static int clinkStart(string key)
        {
            string sys = Distinguish64or32System();
            int ret = 0;
            if (sys == "32")
            {
                ret = SDK32.clinkStart(key);
            }
            else
            {
                ret = SDK64.clinkStart(key);
            }
            return ret;
        }

        /// <summary>
        /// 获取客户端IP(转发规则中“获取IP方式”无论设成什么，这个接口都是有效的，这个是在客户端获取的，转发规则是给你的服务器端获取的，两个没有关系，只是获取到的客户端IP都是一样的)。有个别语言不支持无符号32位整数所以返回64位的
        /// </summary>
        /// <returns>返回的是有符号64位小字头整数IP值，通过其它函数可转成字符串格式的IP，返回0说明盾未启动或未启动成功</returns>   
        public static long dunGetClientIP()
        {
            string sys = Distinguish64or32System();
            long clientIP = 0;
            if (sys == "32")
            {
                clientIP = SDK32.dunGetClientIP();
            }
            else
            {
                clientIP = SDK64.dunGetClientIP();
            }
            return clientIP;
        }

        /// <summary>
        /// 获取当前tcp对应端口(使用本接口前题条件：在盾启动前先调用dunSetAutoChangePort(1)将盾设置成端口冲突时自动更换新的端口，这样调用这个本接口才有意义)
        /// </summary>
        /// <param name="ip">转发规则中的客户连接IP，也就是127开头的IP</param>
        /// <param name="port">转发规则中的客户连接端口</param>
        /// <returns>如果传入的端口没有冲突或转发规则不存在返回的将和传入的端口一样，如果有冲突返回的是改变后的新端口</returns>
        public static int dunGetCurrentTCPPort(string ip, int port)
        {
            string sys = Distinguish64or32System();
            int newPort = 0;
            if (sys == "32")
            {
                newPort = SDK32.dunGetCurrentTCPPort(ip, port);
            }
            else
            {
                newPort = SDK64.dunGetCurrentTCPPort(ip, port);
            }
            return newPort;
        }

        /// <summary>
        /// 获取当前udp对应端口(使用本接口前题条件：在盾启动前先调用dunSetAutoChangePort(1)将盾设置成端口冲突时自动更换新的端口，这样调用这个本接口才有意义)
        /// </summary>
        /// <param name="ip">转发规则中的客户连接IP，也就是127开头的IP</param>
        /// <param name="port">转发规则中的客户连接端口</param>
        /// <returns>如果传入的端口没有冲突或转发规则不存在返回的将和传入的端口一样，如果有冲突返回的是改变后的新端口</returns>
        public static int dunGetCurrentUDPPort(string ip, int port)
        {
            string sys = Distinguish64or32System();
            int newPort = 0;
            if (sys == "32")
            {
                newPort = SDK32.dunGetCurrentUDPPort(ip, port);
            }
            else
            {
                newPort = SDK64.dunGetCurrentUDPPort(ip, port);
            }
            return newPort;
        }

        /// <summary>
        /// 获取当前运行状态(一般不需要调用，用来在出现问题时定位问题用)
        /// </summary>
        /// <param name="ip">转发规则中的客户连接IP，也就是127开头的IP</param>
        /// <param name="port">转发规则中的客户连接端口</param>
        /// <returns>0：未启动、1：已外部停止、2：已内部停止、3：启动过程中、4：启动失败、150：运行成功、170：实例到期或密钥不存在</returns>
        public static int dunGetRunState()
        {
            string sys = Distinguish64or32System();
            int state = 0;
            if (sys == "32")
            {
                state = SDK32.dunGetRunState();
            }
            else
            {
                state = SDK64.dunGetRunState();
            }
            return state;
        }

        /// <summary>
        /// 设置侦听端口冲突时是否提示(一般不需要调用，默认是 1：提示)如果要调用本函数应在调用clinkStart前先调用
        /// </summary>
        /// <param name="portConflictAlert">侦听端口冲突时是否提示 0：不提示、1：提示</param>
        public static void setPortConflictAlert(int portConflictAlert)
        {
            string sys = Distinguish64or32System();

            if (sys == "32")
            {
                SDK32.setPortConflictAlert(portConflictAlert);
            }
            else
            {
                SDK64.setPortConflictAlert(portConflictAlert);
            }
        }

        #region 获取操作系统位数
        /// <summary>
        /// 获取操作系统位数
        /// </summary>
        /// <returns></returns>
        public static string Distinguish64or32System()
        {
            if (sysType != "") return sysType;
            try
            {
                string addressWidth = String.Empty;
                ConnectionOptions mConnOption = new ConnectionOptions();
                ManagementScope mMs = new ManagementScope("\\\\localhost", mConnOption);
                ObjectQuery mQuery = new ObjectQuery("select AddressWidth from Win32_Processor");
                ManagementObjectSearcher mSearcher = new ManagementObjectSearcher(mMs, mQuery);
                ManagementObjectCollection mObjectCollection = mSearcher.Get();
                foreach (ManagementObject mObject in mObjectCollection)
                {
                    addressWidth = mObject["AddressWidth"].ToString();
                }
                sysType = addressWidth;
                return addressWidth;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
                return String.Empty;
            }
        }
        #endregion
    }
}
