
using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;

namespace Top.Api.Util
{
    /// <summary>
    /// 安全工具类
    /// </summary>
    public abstract class SecurityUtil
    {
        private static readonly char[] CA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".ToCharArray();
        private static readonly int[] IA = InitIA();
        private static readonly int KeySize = 128;
        private static readonly int BlockSize = 128;
        private static readonly byte[] IvBytes = Encoding.UTF8.GetBytes("0102030405060708");//初始向量

        private static int[] InitIA()
        {
            int len = 256;
            int[] a = new int[len];
            for (int i = 0; i < len; i++)
            {
                a[i] = -1;
            }

            for (int i = 0, iS = CA.Length; i < iS; i++)
            {
                a[CA[i]] = i;
            }
            a['='] = 0;
            return a;
        }

        /// <summary>
        /// 判断是否base64值
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static bool IsBase64Value(string str)
        {
            // Check special case
            int sLen = str != null ? str.Length : 0;
            if (sLen == 0)
                return false;

            // Count illegal characters (including '\r', '\n') to know what size the returned array will be,
            // so we don't have to reallocate & copy it later.
            int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...)
            for (int i = 0; i < sLen; i++)  // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out.
            {
                char currentChar = str[i];
                if (currentChar >= IA.Length) {
                    return false;
                }

                if (IA[currentChar] < 0) {
                    sepCnt++;
                }

            }  


            // Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045.
            if ((sLen - sepCnt) % 4 != 0)
            {
                return false;
            }
            return true;
        }

        /// <summary>
        /// 生成滑动窗口
        /// </summary>
        /// <param name="input">数据</param>
        /// <param name="slideSize">分词大小</param>
        /// <returns>分词元素</returns>
        public static List<string> GetSlideWindows(string input, int slideSize)
        {
            List<string> windows = new List<string>();
            int startIndex = 0;
            int endIndex = 0;
            int currentWindowSize = 0;
            string currentWindow = null;

            while (endIndex < input.Length || currentWindowSize > slideSize)
            {
                bool startsWithLetterOrDigit;
                if (currentWindow == null)
                {
                    startsWithLetterOrDigit = false;
                }
                else
                {
                    startsWithLetterOrDigit = IsLetterOrDigit(currentWindow[0]);
                }

                if (endIndex == input.Length && !startsWithLetterOrDigit)
                {
                    break;
                }

                if (currentWindowSize == slideSize && !startsWithLetterOrDigit && IsLetterOrDigit(input[endIndex]))
                {
                    endIndex++;
                    currentWindow = input.Substring(startIndex, endIndex - startIndex);
                    currentWindowSize = 5;

                }
                else
                {
                    if (endIndex != 0)
                    {
                        if (startsWithLetterOrDigit)
                        {
                            currentWindowSize -= 1;
                        }
                        else
                        {
                            currentWindowSize -= 2;
                        }
                        startIndex++;
                    }

                    while (currentWindowSize < slideSize && endIndex < input.Length)
                    {
                        char currentChar = input[endIndex];
                        if (IsLetterOrDigit(currentChar))
                        {
                            currentWindowSize += 1;
                        }
                        else
                        {
                            currentWindowSize += 2;
                        }
                        endIndex++;
                    }
                    currentWindow = input.Substring(startIndex, endIndex - startIndex);

                }
                windows.Add(currentWindow);
            }
            return windows;
        }

