using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;

namespace JWH
{

    public static class ExtensionByte
    {

        #region [ Byte ] ------------------------------------------------------

        /// <summary>
        /// 바이트 배열을 복제합니다
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="offset"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public static byte[] Clone(this byte[] sender, int offset = -1, int length = -1)
        {
            try
            {
                if (offset < 0) offset = 0;
                if (length < 0) length = sender.Length - offset;
                if (length > sender.Length - offset) length = sender.Length - offset;

                byte[] value = new byte[length];
                Buffer.BlockCopy(sender, offset, value, 0, length);

                return value;
            }
            catch (Exception ex)
            {
                XLogger.Instance.Fatal(ex);
                throw ex;
            }
        }

        #endregion

        #region [ String ] ----------------------------------------------------

        /// <summary>
        /// 문자열을 바이트 배열로 반환한다
        /// </summary>
        /// <param name="sender"></param>
        /// <returns></returns>
        public static byte[] ConvertBytes(this string sender)
        {
            try
            {
                return Encoding.Default.GetBytes(sender);
            }
            catch (Exception ex)
            {
                XLogger.Instance.Fatal(ex);
                throw ex;
            }
        }

        /// <summary>
        /// 진수형태의 문자열을 바이트 배열로 반환한다
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="fromBase">문자열 값의 기수로서 2, 8, 10 또는 16이어야 합니다.</param>
        /// <returns></returns>
        public static byte[] ConvertBytes(this string sender, int fromBase)
        {
            try
            {
                char[] arrContains = "0123456789ABCDEF".Substring(0, fromBase).ToCharArray();

                // 기수에서 허용하지 않는 문자를 필터링
                StringBuilder sb = new StringBuilder();
                string value = sender.ToUpper();
                foreach (char ch in value.ToCharArray())
                {
                    if (arrContains.Contains(ch) == false) continue;
                    sb.Append(ch);
                }

                value = sb.ToString();
                List<byte> bytes = new List<byte>();
                int step = fromBase > 10 ? 2 : 1;
                if (fromBase == 2) step = 8;
                for (int i = 0; i < value.Length; i += step)
                {
                    if (i + step > value.Length) break;
                    byte byteValue = Convert.ToByte(value.Substring(i, step), fromBase);
                    bytes.Add(byteValue);
                }

                return bytes.ToArray();
            }
            catch (Exception ex)
            {
                XLogger.Instance.Fatal(ex);
                throw ex;
            }
        }

        /// <summary>
        /// 바이트 배열을 문자열로 변환합니다
        /// </summary>
        /// <param name="sender"></param>
        /// <returns></returns>
        public static string ConvertString(this byte[] sender, int index = 0, int length = -1)
        {
            try
            {
                if (sender == null) return string.Empty;
                if (length < 0) length = sender.Length;

                return Encoding.Default.GetString(sender, index, length);
            }
            catch (Exception ex)
            {
                XLogger.Instance.Fatal(ex);
                throw ex;
            }
        }

        /// <summary>
        /// 바이트 배열을 HEX 문자열로 반환한다
        /// </summary>
        /// <param name="sender"></param>
        /// <returns></returns>
        public static string ConvertHexString(this byte[] sender, int lineFeed = -1)
        {
            try
            {
                if (sender == null) return string.Empty;

                StringBuilder sb = new StringBuilder();
                int index = 0;
                int length = (lineFeed <= 0 ? int.MaxValue : lineFeed);
                while (true)
                {
                    if (index + length > sender.Length) length = sender.Length - index;
                    if (length == 0) break;
                    sb.AppendLine(BitConverter.ToString(sender, index, length).Replace("-", " "));
                    index += length;
                }

                return sb.ToString();
            }
            catch (Exception ex)
            {
                XLogger.Instance.Fatal(ex);
                throw ex;
            }
        }

        /// <summary>
        /// HEX 문자열을 Byte[]로 변환한다
        /// </summary>
        /// <param name="sender"></param>
        /// <returns></returns>
        public static byte[] ToByteFromHexString(this string sender)
        {
            try
            {
                int mod = sender.Length % 2;
                if (mod != 0) return null;

                byte[] array = new byte[sender.Length / 2];
                for (int i = 0; i < array.Length; i++)
                    array[i] = Convert.ToByte(sender.Substring(i * 2, 2), 16);

                return array;
            }
            catch (Exception ex)
            {
                XLogger.Instance.Fatal(ex);
                return null;
            }
        }

