using System;
using System.Collections.Generic;

using System.Text;
using COSXML.Common;
using System.IO;
using COSXML.Utils;
using COSXML.CosException;
using COSXML.Auth;
using COSXML.Log;
using COSXML.Network;

namespace COSXML.Model.Object
{
    /// <summary>
    /// 使用者用表单的形式将文件(Object)上传至指定 Bucket 中.
    /// <see href="https://cloud.tencent.com/document/product/436/14690"/>
    /// </summary>
    public sealed class PostObjectRequest : ObjectRequest
    {

        /// <summary>
        /// 表单字段
        /// <see href="FormStruct"/>
        /// </summary>
        private FormStruct formStruct;

        private PostObjectRequest(string bucket, string key)
            : base(bucket, "/")
        {
            this.method = CosRequestMethod.POST;
            formStruct = new FormStruct();
            formStruct.key = key;
            this.SetHeader(CosRequestHeaderKey.CONTENT_TYPE, "multipart/form-data; boundary=" + MultipartRequestBody.BOUNDARY);
            this.needMD5 = false;
        }

        /// <summary>
        /// 上传文件
        /// </summary>
        /// <param name="bucket"></param>
        /// <param name="key"></param>
        /// <param name="srcPath"></param>
        public PostObjectRequest(string bucket, string key, string srcPath)
            : this(bucket, key, srcPath, -1L, -1L)
        {
        }

        /// <summary>
        /// 上传文件的指定部分
        /// </summary>
        /// <param name="bucket"></param>
        /// <param name="key"></param>
        /// <param name="srcPath"></param>
        /// <param name="fileOffset">指定文件内容的起始位置</param>
        /// <param name="sendContentLength">指定文件内容的大小</param>
        public PostObjectRequest(string bucket, string key, string srcPath, long fileOffset, long sendContentLength)
            : this(bucket, key)
        {
            formStruct.srcPath = srcPath;
            formStruct.fileOffset = fileOffset < 0 ? 0 : fileOffset;
            formStruct.contentLength = sendContentLength < 0L ? -1L : sendContentLength;
        }

        /// <summary>
        /// 上传data数据
        /// </summary>
        /// <param name="bucket"></param>
        /// <param name="key"></param>
        /// <param name="data"></param>
        public PostObjectRequest(string bucket, string key, byte[] data)
            : this(bucket, key)
        {
            formStruct.data = data;
        }

        /// <summary>
        /// 设置进度回调
        /// </summary>
        /// <param name="progressCallback"></param>
        public void SetCosProgressCallback(COSXML.Callback.OnProgressCallback progressCallback)
        {
            formStruct.progressCallback = progressCallback;
        }

        /// <summary>
        /// 定义 Object 的 acl 属性。有效值:private,public-read-write,public-read;默认值:private
        /// <see href="Common.CosACL"/>
        /// </summary>
        /// <param name="cosACL"></param>
        public void SetCosACL(CosACL cosACL)
        {
            SetCosACL(EnumUtils.GetValue(cosACL));
        }

        /// <summary>
        /// 定义 Object 的 acl 属性。有效值:private,public-read-write,public-read;默认值:private
        /// <see href="Common.CosACL"/>
        /// </summary>
        /// <param name="cosACL"></param>
        public void SetCosACL(string cosACL)
        {

            if (cosACL != null)
            {
                formStruct.acl = cosACL;
            }
        }

        /// <summary>
        /// 设置对象的 cacheControl
        /// </summary>
        /// <param name="cacheControl"></param>
        public void SetCacheControl(string cacheControl)
        {
            SetHeader("Cache-Control", cacheControl);
        }

        /// <summary>
        /// 设置对象的contentType
        /// </summary>
        /// <param name="contentType"></param>
        public void SetContentType(string contentType)
        {
            SetHeader("Content-Type", contentType);
        }

        /// <summary>
        /// 设置对象的contentDisposition
        /// </summary>
        /// <param name="contentDisposition"></param>
        public void SetContentDisposition(string contentDisposition)
        {
            SetHeader("Content-Disposition", contentDisposition);
        }

        /// <summary>
        /// 设置对象的contentEncoding
        /// </summary>
        /// <param name="contentEncoding"></param>
        public void SetContentEncoding(string contentEncoding)
        {
            SetHeader("Content-Encoding", contentEncoding);
        }

        /// <summary>
        /// 设置对象 Expire
        /// </summary>
        /// <param name="expires"></param>
        public void SetExpires(string expires)
        {
            SetHeader("Expires", expires);
        }

