using System;
using System.Collections.Generic;

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

namespace COSXML.Model
{
    public abstract class CosRequest
    {
        private static string TAG = typeof(CosRequest).FullName;

        protected Request realRequest;

        /// <summary>
        /// isHttps = true, https 请求; isHttps = false, http 请求; default isHttps = false.
        /// </summary>
        protected bool? isHttps = null;

        /// <summary>
        /// cos api 请求对应的 http method.
        /// </summary>
        protected string method = CosRequestMethod.GET;

        /// <summary>
        /// http 请求url中 path 部分.
        /// </summary>
        protected string path;

        /// <summary>
        /// http 请求url中 query 部分.
        /// </summary>
        protected Dictionary<string, string> queryParameters = new Dictionary<string, string>();

        /// <summary>
        /// http 请求 header 部分.
        /// </summary>
        protected Dictionary<string, string> headers = new Dictionary<string, string>();

        /// <summary>
        /// cos 服务的 appid.
        /// </summary>
        protected string appid;

        /// <summary>
        /// cos 服务签名的签名源部分.
        /// </summary>
        protected CosXmlSignSourceProvider cosXmlSignSourceProvider = new CosXmlSignSourceProvider();

        /// <summary>
        /// needMD5 = true, 请求中带上 Content-Md5; needMd5 = false, 请求中不带 Content-Md5; defalut needMd5 = false.
        /// </summary>
        protected bool needMD5 = true;

        /// <summary>
        /// 请求预签名URL
        /// </summary>
        protected string requestUrlWithSign = null;

        public CosXmlConfig serviceConfig { get; set; }

        /// <summary>
        /// http or https for cos request.
        /// </summary>
        public bool? IsHttps
        {
            get
            {
                return isHttps;
            }
            
            set { isHttps = value; }
        }

        /// <summary>
        /// http method
        /// </summary>
        public string Method
        {
            get
            {
                return method;
            }
            set { this.method = value; }
        }

        /// <summary>
        /// path of http url.
        /// </summary>
        public string RequestPath
        {
            get
            {
                return path;
            }
            
            private set { }
        }

        /// <summary>
        /// query of http url.
        /// </summary>
        /// <returns></returns>
        public virtual Dictionary<string, string> GetRequestParamters()
        {

            return queryParameters;
        }

        /// <summary>
        /// http request header
        /// </summary>
        /// <returns></returns>
        public virtual Dictionary<string, string> GetRequestHeaders()
        {

            return headers;
        }

        /// <summary>
        /// add query parameter for cos request, and cover the current value if it exists with the key.
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void SetQueryParameter(string key, string value)
        {
            SetQueryParameter(key, value, true);
        }

        /// <summary>
        /// url 部分都统一 url encode
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="isNeedUrlEncode"></param>
        public void SetQueryParameter(string key, string value, bool isNeedUrlEncode)
        {

            try
            {

                if (value == null)
                {
                    value = "";
                }

                if (isNeedUrlEncode)
                {
                    value = URLEncodeUtils.Encode(value);
                }

                queryParameters.Add(key, value);
            }
            catch (ArgumentException)
            {
                // cover the current value
                // cover the current value
                queryParameters[key] = value;
            }
        }

        /// <summary>
        /// add header for cos request, and cover the current value, if it exists with the key.
        /// </summary>
        /// <param name="key"> header: key </param>
        /// <param name="value"> header: value</param>
        public void SetRequestHeader(string key, string value)
        {
            SetRequestHeader(key, value, false);
        }

        /// <summary>
        /// add headers for cos request, and cover the current value, if it exists with the key.
        /// </summary>
        /// <param name="headers"></param>
        public void SetRequestHeaders(Dictionary<string, string> headers)
        {

            foreach (KeyValuePair<string, string> entry in headers)
            {
                SetRequestHeader(entry.Key, entry.Value);
            }
        }

        /// <summary>
        /// header 默认不 encode
        /// </summary>
        /// <param name="key">不能为null 即不包含空格,即 位于(\u0020, \u007F),超过这个范围,urlencode</param>
        /// <param name="value">可以为null,为空,且位于(\u001f,\u007F) 和 '\t',超过这个范围,urlencode</param>
        /// <param name="isNeedUrlEncode"></param>
        public void SetRequestHeader(string key, string value, bool isNeedUrlEncode)
        {

            try
            {

                if (value == null)
                {
                    value = "";
                }

                if (isNeedUrlEncode)
                {
                    value = URLEncodeUtils.Encode(value);
                }

                headers.Add(key, value);
            }
            catch (ArgumentException)
            {
                // cover the current value
                // cover the current value
                headers[key] = value;
            }
        }

