using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using Newtonsoft.Json;
using System.Threading;
using UtilsHelper;

namespace HWOcrClientToken
{
    public class OcrClientToken
    {
        private readonly string uName = "";
        private readonly string psWord = "";
        private readonly string doName = "";
        private readonly string regName = "";
        string token = null;
        private readonly int _RETRY_TIMES = 3;
        private int _RETRY_OCR_TIMES = 2;
        private readonly int _POLLING_INTERVAL = 2000;

        public OcrClientToken()
        {

        }

        public OcrClientToken(string userName, string passWord, string domainName, string regionName)
        {
            if (userName == null || passWord == null || domainName == null || regionName == null)
            {
                throw new Exception("The parameter for the HWOcrClientToken constructor cannot be empty");
            }
            uName = userName;
            psWord = passWord;
            doName = domainName;
            regName = regionName;
        }


        #region  get token
        /// <summary>
        /// get the token
        /// </summary>
        /// <returns>token</returns>
        //get the token for the ocr user from IAM server
        public string GetHttpToken()
        {
            if (token != null)
            {
                return token;
            }
            int retry_times = 0;

            //create the payload
            Dictionary<string, Object> domain = new Dictionary<string, Object>();
            domain.Add("name", doName);
            Dictionary<string, Object> user = new Dictionary<string, Object>();
            user.Add("name", uName);
            user.Add("password", psWord);
            user.Add("domain", domain);
            Dictionary<string, Object> password = new Dictionary<string, Object>();
            password.Add("user", user);

            List<string> methods = new List<string>();
            methods.Add("password");
            Dictionary<string, Object> identity = new Dictionary<string, Object>();
            identity.Add("methods", methods);
            identity.Add("password", password);

            Dictionary<string, string> project = new Dictionary<string, string>();
            project.Add("name", regName);
            Dictionary<string, Object> scope = new Dictionary<string, Object>();
            scope.Add("project", project);

            Dictionary<string, Object> auth = new Dictionary<string, Object>();
            auth.Add("identity", identity);
            auth.Add("scope", scope);

            Dictionary<string, Object> payload = new Dictionary<string, Object>();
            payload.Add("auth", auth);

            //make the payload to json format
            string payjson = JsonConvert.SerializeObject(payload);
            byte[] data = Encoding.UTF8.GetBytes(payjson);

            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

            // set the request
            string URL = string.Format("https://iam.{0}.myhuaweicloud.com/v3/auth/tokens", regName);
            
            try
            {
                while (true)
                {
                    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(URL);
                    request.Method = "post";
                    request.ContentType = "application/json";
                    request.Timeout = 15000;
                    request.ContentLength = data.Length;
                    request.GetRequestStream().Write(data, 0, data.Length);
                    request.GetRequestStream().Close();

                    HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                    if ("Created" == response.StatusCode.ToString() || "201" == response.StatusCode.ToString())
                    {
                        Console.WriteLine("Get token successs!");
                        token = response.Headers["X-Subject-Token"];
                        return token;
                        Console.WriteLine("retry_times = " + retry_times);
                    }
                    else
                    {
                        if (retry_times <= _RETRY_TIMES)
                        {
                            retry_times++;
                            Console.WriteLine("Reget token.");
                            Thread.Sleep(_POLLING_INTERVAL);
                            token = null;
                            continue;
                        }
                        else
                        {
                            Console.WriteLine("Get token failed!");
                            Console.WriteLine(response.StatusCode);
                            token = null;
                            return null;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Get token failed: " + ex);
                return null;
            }
        }
        #endregion

        #region refresh token
        /// <summary>
        /// refresh the attribute token
        /// </summary>
        public void RefreshToken()
        {
            Console.WriteLine("token expired, refresh.");
            GetHttpToken();
        }
        #endregion

        #region ocr request to recognise image
        /*
        <summary>
        ocr recognise image
        </summary>
        <param name = "uri" > api </ param >
        <param name="filepath">imagePath</param>
        <param name = "options" > the params of the request</param>
        <returns>the statusCode of request</returns>
        **/
        public string OcrServiceBase64(string api, string imgPath, Dictionary<string, Object> option, ref string body)
        {
            if (api == null || imgPath == null)
            {
                throw new Exception("the parameter for OcrServiceBase64 cannot be empty");
            }

            string token = GetHttpToken();
            if (token != null)
            {
                try
                {
                    //create the request
                    string url = string.Format("https://ocr.{0}.myhuaweicloud.com{1}", regName, api);
                    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
                    request.Method = "post";
                    request.ContentType = "application/json";
                    request.Headers.Add("X-Auth-Token", token);

                    Dictionary<string, Object> payload = new Dictionary<string, Object>();
                    if (imgPath.Contains("http://") || imgPath.Contains("https://"))
                    {
                        payload.Add("url", imgPath);
                    }
                    else
                    {
                        //image base64 encode
                        Utils utils = new Utils();
                        string strbaser64 = utils.ImgToBase64String(imgPath);
                        if (strbaser64 != null)
                        {
                            payload.Add("image", strbaser64);
                        }
                    }

                    if (option != null)
                    {
                        foreach (var item in option)
                        {
                            if (!payload.ContainsKey(item.Key))
                            {
                                payload.Add(item.Key, item.Value);
                            }
                        }
                    }

                    string payjson = JsonConvert.SerializeObject(payload);

                    byte[] data = Encoding.UTF8.GetBytes(payjson);

                    _RETRY_OCR_TIMES--;

                    request.ContentLength = data.Length;
                    request.GetRequestStream().Write(data, 0, data.Length);
                    request.GetRequestStream().Close();

                    //response of the request
                    HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                    using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
                    {
                        body = reader.ReadToEnd();
                    }
                    if ((response.StatusCode.ToString() == "401" || response.StatusCode.ToString() == "Unauthorized") && 
                        response.StatusDescription.Contains("token expired"))
                    {
                        if (_RETRY_OCR_TIMES <= 0)
                        {
                            return "401";
                        }
                        //token expired, refresh token
                        RefreshToken();
                        return OcrServiceBase64(api, imgPath, option, ref body);
                    }
                    else if ((response.StatusCode.ToString() == "403" || response.StatusCode.ToString() == "Forbidden") && 
                        response.StatusDescription.Contains("The authentication token is abnormal"))
                    {
                        if (_RETRY_OCR_TIMES <= 0)
                        {
                            return "403";
                        }
                        //token expired, refresh token
                        RefreshToken();
                        return OcrServiceBase64(api, imgPath, option, ref body);
                    }

                    return response.StatusCode.ToString();
                }
                catch (WebException ex)
                {
                    var mResponse = ex.Response as HttpWebResponse;
                    var responseStream = mResponse.GetResponseStream();
                    if (responseStream != null)
                    {
                        var streamReader = new StreamReader(responseStream, Encoding.GetEncoding("utf-8"));
                        var result = streamReader.ReadToEnd();
                        streamReader.Close();
                        responseStream.Close();

                        Console.WriteLine("result：" + result);
                        body = JsonConvert.SerializeObject(result);
                        return "5002";
                    } else
                    {
                        body = "{\"message\":\"bad request\"}";
                        Console.WriteLine("Exception caught: {0}", ex.ToString());
                        return "5001";
                    }
                }
            }
            body = "{\"message\":\"token is null\"}";
            return "4001";
        }
        #endregion
    }
}