        /// <summary>
        /// 判断是否小写字母
        /// </summary>
        /// <param name="x"></param>
        /// <returns></returns>
        private static bool IsLetterOrDigit(char x)
        {
            if (0 <= x && x <= 127)
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// 压缩
        /// </summary>
        /// <param name="input"></param>
        /// <param name="toLength"></param>
        /// <returns></returns>
        private static byte[] Compress(byte[] input, int toLength)
        {
            if (toLength < 0)
            {
                return null;
            }
            byte[] output = new byte[toLength];
            for (int i = 0; i < output.Length; i++)
            {
                output[i] = 0;
            }

            for (int i = 0; i < input.Length; i++)
            {
                int index_output = i % toLength;
                output[index_output] ^= input[i];
            }

            return output;
        }

        /// <summary>
        /// Base64加密
        /// </summary>
        /// <param name="source">待加密的明文</param>
        /// <param name="encode">编码方式</param>
        /// <returns></returns>
        public static string EncodeBase64(string source, Encoding encode)
        {
            byte[] bytes = encode.GetBytes(source);
            return Convert.ToBase64String(bytes);
        }

        /// <summary>
        /// Base64加密
        /// </summary>
        /// <param name="source">待加密的明文</param>
        /// <returns></returns>
        /// 
        /// <seealso cref="SecurityUtil.EncodeBase64(string,Encoding)">  
        /// 参看SecurityUtil.EncodeBase64(string,Encoding)方法的说明 </seealso>  
        public static string EncodeBase64(string source)
        {
            return EncodeBase64(source, Encoding.UTF8);
        }

        /// <summary>
        /// AES加密 
        /// </summary>
        /// <param name="context">待加密的内容</param>
        /// <param name="keyBytes">加密密钥</param>
        /// <returns></returns>
        public static string AESEncrypt(string context, byte[] keyBytes)
        {
            RijndaelManaged rijndaelCipher = new RijndaelManaged();

            rijndaelCipher.Mode = CipherMode.CBC;
            rijndaelCipher.Padding = PaddingMode.PKCS7;
            rijndaelCipher.KeySize = KeySize;
            rijndaelCipher.BlockSize = KeySize;


            // 加密密钥
            rijndaelCipher.Key = keyBytes;
            rijndaelCipher.IV = IvBytes;

            ICryptoTransform transform = rijndaelCipher.CreateEncryptor();

            byte[] plainText = Encoding.UTF8.GetBytes(context);
            byte[] cipherBytes = transform.TransformFinalBlock(plainText, 0, plainText.Length);
            return Convert.ToBase64String(cipherBytes);
        }

        /// <summary>
        /// AES解密
        /// </summary>
        /// <param name="context"></param>
        /// <param name="keyBytes"></param>
        /// <returns></returns>
        public static string AESDecrypt(string context, byte[] keyBytes)
        {
            RijndaelManaged rijndaelCipher = new RijndaelManaged();

            rijndaelCipher.Mode = CipherMode.CBC;
            rijndaelCipher.Padding = PaddingMode.PKCS7;
            rijndaelCipher.KeySize = KeySize;
            rijndaelCipher.BlockSize = BlockSize;

            byte[] encryptedData = Convert.FromBase64String(context);
            rijndaelCipher.Key = keyBytes;
            rijndaelCipher.IV = IvBytes;

            ICryptoTransform transform = rijndaelCipher.CreateDecryptor();

            byte[] plainText = transform.TransformFinalBlock(encryptedData, 0, encryptedData.Length);
            return Encoding.UTF8.GetString(plainText);
        }

        public static byte[] HmacMD5Encrypt(String encryptText, byte[] encryptKey)
        {
            HMACMD5 hmac = new HMACMD5(encryptKey);
            byte[] bytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(encryptText));
            return bytes;
        }


        /// <summary>
        /// 生成BASE64(H_MAC)
        /// </summary>
        /// <param name="encryptText">被签名的字符串</param>
        /// <param name="encryptKey">秘钥</param>
        /// <returns></returns>
        public static string HmacMD5EncryptToBase64(string encryptText, byte[] encryptKey)
        {
            return Convert.ToBase64String(HmacMD5Encrypt(encryptText, encryptKey));
        }

        /// <summary>
        /// 生成BASE64(H_MAC),压缩H_MAC值
        /// </summary>
        /// <param name="encryptText"></param>
        /// <param name="encryptKey"></param>
        /// <param name="compressLen"></param>
        /// <returns></returns>
        public static String HmacMD5EncryptToBase64(string encryptText, byte[] encryptKey, int compressLen)
        {
            return Convert.ToBase64String(Compress(HmacMD5Encrypt(encryptText, encryptKey), compressLen));
        }

    }
}