        #endregion

        #region [ Int ] -------------------------------------------------------

        /// <summary>
        /// int 값을 바이트 배열로 반환한다
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="length">Byte Length</param>
        /// <returns></returns>
        public static byte[] ConvertByte(this int sender, int length = 0)
        {
            try
            {
                byte[] intByte = BitConverter.GetBytes(sender);
                if (length < 1) length = intByte.Length;

                byte[] convByte = new byte[length];
                Array.Copy(intByte, convByte, length);
                if (BitConverter.IsLittleEndian) Array.Reverse(convByte);

                return convByte;
            }
            catch (Exception ex)
            {
                XLogger.Instance.Fatal(ex);
                throw ex;
            }
        }

        /// <summary>
        /// 바이트 배열값을 Int로 변환한다
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="startIndex"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public static int ConvertInt(this byte[] sender, int startIndex = 0, int length = -1)
        {
            try
            {
                if (sender == null) return 0;
                if (length == -1) length = sender.Length;

                byte[] byteBuff = new byte[length];
                for (int i = 0; i < length; i++)
                    byteBuff[i] = sender[startIndex + i];
                if (BitConverter.IsLittleEndian) Array.Reverse(byteBuff);

                int value = 0;
                for (int i = 0; i < length; i++)
                    value |= byteBuff[i] << (i * 8);

                return value;
            }
            catch (Exception ex)
            {
                XLogger.Instance.Fatal(ex);
                return 0;
                throw ex;
            }
        }



        /// <summary>
        /// 2바이트 배열 값을 short(Int16) 값으로 반환한다
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="index"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public static short ConvertInt16(this byte[] sender, int index = 0, int length = -1)
        {
            try
            {
                int destIndex = 0;
                byte[] byteBuff = new byte[2];

                if (sender == null) return 0;
                if (length < 1) length = sender.Length;
                if (length > byteBuff.Length) length = byteBuff.Length;
                destIndex = byteBuff.Length - length;

                Array.Copy(sender, index, byteBuff, destIndex, length);
                if (BitConverter.IsLittleEndian) Array.Reverse(byteBuff);

                return BitConverter.ToInt16(byteBuff, 0);
            }
            catch (Exception ex)
            {
                XLogger.Instance.Fatal(ex);
                throw ex;
            }
        }

        /// <summary>
        /// 4바이트 배열 값을 int(Int32) 값으로 반환한다
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="index"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public static int ConvertInt32(this byte[] sender, int index = 0, int length = -1)
        {
            try
            {
                int destIndex = 0;
                byte[] byteBuff = new byte[4];

                if (sender == null) return 0;
                if (length < 1) length = sender.Length;
                if (length > byteBuff.Length) length = byteBuff.Length;
                destIndex = byteBuff.Length - length;

                Array.Copy(sender, index, byteBuff, destIndex, length);
                if (BitConverter.IsLittleEndian) Array.Reverse(byteBuff);

                return BitConverter.ToInt32(byteBuff, 0);
            }
            catch (Exception ex)
            {
                XLogger.Instance.Fatal(ex);
                throw ex;
            }
        }

        /// <summary>
        /// 8바이트 배열 값을 long(Int64) 값으로 반환한다
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="index"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public static long ConvertInt64(this byte[] sender, int index = 0, int length = -1)
        {
            try
            {
                int destIndex = 0;
                byte[] byteBuff = new byte[8];

                if (sender == null) return 0;
                if (length < 1) length = sender.Length;
                if (length > byteBuff.Length) length = byteBuff.Length;
                destIndex = byteBuff.Length - length;

                Array.Copy(sender, index, byteBuff, destIndex, length);
                if (BitConverter.IsLittleEndian) Array.Reverse(byteBuff);

                return BitConverter.ToInt64(byteBuff, 0);
            }
            catch (Exception ex)
            {
                XLogger.Instance.Fatal(ex);
                throw ex;
            }
        }



