using System;
using System.Security.Cryptography;

namespace GameFramework
{
    public static class Utility
    {
       /// <summary>
        /// 加密解密相关的实用函数。
        /// </summary>
        public static class Encryption
        {
            internal const int QuickEncryptLength = 220;

            /// <summary>
            /// 将 bytes 使用 code 做异或运算的快速版本。
            /// </summary>
            /// <param name="bytes">原始二进制流。</param>
            /// <param name="code">异或二进制流。</param>
            /// <returns>异或后的二进制流。</returns>
            public static byte[] GetQuickXorBytes(byte[] bytes, byte[] code)
            {
                return GetXorBytes(bytes, 0, QuickEncryptLength, code);
            }

            /// <summary>
            /// 将 bytes 使用 code 做异或运算的快速版本。此方法将复用并改写传入的 bytes 作为返回值，而不额外分配内存空间。
            /// </summary>
            /// <param name="bytes">原始及异或后的二进制流。</param>
            /// <param name="code">异或二进制流。</param>
            public static void GetQuickSelfXorBytes(byte[] bytes, byte[] code)
            {
                GetSelfXorBytes(bytes, 0, QuickEncryptLength, code);
            }

            /// <summary>
            /// 将 bytes 使用 code 做异或运算。
            /// </summary>
            /// <param name="bytes">原始二进制流。</param>
            /// <param name="code">异或二进制流。</param>
            /// <returns>异或后的二进制流。</returns>
            public static byte[] GetXorBytes(byte[] bytes, byte[] code)
            {
                if (bytes == null)
                {
                    return null;
                }

                return GetXorBytes(bytes, 0, bytes.Length, code);
            }

            /// <summary>
            /// 将 bytes 使用 code 做异或运算。此方法将复用并改写传入的 bytes 作为返回值，而不额外分配内存空间。
            /// </summary>
            /// <param name="bytes">原始及异或后的二进制流。</param>
            /// <param name="code">异或二进制流。</param>
            public static void GetSelfXorBytes(byte[] bytes, byte[] code)
            {
                if (bytes == null)
                {
                    return;
                }

                GetSelfXorBytes(bytes, 0, bytes.Length, code);
            }

            /// <summary>
            /// 将 bytes 使用 code 做异或运算。
            /// </summary>
            /// <param name="bytes">原始二进制流。</param>
            /// <param name="startIndex">异或计算的开始位置。</param>
            /// <param name="length">异或计算长度，若小于 0，则计算整个二进制流。</param>
            /// <param name="code">异或二进制流。</param>
            /// <returns>异或后的二进制流。</returns>
            public static byte[] GetXorBytes(byte[] bytes, int startIndex, int length, byte[] code)
            {
                if (bytes == null)
                {
                    return null;
                }

                int bytesLength = bytes.Length;
                byte[] results = new byte[bytesLength];
                Array.Copy(bytes, 0, results, 0, bytesLength);
                if (length < 0)
                {
                    length = bytesLength;
                }
                GetSelfXorBytes(results, startIndex, length, code);
                return results;
            }

            /// <summary>
            /// 将 bytes 使用 code 做异或运算。此方法将复用并改写传入的 bytes 作为返回值，而不额外分配内存空间。
            /// </summary>
            /// <param name="bytes">原始及异或后的二进制流。</param>
            /// <param name="startIndex">异或计算的开始位置。</param>
            /// <param name="length">异或计算长度。</param>
            /// <param name="code">异或二进制流。</param>
            public static void GetSelfXorBytes(byte[] bytes, int startIndex, int length, byte[] code)
            {
                if (bytes == null)
                {
                    return;
                }

                if (code == null)
                {
                    throw new Exception("Code is invalid.");
                }

                int codeLength = code.Length;
                if (codeLength <= 0)
                {
                    throw new Exception("Code length is invalid.");
                }

                if (startIndex < 0 || length < 0 || startIndex + length > bytes.Length)
                {
                    throw new Exception("Start index or length is invalid.");
                }

                int codeIndex = startIndex % codeLength;
                for (int i = startIndex; i < length; i++)
                {
                    bytes[i] ^= code[codeIndex++];
                    codeIndex %= codeLength;
                }
            }
            
            /// <summary>
            /// 获取MD5值
            /// </summary>
            /// <param name="bytes"></param>
            /// <returns></returns>
            public static byte[] MD5(byte[] bytes)
            {
                System.Security.Cryptography.MD5 md5 = new MD5CryptoServiceProvider();
                return md5.ComputeHash(bytes);
            }
            
            /// <summary>
            /// AES加密
            /// </summary>
            /// <param name="data"></param>
            /// <param name="key"></param>
            /// <param name="iVector"></param>
            /// <returns></returns>
            public static byte[] Encrypt(byte[] data, byte[] key, byte[] iVector)
            {
                RijndaelManaged aes = new RijndaelManaged();
                aes.Key = key;
                aes.IV = iVector;
                aes.Mode = CipherMode.CBC;
                aes.Padding = PaddingMode.Zeros;

                ICryptoTransform cTransform = aes.CreateEncryptor();
                return cTransform.TransformFinalBlock(data, 0, data.Length);
            }
            
            /// <summary>
            /// AES解密
            /// </summary>
            /// <param name="data"></param>
            /// <param name="key"></param>
            /// <param name="iVector"></param>
            /// <returns></returns>
            public static byte[] DecryptByte(byte[] data, byte[] key, byte[] iVector)
            {
                RijndaelManaged aes = new RijndaelManaged();
                aes.Key = key;
                aes.IV = iVector;
                aes.Mode = CipherMode.CBC;
                aes.Padding = PaddingMode.Zeros;

                ICryptoTransform cTransform = aes.CreateDecryptor();
                return cTransform.TransformFinalBlock(data,0, data.Length);
            }

            /// <summary>
            /// Hash256 加密
            /// </summary>
            public static string Hash256(byte[] data)
            {
                using SHA256 sha256 = SHA256.Create();
                byte[] hash = sha256.ComputeHash(data);
                return Convert.ToBase64String(hash);
            }
            
        }
    }
}