        /// <summary>
        /// 设置对象header属性
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void SetHeader(string key, string value)
        {

            try
            {
                formStruct.headers.Add(key, value);
            }
            catch (ArgumentException)
            {
                formStruct.headers[key] = value;
            }
        }

        /// <summary>
        /// 设置对象自定义的header属性
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void SetCustomerHeader(string key, string value)
        {

            try
            {
                formStruct.customHeaders.Add(key, value);
            }
            catch (ArgumentException)
            {
                formStruct.customHeaders[key] = value;
            }
        }

        /// <summary>
        /// 设置对象的存储类型
        /// <see href="Common.CosStorageClass"/>
        /// </summary>
        /// <param name="cosStorageClass"></param>
        public void SetCosStorageClass(string cosStorageClass)
        {
            formStruct.xCosStorageClass = cosStorageClass;
        }

        /// <summary>
        /// 最大上传速度,单位是 bit/s
        /// </summary>
        /// <param name="rate"></param>
        public void LimitTraffic(long rate)
        {
            formStruct.xCOSTrafficLimit = rate.ToString();
        }

        /// <summary>
        /// 若设置优先生效,返回 303 并提供 Location 头部,
        /// 会在 URL 尾部加上 bucket={bucket}&lt;key={key}&gt;etag={%22etag%22} 参数。
        /// </summary>
        /// <param name="redirectHost"></param>
        public void SetSuccessActionRedirect(string redirectHost)
        {
            formStruct.successActionRedirect = redirectHost;
        }

        /// <summary>
        /// successHttpCode can be 200, 201, 204, default value 204
        /// 若填写 success_action_redirect 则会略此设置。
        /// </summary>
        /// <param name="successHttpCode"></param>
        public void SetSuccessActionStatus(int successHttpCode)
        {
            formStruct.successActionStatus = successHttpCode.ToString();
        }

        /// <summary>
        /// 用于做请求检查,如果请求的内容和 Policy 指定的条件不符,返回 403 AccessDenied。
        /// <see href="Policy"/>
        /// </summary>
        /// <param name="policy"></param>
        public void SetPolicy(Policy policy)
        {
            formStruct.policy = policy;
        }

        public override void CheckParameters()
        {
            formStruct.CheckParameter();
            base.CheckParameters();
        }

        public override CosXmlSignSourceProvider GetSignSourceProvider()
        {
            var signSourceProvider = base.GetSignSourceProvider();
            
            if (signSourceProvider != null)
            {
                signSourceProvider.RemoveHeaderKey("content-type");
                signSourceProvider.RemoveHeaderKey("content-length");
                signSourceProvider.RemoveHeaderKey("content-md5");
                signSourceProvider.onGetSign = delegate (Request request, string sign)
                {
                    //添加参数 sign
                    ((MultipartRequestBody)request.Body).AddParameter("Signature", sign);
                };
            }

            return signSourceProvider;
        }

        public override RequestBody GetRequestBody()
        {
            MultipartRequestBody requestBody = new MultipartRequestBody();

            requestBody.AddParamters(formStruct.GetFormParameters());

            if (formStruct.data != null)
            {
                requestBody.AddData(formStruct.data, "file", "tmp");
            }
            else if (formStruct.srcPath != null)
            {
                FileInfo fileInfo = new FileInfo(this.formStruct.srcPath);

                string fileName = fileInfo.Name;

                if (formStruct.contentLength == -1L || formStruct.contentLength + formStruct.fileOffset > fileInfo.Length)
                {
                    formStruct.contentLength = fileInfo.Length - formStruct.fileOffset;
                }

                requestBody.AddData(formStruct.srcPath, formStruct.fileOffset, formStruct.contentLength, "file", fileName);
            }

            requestBody.ProgressCallback = formStruct.progressCallback;

            return requestBody;
        }

        private class FormStruct
        {
            /// <summary>
            /// 对象的ACL
            /// </summary>
            public string acl;

            /// <summary>
            /// 对象的header元数据
            /// </summary>
            public Dictionary<string, string> headers;

            /// <summary>
            /// 上传后的文件名,使用 ${filename} 则会进行替换。
            /// 例如a/b/${filename},上传文件 a1.txt,那么最终的上传路径就是 a/b/a1.txt
            /// </summary>
            public string key;

            /// <summary>
            /// 若设置优先生效,返回 303 并提供 Location 头部
            /// </summary>
            public string successActionRedirect;

            /// <summary>
            /// 可选 200,201,204 默认返回 204。若填写 success_action_redirect 则会略此设置。
            /// </summary>
            public string successActionStatus;

            /// <summary>
            /// 对象的自定义元数据
            /// </summary>
            public Dictionary<string, string> customHeaders;

