using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
/// <summary> 
/// Package for all common classes.
/// Used by Project StdSocket and Project StdParser.
/// </summary>
namespace StdCommon
{
    /// <summary>
    /// Handles byte and hex manipulation and calculations.
    /// </summary>
    public static class StdUtil
    {
        /// <summary>
        /// 4-bit - 0001 : Represent Least Significant Bit
        /// </summary>
        public static readonly int CHECK_BIT_1 = 0x3;
        /// <summary>
        /// 4-bit - 0010
        /// </summary>
        public static readonly int CHECK_BIT_2 = 0x2;
        /// <summary>
        /// 4-bit - 0100
        /// </summary>
        public static readonly int CHECK_BIT_3 = 0x1;
        /// <summary>
        /// 4-bit - 1000 : Represent Most Significant Bit
        /// </summary>
        public static readonly int CHECK_BIT_4 = 0x0;
        /// <summary>
        /// Swaps the value of the two parameters.
        /// <code>
        /// T t = y;
        /// y = x;
        /// x = t;
        /// </code>
        /// </summary>
        /// <typeparam name="T">
        /// The element type of the passed parameters
        /// </typeparam>
        /// <param name="x"></param>
        /// <param name="y"></param>
        static public void Swap<T>(ref T x, ref T y)
        {
            T t = y;
            y = x;
            x = t;
        }
        /// <summary>
        /// Method overloading. <c>ByteArrayToHexString</c>
        /// for 1 parameter.
        /// </summary>
        /// <param name="data">Data from device</param>
        /// <returns>
        /// <c>ByteArrayToHexString(byte[] data, int start_idx, int length, bool hyphen)</c>
        /// </returns>
        static public string ByteArrayToHexString(byte[] data)
        {
            return StdUtil.ByteArrayToHexString(data, 0, data.Length, false);
        }
        /// <summary>
        /// Converts byte array from device to hex string.
        /// </summary>
        /// <param name="data">Data from device</param>
        /// <param name="start_idx">The starting position within value</param>
        /// <param name="length">Length of data from device</param>
        /// <param name="hyphen">
        /// If data from device has hyphen format
        /// <example>
        /// Example: 
        /// <c>true</c> if data is "7E-01-00-64" ; 
        /// <c>false</c> if data is "7E010064";
        /// </example>
        /// </param>
        /// <returns>Hex string</returns>
        static public string ByteArrayToHexString(byte[] data, int start_idx, int length, bool hyphen)
        {
            if (hyphen)
                return BitConverter.ToString(data, start_idx, length);

            return BitConverter.ToString(data, start_idx, length).Replace("-", string.Empty);
        }
        /// <summary>
        /// Method overloading. <c>HexStringToByteArray</c>
        /// for 1 parameter.
        /// </summary>
        /// <param name="hex">Hex string</param>
        /// <returns>
        /// <c>HexStringToByteArray(string hex, int start_idx, int length)</c>
        /// </returns>
        static public byte[] HexStringToByteArray(string hex)
        {
            return StdUtil.HexStringToByteArray(hex, 0, hex.Length);
        }
        /// <summary>
        /// Converts Hex string to byte array
        /// </summary>
        /// <param name="hex">Hex string</param>
        /// <param name="start_idx">The starting position within value</param>
        /// <param name="length">Length of value</param>
        /// <returns>byte[]</returns>
        static public byte[] HexStringToByteArray(string hex, int start_idx, int length)
        {
            return Enumerable.Range(start_idx, length)
                             .Where(x => x % 2 == 0)
                             .Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
                             .ToArray();
        }
        /// <summary>
        /// Calculates data by XORing.
        /// <c>0x00 ^= byte</c>
        /// </summary>
        /// <param name="data">Byte array data</param>
        /// <param name="start_idx">The starting position within value</param>
        /// <param name="length">Length of value</param>
        /// <returns>Returns 0x00 in exception ; Returns XORed byte</returns>
        static public byte CalculateXorCheckSum(byte[] data, int start_idx, int length)
        {
            try
            {
                byte chksum = 0x00;

                for (int i = start_idx; i < length; i++)
                {
                    chksum ^= data[i];
                }
                return chksum;
            }
            catch { }
            return 0x00;
        }
        /// <summary>
        /// Calculates string data by XORing.
        /// Uses <see cref="CalculateXorCheckSum(byte[] data, int start_idx, int length)"/>.
        /// </summary>
        /// <param name="data">String data</param>
        /// <param name="start_idx">The starting position within value</param>
        /// <param name="length">Length of value</param>
        /// <returns>Returns empty string in exception ; Returns XORed string <paramref name="data"/></returns>
        static public string CalculateXorCheckSum(string data, int start_idx, int length)
        {
            try
            {
                byte[] bytes = Encoding.UTF8.GetBytes(data);
                byte b = CalculateXorCheckSum(bytes, start_idx, length);
                return Convert.ToString(b, 16).PadLeft(2, '0').ToUpper();
            }
            catch { }
            return String.Empty;
        }
        /// <summary>
        /// Analyses hex character as bit. Check if hex character is 0 or 1.
        /// </summary>
        /// <param name="cHexChar">Hex character</param>
        /// <param name="nCheckBit">
        /// <see cref="StdUtil.CHECK_BIT_1"/> ; 
        /// <see cref="StdUtil.CHECK_BIT_2"/> ; 
        /// <see cref="StdUtil.CHECK_BIT_3"/> ; 
        /// <see cref="StdUtil.CHECK_BIT_4"/>
        /// </param>
        /// <returns>Returns 0 or 1</returns>
        static public short AnalyzeHexCharAsBit(char cHexChar, int nCheckBit)
        {
            try
            {
                SByte digit = Convert.ToSByte(cHexChar.ToString(), 16);    // to base 16. Sample = A
                char[] array = Convert.ToString(digit, 2).PadLeft(4, '0').ToArray(); // to 4bit binary. Sample = 1010
                return (short)(array[nCheckBit] == '1' ? 1 : 0);    // check which bit value
            }
            catch
            {
                throw new FormatException();
            }
        }
        /// <summary>
        /// Same as <see cref="AnalyzeHexCharAsBit(char cHexChar, int nCheckBit)"/>
        /// but inverted. 1 is 0 and 0 is 1.
        /// </summary>
        /// <param name="cHexChar">Hex character</param>
        /// <param name="nCheckBit">
        /// <see cref="StdUtil.CHECK_BIT_1"/> ; 
        /// <see cref="StdUtil.CHECK_BIT_2"/> ; 
        /// <see cref="StdUtil.CHECK_BIT_3"/> ; 
        /// <see cref="StdUtil.CHECK_BIT_4"/>
        /// </param>
        /// <returns>Returns 0 or 1</returns>
        static public short AnalyzeHexCharAsBitInvert(char cHexChar, int nCheckBit)
        {
            // Below method written by Ahamed to Invert the result(eg: LowBat) @ 22012016
            try
            {
                SByte digit = Convert.ToSByte(cHexChar.ToString(), 16);    // to base 16. Sample = A
                char[] array = Convert.ToString(digit, 2).PadLeft(4, '0').ToArray(); // to 4bit binary. Sample = 1010

                return (short)(array[nCheckBit] == '1' ? 0 : 1);    // check which bit value
            }
            catch
            {
                throw new FormatException();
            }
        }
        /// <summary>
        /// Converts hex character to a
        /// 16 base 16 bit signed integer.
        /// </summary>
        /// <param name="cHexChar">Hex character</param>
        /// <returns>Returns Int16</returns>
        static public short GetShortHexValue(char cHexChar)
        {
            try
            {
                return Convert.ToInt16(cHexChar.ToString(), 16);
            }
            catch
            {
                throw new FormatException();
            }
        }
        /// <summary>
        /// Encodes byte array to ASCII string.
        /// </summary>
        /// <param name="data">Byte array data</param>
        /// <param name="start_idx">The starting position within value</param>
        /// <param name="length">Length of value</param>
        /// <returns>Returns string</returns>
        static public string SubBytesAsString(this byte[] data, int start_idx, int length)
        {
            try
            {
                return Encoding.ASCII.GetString(data, start_idx, length);
            }
            catch { }
            return string.Empty;
        }
        /// <summary>
        /// Converts string DateTime to a specific format.
        /// </summary>
        /// <param name="date">String DateTime to be formatted</param>
        /// <param name="src_format">Current format of string DateTime</param>
        /// <param name="target_format">Target format of string DateTime</param>
        /// <returns>Returns string DateTime using <paramref name="target_format"/> format.</returns>
        static public string ReFormatToDateTime(string date, string src_format, string target_format)
        {
            try
            {
                return DateTime.ParseExact(date, src_format, CultureInfo.InvariantCulture).ToString(target_format);
            }
            catch { }
            return string.Empty;
        }
        /// <summary>
        /// Parses byte array data to UInt16 structure.
        /// </summary>
        /// <param name="bytes">Byte array data</param>
        /// <param name="start_idx">The starting position within value</param>
        /// <param name="length">Length of value</param>
        /// <returns>Returns ushort type data, converted from <paramref name="bytes"/></returns>
        static public ushort CalculateCrc16_CCITT_CheckSum(byte[] bytes, int start_idx, int length)
        {
            try
            {
                const ushort poly = 4129;
                ushort[] table = new ushort[256];
                ushort initialValue = 0xffff;
                ushort temp, a;
                ushort crc = initialValue;
                for (int i = 0; i < table.Length; ++i)
                {
                    temp = 0;
                    a = (ushort)(i << 8);
                    for (int j = 0; j < 8; ++j)
                    {
                        if (((temp ^ a) & 0x8000) != 0)
                            temp = (ushort)((temp << 1) ^ poly);
                        else
                            temp <<= 1;
                        a <<= 1;
                    }
                    table[i] = temp;
                }
                for (int i = start_idx; i < length; ++i)
                {
                    crc = (ushort)((crc << 8) ^ table[((crc >> 8) ^ (0xff & bytes[i]))]);
                }
                return crc;
            }
            catch { }
            return 0;
        }
        /// <summary>
        /// Parses hex string data to a string with UInt16 structure.
        /// </summary>
        /// <param name="hex_str">Hex string data</param>
        /// <param name="start_idx">The starting position within value</param>
        /// <param name="length">Length of value</param>
        /// <returns>Returns string</returns>
        static public string CalculateCrc16_CCITT_CheckSum(string hex_str, int start_idx, int length)
        {
            try
            {
                byte[] data = StdUtil.HexStringToByteArray(hex_str, start_idx, length);
                return StdUtil.CalculateCrc16_CCITT_CheckSum(data, 0, data.Length).ToString("X2");
            }
            catch { }
            return String.Empty;
        } 
    }
}