using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web;

namespace PushPlatform.Common.Util
{
    /// <summary>
    /// Http请求辅助类
    /// </summary>
    public class HttpHelper
    {
        /// <summary>
        /// 私有构造方法，防止实例化
        /// </summary>
        private HttpHelper()
        {

        }

        /// <summary>
        /// 静态对象，单例模式
        /// </summary>
        private static HttpHelper _instance = null;

        public static HttpHelper Instance()
        {
            if (_instance == null)
                _instance = new HttpHelper();

            return _instance;
        }

        #region 获取响应结果
        /// <summary>
        /// 获取响应结果
        /// </summary>
        /// <param name="url">请求地址</param>
        /// <param name="parameters">请求参数对象</param>
        /// <param name="method">请求方式字符串</param>
        /// <returns>返回响应内容</returns>
        public string GetResponse(string url, object parametersObj, string method)
        {
            string responseStr = string.Empty; //定义请求返回的结果
            string parameters = string.Empty; //请求参数字符串
            if (parametersObj != null)
                parameters = GetParamStr(parametersObj);

            try
            {
                if (string.IsNullOrEmpty(url))
                    throw new Exception("url can not by empty");

                method = method ?? "GET"; //不填写请求方式默认为GET

                method = method.ToUpper(); //将请求方式字符串转为大写， 因为部分服务器Http谓词校验严格，导致例如'Get'这样的谓词不通过校验

                if (method == "GET")
                {
                    if (url.EndsWith("?"))
                        url = url + parameters;
                    else
                        url = url + "?" + parameters;
                }

                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); //创建请求
                request.Method = method;
                request.KeepAlive = true;
                request.Timeout = 50000; //请求超时时间
                if (method == "POST")
                {
                    ServicePointManager.Expect100Continue = false;
                    request.ContentType = "application/x-www-form-urlencoded";
                    Encoding encoding = Encoding.UTF8;
                    byte[] bs = encoding.GetBytes(parameters);
                    request.ContentLength = bs.Length;
                    using (Stream s = request.GetRequestStream())
                    {
                        s.Write(bs, 0, bs.Length);
                    }
                }
                using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
                {
                    while (response.StatusCode != HttpStatusCode.OK)
                    {
                        System.Threading.Thread.Sleep(500);
                    }

                    Stream s = response.GetResponseStream();
                    responseStr = new StreamReader(s, Encoding.UTF8).ReadToEnd();
                    s.Close();
                    s.Dispose();
                }
            }
            catch (Exception ex)
            {
                responseStr = ex.Message;
            }
            return responseStr;
        }
        #endregion

        #region Http上传文件
        /// <summary>
        /// Http上传文件
        /// </summary>
        /// <param name="url">上传接口地址</param>
        /// <param name="fileNameValue">文件名值</param>
        /// <param name="UploadStream">流</param>
        /// <param name="otherParameters">其他的参数</param>
        /// <param name="encoding">编码格式</param>
        /// <param name="fileNameKey">文件名键</param>
        /// <returns>接口返回内容</returns>
        public string HttpUploadFile(string url, string fileNameValue, Stream uploadStream, Dictionary<string, string> otherParameters, Encoding encoding, string fileNameKey = "fileName")
        {
            using (MemoryStream memoryStream = new MemoryStream())
            {
                // 1.分界线
                string boundary = string.Format("----{0}", DateTime.Now.Ticks.ToString("x")),       // 分界线可以自定义参数
                    beginBoundary = string.Format("--{0}\r\n", boundary),
                    endBoundary = string.Format("\r\n--{0}--\r\n", boundary);
                byte[] beginBoundaryBytes = encoding.GetBytes(beginBoundary),
                    endBoundaryBytes = encoding.GetBytes(endBoundary);
                // 2.组装开始分界线数据体 到内存流中
                memoryStream.Write(beginBoundaryBytes, 0, beginBoundaryBytes.Length);
                // 3.组装 上传文件附加携带的参数 到内存流中
                if (otherParameters != null && otherParameters.Count > 0)
                {
                    foreach (KeyValuePair<string, string> keyValuePair in otherParameters)
                    {
                        string parameterHeaderTemplate = string.Format("Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}\r\n{2}", keyValuePair.Key, keyValuePair.Value, beginBoundary);
                        byte[] parameterHeaderBytes = encoding.GetBytes(parameterHeaderTemplate);

                        memoryStream.Write(parameterHeaderBytes, 0, parameterHeaderBytes.Length);
                    }
                }
                // 4.组装文件头数据体 到内存流中
                string fileHeaderTemplate = string.Format("Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: application/octet-stream\r\n\r\n", fileNameKey, fileNameValue);
                byte[] fileHeaderBytes = encoding.GetBytes(fileHeaderTemplate);
                memoryStream.Write(fileHeaderBytes, 0, fileHeaderBytes.Length);
                // 5.组装文件流 到内存流中
                byte[] buffer = new byte[1024 * 1024 * 1];
                int size = uploadStream.Read(buffer, 0, buffer.Length);
                while (size > 0)
                {
                    memoryStream.Write(buffer, 0, size);
                    size = uploadStream.Read(buffer, 0, buffer.Length);
                }
                // 6.组装结束分界线数据体 到内存流中
                memoryStream.Write(endBoundaryBytes, 0, endBoundaryBytes.Length);
                // 7.获取二进制数据
                byte[] postBytes = memoryStream.ToArray();
                // 8.HttpWebRequest 组装
                HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(new Uri(url, UriKind.RelativeOrAbsolute));
                webRequest.Method = "POST";
                webRequest.Timeout = 10000;
                webRequest.ContentType = string.Format("multipart/form-data; boundary={0}", boundary);
                webRequest.ContentLength = postBytes.Length;
                if (Regex.IsMatch(url, "^https://"))
                {
                    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;
                    ServicePointManager.ServerCertificateValidationCallback = CheckValidationResult;
                }
                // 9.写入上传请求数据
                using (Stream requestStream = webRequest.GetRequestStream())
                {
                    requestStream.Write(postBytes, 0, postBytes.Length);
                    requestStream.Close();
                }
                // 10.获取响应
                using (HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse())
                {
                    using (StreamReader reader = new StreamReader(webResponse.GetResponseStream(), encoding))
                    {
                        string body = reader.ReadToEnd();
                        reader.Close();
                        return body;
                    }
                }
            }
        }