            /// <summary>
            /// 对象存储类型
            /// </summary>
            public string xCosStorageClass;

            /// <summary>
            /// 速度限制
            /// </summary>
            public string xCOSTrafficLimit;

            /// <summary>
            /// 请求检查策略
            /// <see href="Policy"/>
            /// </summary>
            public Policy policy;

            /// <summary>
            /// 上传文件的本地路径
            /// </summary>
            public string srcPath;

            /// <summary>
            /// 上传文件指定起始位置
            /// </summary>
            public long fileOffset = 0L;

            /// <summary>
            /// 上传文件指定内容大小
            /// </summary>
            public long contentLength = -1L;

            /// <summary>
            /// 上传data数据
            /// </summary>
            public byte[] data;

            /// <summary>
            /// 上传回调
            /// </summary>
            public COSXML.Callback.OnProgressCallback progressCallback;


            public FormStruct()
            {
                headers = new Dictionary<string, string>();
                customHeaders = new Dictionary<string, string>();
            }

            public Dictionary<string, string> GetFormParameters()
            {
                Dictionary<string, string> formParameters = new Dictionary<string, string>();

                if (acl != null)
                {
                    formParameters.Add("Acl", acl);
                }

                foreach (KeyValuePair<string, string> pair in headers)
                {
                    formParameters.Add(pair.Key, pair.Value);
                }

                formParameters.Add("key", key);

                if (successActionRedirect != null)
                {
                    formParameters.Add("success_action_redirect", successActionRedirect);
                }

                if (successActionStatus != null)
                {
                    formParameters.Add("success_action_status", successActionStatus);
                }

                foreach (KeyValuePair<string, string> pair in customHeaders)
                {
                    formParameters.Add(pair.Key, pair.Value);
                }

                if (xCosStorageClass != null)
                {
                    formParameters.Add("x-cos-storage-class", xCosStorageClass);
                }

                if (xCOSTrafficLimit != null)
                {
                    formParameters.Add(CosRequestHeaderKey.X_COS_TRAFFIC_LIMIT, xCOSTrafficLimit);
                }

                if (policy != null)
                {
                    formParameters.Add("policy", DigestUtils.GetBase64(policy.Content(), Encoding.UTF8));
                }

                return formParameters;
            }

            public void CheckParameter()
            {

                if (String.IsNullOrEmpty(key))
                {
                    throw new CosClientException((int)CosClientError.InvalidArgument, "FormStruct.key(null or empty) is invalid");
                }

                if (srcPath == null && data == null)
                {
                    throw new CosClientException((int)CosClientError.InvalidArgument, "data source = null");
                }

                if (srcPath != null)
                {

                    if (!File.Exists(srcPath))
                    {
                        throw new CosClientException((int)CosClientError.InvalidArgument, "srcPath not exist");
                    }
                }
            }
        }

        public class Policy
        {
            /// <summary>
            /// 过期时间
            /// </summary>
            private string expiration;

            /// <summary>
            /// 检查条件
            /// </summary>
            private StringBuilder conditions = new StringBuilder();

            public void SetExpiration(long endTimeMills)
            {
                this.expiration = TimeUtils_QCloud.GetFormatTime("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", endTimeMills, TimeUnit.Milliseconds);
            }

            public void AddConditions(string key, string value, bool isPrefixMatch)
            {

                if (conditions.Length > 0)
                {
                    conditions.Append(',');
                }

                if (isPrefixMatch)
                {
                    conditions.Append('[')
                        .Append("\"starts-with\"")
                        .Append(",\"")
                        .Append(key)
                        .Append("\",\"")
                        .Append(value)
                        .Append("\"]");
                }
                else
                {
                    conditions.Append("{\"")
                        .Append(key)
                        .Append("\":\"")
                        .Append(value)
                        .Append("\"}");
                }
            }

            public void AddContentConditions(int start, int end)
            {
                if (conditions.Length > 0)
                {
                    conditions.Append(',');
                }
                
                conditions.Append('[')
                    .Append("\"content-length-range\"")
                    .Append(',')
                    .Append(start)
                    .Append(',')
                    .Append(end)
                    .Append(']');
            }

            public string Content()
            {
                StringBuilder content = new StringBuilder();

                content.Append('{');

                if (expiration != null)
                {
                    content.Append(String.Format("\"expiration\":\"{0}\"", expiration));
                }

                if (conditions.Length > 0)
                {
                    if (expiration != null) 
                    {
                        content.Append(",");
                    }

                    content.Append(String.Format("\"conditions\":[{0}]", conditions));
                }

                content.Append('}');

                return content.ToString();
            }
        }
    }
}