﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Cdev.Frameworks.Common
{
    public static class ByteHelper
    {
        /// <summary>
        /// 设置一个字节中的第index位的值为0 例如 58(二进制00111010) index=1 -> 00111000
        /// </summary>
        /// <param name="b">一个字节</param>
        /// <param name="index">0-7</param>
        /// <returns>一个字节</returns>
        public static byte ClearBit(byte b, int index)
        {
            return (byte)(b | (1 << index));
        }

        /// <summary>
        /// 将6个字节的字节数组转换成时间
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="year"></param>
        /// <returns></returns>
        public static DateTime ConvertBytesToDateTime(byte[] bytes, int year = 2000)
        {
            return new DateTime(
                ConvertByteTo10(bytes[0]) + year,
                ConvertByteTo10(bytes[1]),
                ConvertByteTo10(bytes[2]),
                ConvertByteTo10(bytes[3]),
                ConvertByteTo10(bytes[4]),
                ConvertByteTo10(bytes[5]));
        }

        /// <summary>
        /// 将6个字节的字节数组 转换成时间 BCD编码 例如 0x14 0x10 0x08 0x18 0x45 0x34 -> 2014-10-08 18:45:34
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="year"></param>
        /// <returns></returns>
        public static DateTime ConvertBytesToDateTimeByBcd(byte[] bytes, int year = 2000)
        {
            return new DateTime(
                ConvertByteToBcd(bytes[0]) + year,
                ConvertByteToBcd(bytes[1]),
                ConvertByteToBcd(bytes[2]),
                ConvertByteToBcd(bytes[3]),
                ConvertByteToBcd(bytes[4]),
                ConvertByteToBcd(bytes[5]));
        }

        /// <summary>
        ///  把8个字节的字节数组转换成Double
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="isReverse">是否大小端处理</param>
        /// <returns></returns>
        public static Double ConvertBytesToDouble(byte[] bytes, bool isReverse = true)
        {
            if (bytes == null || bytes.Length != 8)
            {
                throw new ArgumentException("参数不能为空,并且长度必须等于8");
            }
            if (isReverse)
            {
                ReverseBytes(bytes);
            }
            return BitConverter.ToDouble(bytes, 0);
        }

        /// <summary>
        /// 把2个字节的字节数组转换成Int16
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="isReverse">是否大小端处理</param>
        /// <returns></returns>
        public static Int16 ConvertBytesToInt16(byte[] bytes, bool isReverse = true)
        {
            if (bytes == null || bytes.Length != 2)
            {
                throw new ArgumentException("参数不能为空,并且长度必须等于2");
            }

            if (isReverse)
            {
                ReverseBytes(bytes);
            }
            return BitConverter.ToInt16(bytes, 0);
        }

        /// <summary>
        /// 把2个字节的字节数组转换成UInt16
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="isReverse">是否大小端处理</param>
        /// <returns></returns>
        public static UInt16 ConvertBytesToUInt16(byte[] bytes, bool isReverse = true)
        {
            if (bytes == null || bytes.Length != 2)
            {
                throw new ArgumentException("参数不能为空,并且长度必须等于2");
            }

            if (isReverse)
            {
                ReverseBytes(bytes);
            }
            return BitConverter.ToUInt16(bytes, 0);
        }

        /// <summary>
        ///  把4个字节的字节数组转换成Int32
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="isReverse">是否大小端处理</param>
        /// <returns></returns>
        public static Int32 ConvertBytesToInt32(byte[] bytes, bool isReverse = true)
        {
            if (bytes == null || bytes.Length != 4)
            {
                throw new ArgumentException("参数不能为空,并且长度必须等于4");
            }

            if (isReverse)
            {
                ReverseBytes(bytes);
            }
            return BitConverter.ToInt32(bytes, 0);
        }

        /// <summary>
        ///  把8个字节的字节数组转换成Int64
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="isReverse">是否大小端处理</param>
        /// <returns></returns>
        public static Int64 ConvertBytesToInt64(byte[] bytes, bool isReverse = true)
        {
            if (bytes == null || bytes.Length != 8)
            {
                throw new ArgumentException("参数不能为空,并且长度必须等于8");
            }
            if (isReverse)
            {
                ReverseBytes(bytes);
            }
            return BitConverter.ToInt64(bytes, 0);
        }

        /// <summary>
        /// 转换字节数组为字符串
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public static string ConvertBytesToString(byte[] bytes, Encoding encoding)
        {
            return encoding.GetString(bytes);
        }

        /// <summary>
        /// 转换字节数组为ASCII字符串
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public static string ConvertBytesToStringByAscii(byte[] bytes)
        {
            return ConvertBytesToString(bytes, Encoding.ASCII);
        }

        /// <summary>
        /// 将字节数组转换成十进制 0x15 0x16 0x56 ->151656
        /// </summary>
        /// <returns></returns>
        public static string ConvertBytesToStringByBcd(byte[] bytes)
        {
            return string.Join("", bytes.Select(p =>
            {
                var result = ConvertByteToBcd(p);
                if (result < 10)
                {
                    return "0" + result;
                }
                else
                {
                    return result.ToString();
                }
            }));
        }

        /// <summary>
        /// 转换字节数组为16进制字符串 例如 0x15 0x16  -> 15 16
        /// </summary>
        /// <param name="b"></param>
        /// <returns></returns>
        public static string ConvertBytesToStringByHex(byte[] bytes, string separator = "")
        {
            return string.Join(separator, bytes.Select(ConvertByteToStringByHex));
        }

        /// <summary>
        /// 转换字节数组为UTF8符串
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public static string ConvertBytesToStringByUtf8(byte[] bytes)
        {
            return ConvertBytesToString(bytes, Encoding.UTF8);
        }

        /// <summary>
        /// 把一个字节转换成10进制数 例如 0x15 -> 21
        /// </summary>
        /// <param name="b"></param>
        /// <returns></returns>
        public static int ConvertByteTo10(byte b)
        {
            return Convert.ToInt32(ConvertByteToStringByHex(b), 16);
        }

        /// <summary>
        /// 将一个字节转换成 BCD编码 0x15 ->15
        /// </summary>
        /// <returns></returns>
        public static int ConvertByteToBcd(byte b)
        {
            return ((b & 0xf0) / 16) * 10 + (b & 0x0f);
        }

        /// <summary>
        /// 转换一个字节为16进制字符 例如 0x15 -> 15 0xAB->AB
        /// </summary>
        /// <param name="b"></param>
        /// <returns></returns>
        public static string ConvertByteToStringByHex(byte b)
        {
            return b.ToString("X2");
        }

        /// <summary>
        /// 把一个时间转换成字节数组 BCD编码 例如 2014-10-08 18:45:34 -> 0x14 0x10 0x08 0x18 0x45 0x34
        /// </summary>
        /// <returns></returns>
        public static byte[] ConvertDateTimeToBytesByBcd(DateTime dt, int year = 2000)
        {
            byte[] buffer = new byte[]
           {
               ConvertIntToByteByBcd(dt.Year-year),
               ConvertIntToByteByBcd(dt.Month),
               ConvertIntToByteByBcd(dt.Day),
               ConvertIntToByteByBcd(dt.Hour),
               ConvertIntToByteByBcd(dt.Minute),
               ConvertIntToByteByBcd(dt.Second)
           };

            return buffer;
        }

        /// <summary>
        /// 把一个时间转换成字节数组 BCD4 编码 例如 2014-10-08 -> 0x20 0x14 0x08 0x18
        /// </summary>
        /// <returns></returns>
        public static byte[] ConvertDateTimeToBytesByBcd4(DateTime dt, int year = 2000)
        {
            var buffer = new byte[4]
            {
                ConvertIntToByteByBcd(dt.Year/100),
                ConvertIntToByteByBcd(dt.Year-year),
                ConvertIntToByteByBcd(dt.Month),
                ConvertIntToByteByBcd(dt.Day),
            };

            return buffer;
        }

        /// <summary>
        /// 把一个 BCD4 编码字节数组 转换成 时间 例如 0x20 0x14 0x08 -> 0x18 2014-10-08
        /// </summary>
        /// <returns></returns>
        public static DateTime ConvertBytesToDateTimeByBcd4(byte[] buf, int start)
        {
            if (buf.Length - start - 4 < 0) throw new ArgumentException("数据长度不正确");
            return new DateTime(
                ConvertByteToBcd(buf[start]) * 100
                + ConvertByteToBcd(buf[start + 1])
                , ConvertByteToBcd(buf[start + 2])
                , ConvertByteToBcd(buf[start + 3])
            );
        }


        /// <summary>
        /// 转换double为大端传输使用的字节数组
        /// </summary>
        /// <param name="val"></param>
        /// <param name="isReverse">是否大小端处理</param>
        /// <returns></returns>
        public static byte[] ConvertDoubleToBytes(double val, bool isReverse = true)
        {
            byte[] buffer = BitConverter.GetBytes(val);
            if (isReverse)
            {
                ReverseBytes(buffer);
            }
            return buffer;
        }

        /// <summary>
        /// 转换十六进制字符串到字节数组 例如 080156AB->0x08 0x01 0x56 0xAB
        /// </summary>
        /// <param name="val"></param>
        /// <returns></returns>
        public static byte[] ConvertHexStringToBytes(string val)
        {
            if (!val.HasValue()) { throw new ArgumentNullException("字符串不能为空!"); }
            List<byte> list = new List<byte>();
            var index = 0;
            while (index <= val.Length - 2)
            {
                var item = val.Substring(index, 2);
                index += 2;
                list.Add(Convert.ToByte(item, 16));
            }
            return list.ToArray();
        }

        /// <summary>
        /// 转换Int16为大端传输使用的字节数组 2个字节
        /// </summary>
        /// <param name="val">0-32767</param>
        /// <param name="isReverse">是否大小端处理</param>
        /// <returns></returns>
        public static byte[] ConvertInt16ToBytes(Int16 val, bool isReverse = true)
        {
            byte[] buffer = BitConverter.GetBytes(val);
            if (isReverse)
            {
                ReverseBytes(buffer);
            }
            return buffer;
        }

        /// <summary>
        /// 转换Int16为大端传输使用的字节数组 2个字节
        /// </summary>
        /// <param name="val">0-32767</param>
        /// <param name="isReverse">是否大小端处理</param>
        /// <returns></returns>
        public static byte[] ConvertInt16ToBytes(string val, bool isReverse = true)
        {
            return ConvertInt16ToBytes(Convert.ToInt16(val), isReverse);
        }

        /// <summary>
        /// 转换Int16为大端传输使用的字节数组 2个字节
        /// </summary>
        /// <param name="val">0-32767</param>
        /// <param name="isReverse">是否大小端处理</param>
        /// <returns></returns>
        public static byte[] ConvertInt16ToBytes(object val, bool isReverse = true)
        {
            return ConvertInt16ToBytes(Convert.ToInt16(val), isReverse);
        }

        /// <summary>
        /// 转换Int32为大端传输使用的字节数组 4个字节
        /// </summary>
        /// <param name="val">0-2147483647</param>
        /// <param name="isReverse">是否大小端处理</param>
        /// <returns></returns>
        public static byte[] ConvertInt32ToBytes(Int32 val, bool isReverse = true)
        {
            byte[] buffer = BitConverter.GetBytes(val);
            if (isReverse)
            {
                ReverseBytes(buffer);
            }
            return buffer;
        }

        /// <summary>
        /// 转换Int32为大端传输使用的字节数组 4个字节
        /// </summary>
        /// <param name="val">0-2147483647</param>
        /// <param name="isReverse">是否大小端处理</param>
        /// <returns></returns>
        public static byte[] ConvertInt32ToBytes(string val, bool isReverse = true)
        {
            return ConvertInt32ToBytes(Convert.ToInt32(val),isReverse);
        }

        /// <summary>
        /// 转换Int32为大端传输使用的字节数组 4个字节
        /// </summary>
        /// <param name="val">0-2147483647</param>
        /// <returns></returns>
        public static byte[] ConvertInt32ToBytes(object val, bool isReverse = true)
        {
            return ConvertInt32ToBytes(Convert.ToInt32(val),isReverse);
        }

        /// <summary>
        /// 转换Int64为大端传输使用的字节数组 8个字节
        /// </summary>
        /// <param name="val">0-9223372036854775807</param>
        /// <param name="isReverse">是否大小端处理</param>
        /// <returns></returns>
        public static byte[] ConvertInt64ToBytes(Int64 val, bool isReverse = true)
        {
            byte[] buffer = BitConverter.GetBytes(val);
            if (isReverse)
            {
                ReverseBytes(buffer);
            }
            return buffer;
        }

        /// <summary>
        /// 转换Int64为大端传输使用的字节数组 8个字节
        /// </summary>
        /// <param name="val">0-9223372036854775807</param>
        /// <param name="isReverse">是否大小端处理</param>
        /// <returns></returns>
        public static byte[] ConvertInt64ToBytes(string val, bool isReverse = true)
        {
            return ConvertInt64ToBytes(Convert.ToInt64(val),isReverse);
        }

        /// <summary>
        /// 转换Int64为大端传输使用的字节数组 8个字节
        /// </summary>
        /// <param name="val">0-9223372036854775807</param>
        /// <param name="isReverse">是否大小端处理</param>
        /// <returns></returns>
        public static byte[] ConvertInt64ToBytes(object val, bool isReverse = true)
        {
            return ConvertInt64ToBytes(Convert.ToInt64(val),isReverse);
        }

        /// <summary>
        /// 转换Int为字节 1个字节
        /// </summary>
        /// <param name="val">0-255</param>
        /// <param name="isReverse">是否大小端处理</param>
        /// <returns></returns>
        public static byte ConvertIntToByte(string val)
        {
            return ConvertIntToByte(Convert.ToInt32(val));
        }

        /// <summary>
        /// 转换Int为字节 1个字节
        /// </summary>
        /// <param name="val">0-255</param>
        /// <returns></returns>
        public static byte ConvertIntToByte(object val)
        {
            return ConvertIntToByte(Convert.ToInt32(val));
        }

        /// <summary>
        /// 转换Int为字节 1个字节
        /// </summary>
        /// <param name="val">0-255</param>
        /// <returns></returns>
        public static byte ConvertIntToByte(int val)
        {
            return Convert.ToByte(val);
        }

        /// <summary>
        /// 转换两位数int为BCD编码 例如 12->0x12 60->0x60  其中的0x60二进制 编码为 0110（十进制6） 0000（十进制0）
        /// BCD编码 即用4位二进制表示一个十进制数 像上面的0110 表示十进制6
        /// </summary>
        /// <param name="val">0-99 必须是两位数</param>
        /// <returns></returns>
        public static byte ConvertIntToByteByBcd(int val)
        {
            byte b = (byte)val;
            byte b1 = (byte)(b / 10);
            byte b2 = (byte)(b % 10);
            return (byte)((b1 << 4) | b2);
        }

        /// <summary>
        /// 转换数字字符串到字节数组 BCD编码 例如 12345678->0x12 0x34 0x56 0x78
        /// </summary>
        /// <param name="val"></param>
        /// <returns></returns>
        public static byte[] ConvertNumStringToBytesByBcd(string val)
        {
            if (!val.HasValue()) { throw new ArgumentNullException("字符串不能为空!"); }
            List<byte> list = new List<byte>();
            var index = 0;
            while (index <= val.Length - 2)
            {
                var item = val.Substring(index, 2);
                index += 2;
                list.Add(ConvertIntToByteByBcd(int.Parse(item)));
            }
            return list.ToArray();
        }

        /// <summary>
        /// 转换字符串为大端传输使用的字节数组
        /// </summary>
        /// <param name="val"></param>
        /// <param name="encoding"></param>
        /// <returns></returns>
        public static byte[] ConvertStringToBytes(string val, Encoding encoding)
        {
            return encoding.GetBytes(val);
        }

        /// <summary>
        /// 获取一个字节中的第index位的值 例如 58(二进制00111010) index=0 -> 0 ,index=3->1
        /// </summary>
        /// <param name="b">一个字节</param>
        /// <param name="index">0-7</param>
        /// <returns>0/1</returns>
        public static int GetBit(byte b, int index)
        {
            return ((b & (1 << index)) > 0) ? 1 : 0;
        }

        /// <summary>
        /// 获取二个字节中的第index位的值 例如 5858(二进制0011101000111010) index=0 -> 0 ,index=3->1
        /// </summary>
        /// <param name="b">一个字节</param>
        /// <param name="index">0-7</param>
        /// <returns>0/1</returns>
        public static int GetBit(short b, int index)
        {
            return ((b & (1 << index)) > 0) ? 1 : 0;
        }

        /// <summary>
        /// 替换字节数组 把 0x15 0x26 0x45 0x36 中的 0x26 0x45 替换成 0x02 ->0x15 0x02 0x36
        /// </summary>
        /// <returns></returns>
        public static byte[] Replace(byte[] bytes, byte[] source, byte[] destination)
        {
            List<byte> list = new List<byte>();

            for (int i = 0; i < bytes.Length; i++)
            {
                if (bytes[i] == source[0])
                {
                    int count = 0;
                    for (int j = 0; j < source.Length; j++)
                    {
                        if (i + j > bytes.Length)
                        {
                            break;
                        }

                        if (bytes[i + j] == source[j])
                        {
                            count++;
                        }
                    }

                    if (count == source.Length)
                    {
                        list.AddRange(destination);
                        i += source.Length - 1;
                    }
                    else
                    {
                        list.Add(bytes[i]);
                    }
                }
                else
                {
                    list.Add(bytes[i]);
                }
            }

            return list.ToArray();
        }

        /// <summary>
        /// 按位取反一个字节中的第index位的值 例如 58(二进制00111010) index=1 -> 00111000 index=2->00111110
        /// </summary>
        /// <param name="b">一个字节</param>
        /// <param name="index">0-7</param>
        /// <returns></returns>
        public static byte ReverseBit(byte b, int index)
        {
            return (byte)(b ^ (byte)(1 << index));
        }

        /// <summary>
        /// 按位取反一个字节中的第index位的值 例如 5858(二进制0011101000111010) index=1 -> 00111000 index=2->00111110
        /// </summary>
        /// <param name="b">一个字节</param>
        /// <param name="index">0-7</param>
        /// <returns></returns>
        public static byte ReverseBit(short b, int index)
        {
            return (byte)(b ^ (byte)(1 << index));
        }

        /// <summary>
        /// 根据情况反转字节数组
        /// </summary>
        /// <returns></returns>
        public static void ReverseBytes(byte[] bytes)
        {
            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(bytes);
            }
        }

        /// <summary>
        /// 设置一个字节中的第index位的值为1 例如 58(二进制00111010) index=0 -> 00111011
        /// </summary>
        /// <param name="b">一个字节</param>
        /// <param name="index">0-7</param>
        /// <returns>一个字节</returns>
        public static byte SetBit(byte b, int index)
        {
            return (byte)(b | (1 << index));
        }

        /// <summary>
        /// 设置一个字节中的第index位的值为1 例如 5858(二进制0011101000111010) index=0 -> 00111011
        /// </summary>
        /// <param name="b">一个字节</param>
        /// <param name="index">0-7</param>
        /// <returns>一个字节</returns>
        public static byte SetBit(short b, int index)
        {
            return (byte)(b | (1 << index));
        }

        public static void BlockCopy(byte[] from, int indexFrom, byte[] to, int indexTo, int maxLength)
        {
            Buffer.BlockCopy(from, indexFrom, to, indexTo, from.Length <= maxLength ? from.Length : maxLength);
        }
    }
}