using System.Net;
using System.Text;

namespace DataSharing
{
    /// <summary>
    /// Post提交（包含文件提交）
    /// </summary>
    public class CFormUpload
    {
        /// <summary>
        /// 
        /// </summary>
        private static readonly Encoding encoding = Encoding.UTF8;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="postUrl"></param>
        /// <param name="userAgent"></param>
        /// <param name="postParameters"></param>
        /// <param name="authorization"></param>
        /// <returns></returns>
        public static string MultipartFormDataPost(string postUrl, string userAgent, Dictionary<string, object> postParameters, string authorization = "")
        {
            string formDataBoundary = String.Format("----------{0:N}", Guid.NewGuid());
            string contentType = "multipart/form-data; boundary=" + formDataBoundary;

            byte[] formData = GetMultipartFormData(postParameters, formDataBoundary);

            HttpWebResponse webResponse = PostForm(postUrl, userAgent, contentType, formData, authorization);

            StreamReader responseReader = new StreamReader(webResponse.GetResponseStream());
            string fullResponse = responseReader.ReadToEnd();
            webResponse.Close();
            return fullResponse;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="postUrl"></param>
        /// <param name="userAgent"></param>
        /// <param name="contentType"></param>
        /// <param name="formData"></param>
        /// <param name="authorization"></param>
        /// <returns></returns>
        /// <exception cref="NullReferenceException"></exception>
        private static HttpWebResponse PostForm(string postUrl, string userAgent, string contentType, byte[] formData, string authorization = "")
        {
            HttpWebRequest request = WebRequest.Create(postUrl) as HttpWebRequest;

            if (request == null)
            {
                throw new NullReferenceException("request is not a http request");
            }

            // Set up the request properties.
            if (false == string.IsNullOrEmpty(authorization))
            {
                request.Headers.Add("Authorization", "Bearer " + authorization);
            }
            request.Method = "POST";
            request.ContentType = contentType;
            request.UserAgent = userAgent;
            request.CookieContainer = new CookieContainer();
            request.ContentLength = formData.Length;

            using (Stream requestStream = request.GetRequestStream())
            {
                requestStream.Write(formData, 0, formData.Length);
                requestStream.Close();
            }

            var result = request.GetResponse() as HttpWebResponse;
            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="postParameters"></param>
        /// <param name="boundary"></param>
        /// <returns></returns>
        private static byte[] GetMultipartFormData(Dictionary<string, object> postParameters, string boundary)
        {
            Stream formDataStream = new System.IO.MemoryStream();
            bool needsCLRF = false;

            foreach (var param in postParameters)
            {
                if (needsCLRF)
                    formDataStream.Write(encoding.GetBytes("\r\n"), 0, encoding.GetByteCount("\r\n"));

                needsCLRF = true;

                if (param.Value is FileParameter)
                {
                    FileParameter fileToUpload = (FileParameter)param.Value;

                    string header = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"; filename=\"{2}\"\r\nContent-Type: {3}\r\n\r\n",
                        boundary,
                        fileToUpload.Name,
                        fileToUpload.FileName ?? param.Key,
                        fileToUpload.ContentType ?? "application/octet-stream");

                    formDataStream.Write(encoding.GetBytes(header), 0, encoding.GetByteCount(header));

                    formDataStream.Write(fileToUpload.File, 0, fileToUpload.File.Length);
                }
                else
                {
                    string postData = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"\r\n\r\n{2}",
                        boundary,
                        param.Key,
                        param.Value);
                    formDataStream.Write(encoding.GetBytes(postData), 0, encoding.GetByteCount(postData));
                }
            }

            string footer = "\r\n--" + boundary + "--\r\n";
            formDataStream.Write(encoding.GetBytes(footer), 0, encoding.GetByteCount(footer));

            formDataStream.Position = 0;
            byte[] formData = new byte[formDataStream.Length];
            formDataStream.Read(formData, 0, formData.Length);
            formDataStream.Close();

            return formData;
        }

        /// <summary>
        /// 附件参数
        /// </summary>
        public class FileParameter
        {
            /// <summary>
            /// 
            /// </summary>
            public string Name { get; set; }

            /// <summary>
            /// 
            /// </summary>
            public byte[] File { get; set; }

            /// <summary>
            /// 
            /// </summary>
            public string FileName { get; set; }

            /// <summary>
            /// 
            /// </summary>
            public string ContentType { get; set; }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="name"></param>
            /// <param name="file"></param>
            public FileParameter(string name, byte[] file) : this(name, file, null) { }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="name"></param>
            /// <param name="file"></param>
            /// <param name="filename"></param>
            public FileParameter(string name, byte[] file, string filename) : this(name, file, filename, null) { }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="name"></param>
            /// <param name="file"></param>
            /// <param name="filename"></param>
            /// <param name="contenttype"></param>
            public FileParameter(string name, byte[] file, string filename, string contenttype)
            {
                Name = name;
                File = file;
                FileName = filename;
                ContentType = contenttype;
            }
        }
    }
}