        static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
        {
            return true;
        }
        #endregion

        #region Http下载文件
        /// <summary>
        /// Http下载文件
        /// </summary>
        /// <param name="url">下载地址</param>
        /// <param name="strFileName">保存的文件名</param>
        /// <returns>true下载成功，false下载失败</returns>
        public bool DeownloadFile(string url, string strFileName)
        {
            bool flag = false;
            //打开上次下载的文件
            long SPosition = 0;
            //实例化流对象
            FileStream FStream;
            //判断要下载的文件夹是否存在
            if (File.Exists(strFileName))
            {
                //打开要下载的文件
                FStream = File.OpenWrite(strFileName);
                //获取已经下载的长度
                SPosition = FStream.Length;
                FStream.Seek(SPosition, SeekOrigin.Current);
            }
            else
            {
                //文件不保存创建一个文件
                FStream = new FileStream(strFileName, FileMode.Create);
                SPosition = 0;
            }
            try
            {
                //打开网络连接
                HttpWebRequest myRequest = (HttpWebRequest)HttpWebRequest.Create(url);
                if (SPosition > 0)
                    myRequest.AddRange((int)SPosition);             //设置Range值
                //向服务器请求,获得服务器的回应数据流
                Stream myStream = myRequest.GetResponse().GetResponseStream();
                //定义一个字节数据
                byte[] btContent = new byte[512];
                int intSize = 0;
                intSize = myStream.Read(btContent, 0, 512);
                while (intSize > 0)
                {
                    FStream.Write(btContent, 0, intSize);
                    intSize = myStream.Read(btContent, 0, 512);
                }
                //关闭流
                FStream.Close();
                myStream.Close();
                flag = true;        //返回true下载成功
            }
            catch (Exception)
            {
                FStream.Close();
                flag = false;       //返回false下载失败
            }
            return flag;
        }
        #endregion

        #region 转参数对象为字符串
        /// <summary>
        /// 转参数对象为字符串
        /// </summary>
        /// <param name="obj">要转的对象</param>
        /// <returns>返回转换后的字符串</returns>
        private static string GetParamStr(object obj)
        {
            string param = string.Empty;

            var sPropertys = obj.GetType().GetProperties();

            foreach (var sp in sPropertys)
            {
                var name = sp.Name;

                var value = sp.GetValue(obj, null).ToString().Trim();

                param += name + "=" + value + "&";
            }

            param = param.Remove(param.LastIndexOf('&'), 1);

            return param;
        }
        #endregion

        #region 获取客户端IP地址
        /// <summary>
        /// 获取客户端IP地址
        /// </summary>
        /// <returns>返回IP地址</returns>
        public static string GetClientIp()
        {
            string ip = "";
            //Request.ServerVariables[""]--获取服务变量集合   
            if (HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"] != null) //判断发出请求的远程主机的ip地址是否为空  
            {
                //获取发出请求的远程主机的Ip地址  
                ip = HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"].ToString();
            }
            //判断登记用户是否使用设置代理  
            else if (HttpContext.Current.Request.ServerVariables["HTTP_VIA"] != null)
            {
                if (HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"] != null)
                {
                    //获取代理的服务器Ip地址  
                    ip = HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"].ToString();
                }
                else
                {
                    //获取客户端IP  
                    ip = HttpContext.Current.Request.UserHostAddress;
                }
            }
            else
            {
                //获取客户端IP  
                ip = HttpContext.Current.Request.UserHostAddress;
            }
            return ip;
        }
        #endregion
    }
}