        /// <summary>
        /// set appid for cos.
        /// </summary>
        public string APPID
        {
            get
            {
                return this.appid;
            }
            set { this.appid = value; }
        }

        /// <summary>
        /// 是否带上content-md5
        /// </summary>
        public bool IsNeedMD5
        {
            get
            {
                return needMD5;
            }
            set { needMD5 = value; }
        }

        /// <summary>
        /// return the host for cos request
        /// </summary>
        /// <returns>host(string)</returns>
        public abstract string GetHost();

        /// <summary>
        /// return the body for cos request. such as upload file.
        /// </summary>
        /// <returns> <see href="COSXML.Network.RequestBody"/></returns>
        public abstract RequestBody GetRequestBody();

        /// <summary>
        /// 返回 xml 格式的 requestBody
        /// </summary>
        /// <param name="d"></param>
        /// <returns></returns>
        protected Network.RequestBody GetXmlRequestBody(object d)
        {
            string content = Transfer.XmlBuilder.Serialize(d);
            byte[] data = Encoding.UTF8.GetBytes(content);
            ByteRequestBody body = new ByteRequestBody(data);
            return body;
        }


        /// <summary>    
        ///   check parameter for cos.
        /// </summary>
        /// <exception cref="COSXML.CosException.CosClientException"></exception>
        public abstract void CheckParameters();

        /// <summary>
        /// 设置签名的有效期: [signStartTimeSecond, signStartTimeSecond + durationSecond]
        /// </summary>
        /// <param name="signStartTimeSecond"></param>
        /// <param name="durationSecond"></param>
        public virtual void SetSign(long signStartTimeSecond, long durationSecond)
        {
            cosXmlSignSourceProvider.SetSignTime(signStartTimeSecond, durationSecond);
        }

        /// <summary>
        /// 计算签名时,带上头部header 和查询参数 query验证.
        /// 设置签名的有效期: [signStartTimeSecond, signStartTimeSecond + durationSecond]
        /// </summary>
        /// <param name="signStartTimeSecond"></param>
        /// <param name="durationSecond"></param>
        /// <param name="headerKeys"></param>
        /// <param name="queryParameterKeys"></param>
        public virtual void SetSign(long signStartTimeSecond, long durationSecond, List<string> headerKeys, List<string> queryParameterKeys)
        {
            cosXmlSignSourceProvider.SetSignTime(signStartTimeSecond, durationSecond);
            cosXmlSignSourceProvider.AddHeaderKeys(headerKeys);
            cosXmlSignSourceProvider.AddParameterKeys(queryParameterKeys);
        }

        /// <summary>
        /// 直接设置签名串.
        /// </summary>
        /// <param name="sign"></param>
        public virtual void SetSign(string sign)
        {
            SetRequestHeader(CosRequestHeaderKey.AUTHORIZAIION, sign);
        }

        /// <summary>
        /// 返回签名数据源
        /// </summary>
        /// <returns></returns>
        public virtual CosXmlSignSourceProvider GetSignSourceProvider()
        {
            // 默认签署的头部跟参数
            cosXmlSignSourceProvider.AddHeaderKeys(new List<string>() 
            {
                "cache-control",
                "content-disposition",
                "content-encoding",
                "content-length",
                "content-md5",
                "content-type",
                "expect",
                "expires",
                "host",
                "if-match",
                "if-modified-since",
                "if-none-match",
                "if-unmodified-since",
                "origin",
                "range",
                "response-cache-control",
                "response-content-disposition",
                "response-content-encoding",
                "response-content-language",
                "response-content-type",
                "response-expires",
                "transfer-encoding",
                "versionid"
            });

            foreach (KeyValuePair<string, string> pair in headers)
            {
                if (pair.Key.StartsWith("x-cos-"))
                {
                    cosXmlSignSourceProvider.AddHeaderKey(pair.Key.ToLower());
                }
            }

            foreach (KeyValuePair<string, string> pair in queryParameters)
            {
                cosXmlSignSourceProvider.AddParameterKey(pair.Key.ToLower());
            }

            return cosXmlSignSourceProvider;
        }

        /// <summary>
        /// 设置预签名URL
        /// </summary>
        public string RequestURLWithSign
        {
            get
            {
                return requestUrlWithSign;
            }
            set { requestUrlWithSign = value; }
        }

        public void BindRequest(Request request)
        {
            this.realRequest = request;
        }

        public void Cancel()
        {

            if (realRequest != null)
            {
                realRequest.Cancel();
            }
        }
    }
}