        /// <summary>
        /// 2바이트 배열 값을 ushort(UInt16) 값으로 반환한다
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="index"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public static ushort ConvertUInt16(this byte[] sender, int index = 0, int length = -1)
        {
            try
            {
                int destIndex = 0;
                byte[] byteBuff = new byte[2];

                if (sender == null) return 0;
                if (length < 1) length = sender.Length;
                if (length > byteBuff.Length) length = byteBuff.Length;
                destIndex = byteBuff.Length - length;

                Array.Copy(sender, index, byteBuff, destIndex, length);
                if (BitConverter.IsLittleEndian) Array.Reverse(byteBuff);

                return BitConverter.ToUInt16(byteBuff, 0);
            }
            catch (Exception ex)
            {
                XLogger.Instance.Fatal(ex);
                throw ex;
            }
        }

        /// <summary>
        /// 4바이트 배열 값을 ulong(UInt32) 값으로 반환한다
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="index"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public static ulong ConvertUInt32(this byte[] sender, int index = 0, int length = -1)
        {
            try
            {
                int destIndex = 0;
                byte[] byteBuff = new byte[4];

                if (sender == null) return 0;
                if (length < 1) length = sender.Length;
                if (length > byteBuff.Length) length = byteBuff.Length;
                destIndex = byteBuff.Length - length;

                Array.Copy(sender, index, byteBuff, destIndex, length);
                if (BitConverter.IsLittleEndian) Array.Reverse(byteBuff);

                return BitConverter.ToUInt32(byteBuff, 0);
            }
            catch (Exception ex)
            {
                XLogger.Instance.Fatal(ex);
                throw ex;
            }
        }

        /// <summary>
        /// 8바이트 배열 값을 ulong(UInt64) 값으로 반환한다
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="index"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public static ulong ConvertUInt64(this byte[] sender, int index = 0, int length = -1)
        {
            try
            {
                int destIndex = 0;
                byte[] byteBuff = new byte[8];

                if (sender == null) return 0;
                if (length < 1) length = sender.Length;
                if (length > byteBuff.Length) length = byteBuff.Length;
                destIndex = byteBuff.Length - length;

                Array.Copy(sender, index, byteBuff, destIndex, length);
                if (BitConverter.IsLittleEndian) Array.Reverse(byteBuff);

                return BitConverter.ToUInt64(byteBuff, 0);
            }
            catch (Exception ex)
            {
                XLogger.Instance.Fatal(ex);
                throw ex;
            }
        }



        /// <summary>
        /// 4바이트 배열 값을 float(Single 단정밀도 부동 소수점) 값으로 반환한다
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="index"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public static float ConvertSingle(this byte[] sender, int index = 0, int length = -1)
        {
            try
            {
                int destIndex = 0;
                byte[] byteBuff = new byte[4];

                if (sender == null) return 0;
                if (length < 1) length = sender.Length;
                if (length > byteBuff.Length) length = byteBuff.Length;
                destIndex = byteBuff.Length - length;

                Array.Copy(sender, index, byteBuff, destIndex, length);
                if (BitConverter.IsLittleEndian) Array.Reverse(byteBuff);

                return BitConverter.ToSingle(byteBuff, 0);
            }
            catch (Exception ex)
            {
                XLogger.Instance.Fatal(ex);
                throw ex;
            }
        }

        /// <summary>
        /// 8바이트 배열 값을 double(배정밀도 부동 소수점) 값으로 반환한다
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="index"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public static double ConvertDouble(this byte[] sender, int index = 0, int length = -1)
        {
            try
            {
                int destIndex = 0;
                byte[] byteBuff = new byte[8];

                if (sender == null) return 0;
                if (length < 1) length = sender.Length;
                if (length > byteBuff.Length) length = byteBuff.Length;
                destIndex = byteBuff.Length - length;

                Array.Copy(sender, index, byteBuff, destIndex, length);
                if (BitConverter.IsLittleEndian) Array.Reverse(byteBuff);

                return BitConverter.ToDouble(byteBuff, 0);
            }
            catch (Exception ex)
            {
                XLogger.Instance.Fatal(ex);
                throw ex;
            }
        }

        #endregion

        /// <summary>
        /// ToObject
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="array"></param>
        /// <returns></returns>
        public static T ToObject<T>(this byte[] array) where T : class, new()
        {
            try
            {
                T obj = new T();
                using (MemoryStream stream = new MemoryStream(array))
                {
                    BinaryFormatter formatter = new BinaryFormatter();
                    stream.Position = 0;
                    obj = (T)formatter.Deserialize(stream);

                    return obj;
                }
            }
            catch (Exception ex)
            {
                XLogger.Instance.Fatal(ex);
                return null;
            }
        }

    }

}
