// ==========================================================================
// Author:  Yee Hsu
// Date:    2013-2021
//
// Desc:    Some common C# utility functions
// ==========================================================================

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.Common;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;

using FastMember;
using Newtonsoft.Json;

namespace SPTrader.Common
{
    public static class CommonUtil
    {
        private static readonly DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

        public static DateTime GetStartOfDay(DateTime dateTime)
        {
            return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, 0, 0, 0, 0);
        }

        public static DateTime GetEndOfDay(DateTime dateTime)
        {
            return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, 23, 59, 59, 999);
        }

        public static DateTime FromEpochTime(this uint epochTime)
        {
            return FromEpochTime((long) epochTime);
        }

        public static DateTime FromEpochTime(this long epochTime)
        {
            return epoch.AddSeconds(epochTime);
        }

        public static DateTime ParseDateTime(this uint dt, string format)
        {
            return DateTime.ParseExact(dt.ToString(), format, CultureInfo.InvariantCulture);
        }

        public static uint ToEpochTime(this DateTime date)
        {
            return Convert.ToUInt32((date.ToUniversalTime() - epoch).TotalSeconds);
        }

        public static ulong ToEpochTimeMilliseconds(this DateTime date)
        {
            return Convert.ToUInt64((date.ToUniversalTime() - epoch).TotalMilliseconds);
        }

        public static string ToJsonString(this object _obj, Formatting fmt = Formatting.None)
        {
            string json = JsonConvert.SerializeObject(_obj, fmt, new JsonSerializerSettings
            {
                PreserveReferencesHandling = PreserveReferencesHandling.Objects,
                ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
                NullValueHandling = NullValueHandling.Ignore
            });
            return json;
        }

        public static uint FormatDateTime(this DateTime date, string format)
        {
            return Convert.ToUInt32(date.ToString(format));
        }

        public static string ToDateTimeStr(this DateTime date, string format)
        {
            return Convert.ToDateTime(date).ToString(format);
        }

        public static T FromJsonToObject<T>(this string json)
        {
            //var settings = new JsonSerializerSettings
            //{
            //    NullValueHandling = NullValueHandling.Ignore,
            //    MissingMemberHandling = MissingMemberHandling.Ignore
            //};
            return JsonConvert.DeserializeObject<T>(json);
        }

        /// <summary>
        /// Compares two strings, set ignoreCase to true to ignore case comparison ('A' == 'a')
        /// </summary>
        public static bool CompareTo(this string strA, string strB, bool ignoreCase = false)
        {
            return String.Compare(strA, strB, ignoreCase) == 0;
        }

        public static DateTime? ParseDateTime(string strdate, string format)
        {
            DateTime result;
            if (!DateTime.TryParseExact(strdate, format, 
                CultureInfo.InvariantCulture,
                DateTimeStyles.AssumeUniversal, out result))
            {
                return null;
            };
            return result;
        }

        public static string SimpleEncrypt(this string text)
        {
            return Convert.ToBase64String(
                ProtectedData.Protect(Encoding.Unicode.GetBytes(text), null, DataProtectionScope.CurrentUser));
        }

        public static string SimpleDecrypt(this string text)
        {
            return Encoding.Unicode.GetString(
                ProtectedData.Unprotect(Convert.FromBase64String(text), null, DataProtectionScope.CurrentUser));
        }

        public static string ComputeMD5(string message)
        {
            var src = ASCIIEncoding.ASCII.GetBytes(message);
            var hash = new MD5CryptoServiceProvider().ComputeHash(src);
            return BitConverter.ToString(hash).Replace("-", string.Empty);
            //return Convert.ToBase64String(hash);
        }

        public static BitmapImage GetImageFromUrl(string url, UriKind kind)
        {
            if (url == null) return null;
            return new BitmapImage(new Uri(url, kind));
        }

        public static void CreateAppDataFile(string path)
        {
            // The folder for the roaming current user 
            string folder = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);

            // Combine the base folder with your specific folder....
            string specificFolder = Path.Combine(folder, path);

            // CreateDirectory will check if folder exists and, if not, create it.
            // If folder exists then CreateDirectory will do nothing.
            Directory.CreateDirectory(specificFolder);
        }

        public static DateTime GetLinkerTime(this Assembly assembly, TimeZoneInfo target = null)
        {
            var filePath = assembly.Location;
            const int c_PeHeaderOffset = 60;
            const int c_LinkerTimestampOffset = 8;

            var buffer = new byte[2048];

            using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
                stream.Read(buffer, 0, 2048);

            var offset = BitConverter.ToInt32(buffer, c_PeHeaderOffset);
            var secondsSince1970 = BitConverter.ToInt32(buffer, offset + c_LinkerTimestampOffset);

            var linkTimeUtc = epoch.AddSeconds(secondsSince1970);

            var tz = target ?? TimeZoneInfo.Local;
            var localTime = TimeZoneInfo.ConvertTimeFromUtc(linkTimeUtc, tz);

            return localTime;
        }

        public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
        {
            foreach (var cur in enumerable)
            {
                action(cur);
            }
        }

        public static int Remove<T>(this ObservableCollection<T> coll, Func<T, bool> condition)
        {
            var itemsToRemove = coll.Where(condition).ToList();

            foreach (var itemToRemove in itemsToRemove)
            {
                coll.Remove(itemToRemove);
            }
            return itemsToRemove.Count;
        }

        // _someObservableCollection.Sort(x => x.Number, false); // Where number is an simple property (non-onject)
        public static void Sort<TSource, TKey>(this ObservableCollection<TSource> source, Func<TSource, TKey> keySelector, bool isAZ)
        {
            if (isAZ)
            {
                List<TSource> sortedList = source.OrderBy(keySelector).ToList();
                source.Clear();
                foreach (var sortedItem in sortedList)
                {
                    source.Add(sortedItem);
                }
            }
            else
            {
                List<TSource> sortedList = source.OrderByDescending(keySelector).ToList();
                source.Clear();
                foreach (var sortedItem in sortedList)
                {
                    source.Add(sortedItem);
                }
            }
        }

        public static bool IsInt(object Expression)
        {
            return int.TryParse(Convert.ToString(Expression), NumberStyles.Any, NumberFormatInfo.InvariantInfo, out _);
        }

        public static bool IsDouble(object Expression)
        {
            return double.TryParse(Convert.ToString(Expression), NumberStyles.Any, NumberFormatInfo.InvariantInfo, out _);
        }

        public static double GetValidBestValue(double[] d)
        {
            if (d == null || d.Length == 0 || d[0] == 0)
                return double.NaN;

            return d[0];
        }

        public static double GetOptionExpiryTime(uint prodExpDate)
        {
            DateTime expCal = CommonUtil.FromEpochTime(prodExpDate).ToLocalTime();
            DateTime expiryCal = new DateTime(expCal.Year, expCal.Month, expCal.Day, 0, 0, 0, 0);
            expiryCal = expiryCal.AddDays(1);

            DateTime nowDate = DateTime.Now;
            DateTime nowCal = new DateTime(nowDate.Year, nowDate.Month, nowDate.Day, 0, 0, 0, 0);

            ulong now = nowCal.ToEpochTimeMilliseconds() / 1000;
            ulong expiryDate = expiryCal.ToEpochTimeMilliseconds() / 1000;

            ulong expiryTime = expiryDate - now + 1;
            double T = expiryTime / (365.0 * 24 * 60 * 60);
            return T;
        }

        public static void StartProcess(string exepath, string args)
        {
            ProcessStartInfo startInfo = new ProcessStartInfo(exepath);
            startInfo.WindowStyle = ProcessWindowStyle.Normal;
            startInfo.Arguments = args;
            Process.Start(startInfo);
        }

        public static async Task<long> PerformLatencyTest(string host, int timeout)
        {
            var task = Task.Run(() => {
                try
                {
                    Ping myPing = new Ping();
                    PingReply reply = myPing.Send(host, timeout);

                    if (reply != null && reply.Status == IPStatus.Success)
                        return reply.RoundtripTime;
                }
                catch { }
                return 0;
            });
            return await task;
        }

        public static string ConvertGreekValueStr(double value, int pre)
        {
            if (double.IsNaN(value))
                return string.Empty;

            if (pre == 6)
                return string.Format("{0:0.000000}", value);
            else if (pre == 4)
                return string.Format("{0:0.0000}", value);
            else

            return string.Format("{0:0.00}", value);
        }

        public static BitmapImage GetImage(string imageUri)
        {
            var bitmapImage = new BitmapImage();

            bitmapImage.BeginInit();
            bitmapImage.UriSource = new Uri("pack://siteoforigin:,,," + imageUri, UriKind.RelativeOrAbsolute);
            bitmapImage.EndInit();

            return bitmapImage;
        }

        public static T CopyObjectByMemory<T>(T source)
        {
            using (MemoryStream _s = new MemoryStream())
            {
                var _f = new BinaryFormatter();
                _f.Serialize(_s, source);
                _s.Position = 0;
                return (T)_f.Deserialize(_s);
            }
        }

        public static T CloneObjectByXml<T>(T source)
        {
            var _so = System.Windows.Markup.XamlWriter.Save(source);
            var _sr = new StringReader(_so);
            var _xr = System.Xml.XmlReader.Create(_sr);
            return (T)System.Windows.Markup.XamlReader.Load(_xr);
        }

        public static T CloneObjectByJson<T>(T source)
        {
            var ss = source.ToJsonString();
            return FromJsonToObject<T>(ss);
        }

        public static string ToBase64(this object obj)
        {
            string json = obj.ToJsonString();
            byte[] bytes = Encoding.Default.GetBytes(json);
            return Convert.ToBase64String(bytes);
        }

        public static T FromBase64<T>(this string base64Text)
        {
            byte[] bytes = Convert.FromBase64String(base64Text);
            string json = Encoding.Default.GetString(bytes);
            return json.FromJsonToObject<T>();
        }

        public static void DownloadFileAsync(string url, string save_path)
        {
            using (WebClient wc = new WebClient())
            {
                wc.DownloadFileAsync(new Uri(url), save_path);
            }
        }

        public static T GetChildOfType<T>(DependencyObject depObj) where T : DependencyObject
        {
            if (depObj == null) return null;

            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
            {
                var child = VisualTreeHelper.GetChild(depObj, i);

                var result = (child as T) ?? GetChildOfType<T>(child);
                if (result != null) return result;
            }
            return null;
        }

        public static List<T> ItemSourceToList<T>(this ItemCollection obj)
        {
            var iList = obj.SourceCollection as IList<T>;
            return iList.ToList();
        }

        // use this to generate sig
        public static string GenerateSignature(string timestamp, string method, string url, string body, string appSecret)
        {
            return GetHMACInHex(appSecret, timestamp + method + url + body);
        }

        public static string GetHMACInHex(string key, string data)
        {
            var hmacKey = Encoding.UTF8.GetBytes(key);
            var dataBytes = Encoding.UTF8.GetBytes(data);

            using (var hmac = new HMACSHA256(hmacKey))
            {
                var sig = hmac.ComputeHash(dataBytes);
                return ByteToHexString(sig);
            }
        }

        public static string ByteToHexString(byte[] bytes)
        {
            char[] c = new char[bytes.Length * 2];
            int b;
            for (int i = 0; i < bytes.Length; i++)
            {
                b = bytes[i] >> 4;
                c[i * 2] = (char)(87 + b + (((b - 10) >> 31) & -39));
                b = bytes[i] & 0xF;
                c[i * 2 + 1] = (char)(87 + b + (((b - 10) >> 31) & -39));
            }
            return new string(c);
        }

        /// <summary>
        /// Creates the Access Sign for the API based on the request.
        /// </summary>
        /// <param name="timestamp">The current timestamp since 01-01-1970 epoch.</param>
        /// <param name="command">The HTTP verb of the request.</param>
        /// <param name="path">The path of the API request.</param>
        /// <param name="body">The (optional) body content.</param>
        /// <param name="apiSecret">The API secret.</param>
        /// <returns>The access sign.</returns>
        public static string GetAccessSign(string timestamp, string command, string path, string body, string apiSecret)
        {
            var hmacKey = Encoding.UTF8.GetBytes(apiSecret);

            string data = timestamp + command + path + body;
            using (var signatureStream = new MemoryStream(Encoding.UTF8.GetBytes(data)))
            {
                return new HMACSHA256(hmacKey).ComputeHash(signatureStream).Aggregate(new StringBuilder(), (sb, b) => sb.AppendFormat("{0:x2}", b), sb => sb.ToString());
            }
        }

        public static TcpState GetState(this TcpClient tcpClient)
        {
            var foo = IPGlobalProperties.GetIPGlobalProperties()
              .GetActiveTcpConnections()
              .SingleOrDefault(x => x.LocalEndPoint.Equals(tcpClient.Client.LocalEndPoint)
                                 && x.RemoteEndPoint.Equals(tcpClient.Client.RemoteEndPoint)
              );

            return foo != null ? foo.State : TcpState.Unknown;
        }

        // DbDataReader probably need to be a child class instead
        public static T ConvertToObject<T>(this DbDataReader rd) where T : class, new()
        {
            Type type = typeof(T);
            var accessor = TypeAccessor.Create(type);
            var members = accessor.GetMembers();
            var t = new T();

            for (int i = 0; i < rd.FieldCount; i++)
            {
                if (!rd.IsDBNull(i))
                {
                    string fieldName = rd.GetName(i);

                    if (members.Any(m => string.Equals(m.Name, fieldName, StringComparison.OrdinalIgnoreCase)))
                    {
                        accessor[t, fieldName] = rd.GetValue(i);
                    }
                }
            }
            return t;
        }

        /// <summary>
        /// Below here is OLD hex/byte/string formatter
        /// </summary>
        /// <typeparam name="T"></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 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 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="GeoUtil.CHECK_BIT_1"/> ; 
        /// <see cref="GeoUtil.CHECK_BIT_2"/> ; 
        /// <see cref="GeoUtil.CHECK_BIT_3"/> ; 
        /// <see cref="GeoUtil.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="GeoUtil.CHECK_BIT_1"/> ; 
        /// <see cref="GeoUtil.CHECK_BIT_2"/> ; 
        /// <see cref="GeoUtil.CHECK_BIT_3"/> ; 
        /// <see cref="GeoUtil.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 = HexStringToByteArray(hex_str, start_idx, length);
                return CalculateCrc16_CCITT_CheckSum(data, 0, data.Length).ToString("X2");
            }
            catch { }
            return String.Empty;
        }


        /// <summary>
        /// FROM OLD COMMON
        /// </summary>

        // for ini i/o
        [DllImport("kernel32")]
        private static extern long WritePrivateProfileString(string section, string key, string val, string filePath);

        [DllImport("kernel32")]
        private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath);

        static public string GetCurrentDirectory()
        {
            return AppDomain.CurrentDomain.BaseDirectory;
        }

        static public string ExtractStringBetween(string sString, string sLeft, string sRight)
        {
            return ExtractStringBetweenCount(sString, 1, sLeft, sRight);
        }

        static public string ExtractStringBetweenCount(string sString, int count, string sLeft, string sRight)
        {
            try
            {
                if (sString == "" || sString == null)
                    return null;

                int nBeg = 0;
                int nEnd = sString.Length;

                if (sLeft != null && count > 0)
                {
                    for (int i = 0; i < count; i++)
                    {
                        nBeg = sString.IndexOf(sLeft, nBeg);

                        if (nBeg == -1)
                            return null;

                        nBeg += sLeft.Length;
                    }
                }

                if (sRight != null)
                    nEnd = sString.IndexOf(sRight, nBeg);

                string sRetString = sString.Substring(nBeg, nEnd - nBeg).Trim();

                if (nBeg >= 0 && nEnd >= nBeg)
                    return sRetString;
            }
            catch { }
            return null;
        }

        static public string ExtractStringBetweenReverse(string sString, string sLeft, string sRight)
        {
            try
            {
                if (sString == "" || sString == null)
                    return null;

                int nBeg = sString.LastIndexOf(sLeft) + sLeft.Length;
                int nEnd = sString.IndexOf(sRight, nBeg);
                string sRetString = sString.Substring(nBeg, nEnd - nBeg);

                if (nBeg < 0 || nEnd < nBeg)
                    return null;
                else
                    return sRetString;
            }
            catch { }
            return null;
        }

        static public string GetMD5Hash(string sMessage)
        {
            Byte[] originalBytes;
            Byte[] encodedBytes;

            MD5 md5 = new MD5CryptoServiceProvider();
            originalBytes = ASCIIEncoding.Default.GetBytes(sMessage);
            encodedBytes = md5.ComputeHash(originalBytes);

            return BitConverter.ToString(encodedBytes).Replace("-", null);
        }

        static public string GetSha1Hash(string sMessage)
        {
            Byte[] originalBytes;
            Byte[] encodedBytes;

            SHA1 sha = new SHA1CryptoServiceProvider();
            originalBytes = ASCIIEncoding.Default.GetBytes(sMessage);
            encodedBytes = sha.ComputeHash(originalBytes);

            return BitConverter.ToString(encodedBytes).Replace("-", null);
        }

        static public void DeleteFile(string sFileName)
        {            // Delete the file if it exists.
            if (File.Exists(sFileName))
            {
                File.Delete(sFileName);
            }
        }

        static public string ReadFileContents(string sFileName)
        {
            string sFileContent = string.Empty;

            try
            {
                StreamReader sr;

                sr = File.OpenText(sFileName);
                sFileContent = sr.ReadToEnd();
                sr.Close();
            }
            catch { }
            return sFileContent;
        }

        /*
        static public bool FtpReadFile(FTPInformation ftpinfo, out string sFileContent, out DateTime dtTime)
        {
            int nTry = 3;
            sFileContent = "";
            dtTime = DateTime.Now;
            bool bSuccess = false;

            do
            {
                bSuccess = Common.DownloadFile(ftpinfo);

                if (bSuccess == true)
                {
                    sFileContent = Common.ReadFileContents(ftpinfo.sFile);
                    Common.GetFileTimeStamp(ftpinfo, out dtTime);
                    break;
                }
                Thread.Sleep(3000);
                nTry--;

            } while (nTry > 0);

            return bSuccess;
        }

        static public bool GetFileTimeStamp(FTPInformation ftpinfo, out DateTime dtTime)
        {
            dtTime = DateTime.Now;

            try
            {
                FtpWebRequest request = (FtpWebRequest)FtpWebRequest.Create("ftp://" + ftpinfo.sHost + ftpinfo.sPath + ftpinfo.sFile);
                request.Credentials = new NetworkCredential(ftpinfo.sUser, ftpinfo.sPass);
                request.Method = WebRequestMethods.Ftp.GetDateTimestamp;
                request.Timeout = 60000;

                FtpWebResponse response = (FtpWebResponse)request.GetResponse();
                dtTime = response.LastModified;
            }
            catch (Exception e)
            {
                Common.SendEmail("Exception!", e.ToString());
                return false;
            }
            return true;
        }

        static public bool UploadFile(FTPInformation ftpinfo)
        {
            try
            {
                FileInfo fileInf = new FileInfo(ftpinfo.sFile);
                string uri = "ftp://" + ftpinfo.sHost + ftpinfo.sPath + fileInf.Name;

                FtpWebRequest reqFTP;

                // Create FtpWebRequest object from the Uri provided
                reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(uri));

                // Provide the WebPermission Credentials
                reqFTP.Credentials = new NetworkCredential(ftpinfo.sUser, ftpinfo.sPass);

                // By default KeepAlive is true, where the control connection is not closed
                // after a command is executed.
                reqFTP.KeepAlive = false;

                // Specify the command to be executed.
                reqFTP.Method = WebRequestMethods.Ftp.UploadFile;

                // Specify the data transfer type.
                reqFTP.UseBinary = true;

                // Notify the server about the size of the uploaded file
                reqFTP.ContentLength = fileInf.Length;

                // The buffer size is set to 2kb
                int buffLength = 2048;
                byte[] buff = new byte[buffLength];
                int contentLen;

                // Opens a file stream (System.IO.FileStream) to read the file to be uploaded
                FileStream fs = fileInf.OpenRead();

                // Stream to which the file to be upload is written
                Stream strm = reqFTP.GetRequestStream();

                // Read from the file stream 2kb at a time
                contentLen = fs.Read(buff, 0, buffLength);

                // Till Stream content ends
                while (contentLen != 0)
                {
                    // Write Content from the file stream to the FTP Upload Stream
                    strm.Write(buff, 0, contentLen);
                    contentLen = fs.Read(buff, 0, buffLength);
                }

                // Close the file stream and the Request Stream
                strm.Close();
                fs.Close();

                return true;
            }
            catch (Exception e)
            {
                Common.SendEmail("Error uploading XML file.", "");
                return false;
            }
            return false;
        }

        static public bool DownloadFile(FTPInformation ftpinfo)
        {
            try
            {
                FtpWebRequest request = (FtpWebRequest)FtpWebRequest.Create("ftp://" + ftpinfo.sHost + ftpinfo.sPath + ftpinfo.sFile);
                request.Credentials = new NetworkCredential(ftpinfo.sUser, ftpinfo.sPass);
                request.Method = WebRequestMethods.Ftp.DownloadFile;
                request.Timeout = 60000;

                FtpWebResponse response = (FtpWebResponse)request.GetResponse();
                Stream rs = response.GetResponseStream();
                Common.DeleteFile(ftpinfo.sFile);
                FileStream fs = new FileStream(ftpinfo.sFile, FileMode.Create);

                Byte[] buffer = new Byte[2047];
                int read = 0;

                do
                {
                    read = rs.Read(buffer, 0, buffer.Length);
                    fs.Write(buffer, 0, read);
                } while (read != 0);

                fs.Flush();
                fs.Close();
                rs.Close();
            }
            catch (Exception e)
            {
                Common.SendEmail("Error downloading file.", "");
                return false;
            }
            return true;
        }
         * */

        static public string GetPrivateProfileString(string sSection, string sKey, string sFileName)
        {
            StringBuilder temp = new StringBuilder(1024);
            GetPrivateProfileString(sSection, sKey, "", temp, 1024, GetCurrentDirectory() + sFileName);
            return temp.ToString();
        }

        static public string ConvertDateTimeToString(DateTime dtTime)
        {
            return dtTime.ToString("d");
        }

        static public string GetNow_YYYYMMDD()
        {
            DateTime dt = DateTime.Now;
            return dt.ToString("yyyyMMdd");
        }

        static public string MergeSplitDateToString(int nMon, int nDay, int nYear)
        {
            return String.Format("{0}/{1}/{2}", nMon, nDay, nYear);
        }

        static public int GetNumericalMonth(string sMon)
        {
            int value = 0;
            Dictionary<string, int> dMon = new Dictionary<string, int>();
            string sMonth = sMon.Substring(0, 3);

            dMon.Add("Jan", 1); dMon.Add("Feb", 2); dMon.Add("Mar", 3); dMon.Add("Apr", 4);
            dMon.Add("May", 5); dMon.Add("Jun", 6); dMon.Add("Jul", 7); dMon.Add("Aug", 8);
            dMon.Add("Sep", 9); dMon.Add("Oct", 10); dMon.Add("Nov", 11); dMon.Add("Dec", 12);

            dMon.TryGetValue(sMonth, out value);

            return value;
        }

        static public void DisplayStringArray(string[] sArray)
        {
            for (int i = 0; i < sArray.Length; i++)
            {
                Console.WriteLine("sArray[{0}] = \"{1}\"", i, sArray[i]);
            }
        }

        /*
        static public void DisplayDefInfo(DefInformation definfo)
        {
            if (definfo != null)
            {
                Console.WriteLine("[{0}][{1}][{2}][{3}][{4}]", definfo.nUpdateId,
                    definfo.sDefDate, definfo.sDefVersion, definfo.sDefSignature, definfo.sEngineVersion);
            }
        }

        static public void DisplayUrlInfo(UrlInformation urlinfo)
        {
            if (urlinfo != null)
            {
                Console.WriteLine("url: {0}", urlinfo.sPatchLink);

                if (urlinfo.lUrlInfos != null)
                {
                    foreach (string sUrl in urlinfo.lUrlInfos)
                    {
                        Console.WriteLine("---: {0}", sUrl);
                    }
                }
            }
        }

        static public void DisplayFixInfo(List<FixInformation> lfinfo)
        {
            if (lfinfo != null)
            {
                foreach (FixInformation fix in lfinfo)
                {
                    Console.WriteLine("\t+ [{0}][{1}]", fix.sFixName, fix.sUrl);
                }
                Console.WriteLine();
            }
        }

        static public bool SendEmail(string sSubject, string sMessage)
        {
            try
            {
                // create mail message object
                MailMessage mail = new MailMessage();
                SmtpClient smtp = new SmtpClient("smtp.sbcglobal.net");

                mail.From = new MailAddress("xml3-daemon@opswat.com");

                foreach (string sEmail in Configuration.lEmailAddress)
                {
                    mail.To.Add(sEmail);
                }
                mail.Subject = sSubject;
                mail.Body = sMessage;
                smtp.Send(mail);
            }
            catch (Exception e)
            {
                return false;
            }
            return true;
        }
         * */

        static public bool ExecuteShellCommand(string sExe)
        {
            try
            {
                var p = Process.Start(sExe);
                p.WaitForExit(10000);

                return true;
            }
            catch { }
            return false;
        }

        static public string[] ExtractEmails(string str)
        {
            string RegexPattern = @"\b[A-Z0-9._-]+@[A-Z0-9][A-Z0-9.-]{0,61}[A-Z0-9]\.[A-Z.]{2,6}\b";

            var matches = Regex.Matches(str, RegexPattern, RegexOptions.IgnoreCase);
            string[] MatchList = new string[matches.Count];

            int c = 0;
            foreach (Match match in matches)
            {
                MatchList[c] = match.ToString();
                c++;
            }
            return MatchList;
        }

        static public string[] ExtractURLs(string str)
        {
            string RegexPattern = @"<a.*?href=[""'](?<url>.*?)[""'].*?>(?<name>.*?)</a>";

            var matches = Regex.Matches(str, RegexPattern, RegexOptions.IgnoreCase);
            string[] MatchList = new string[matches.Count];

            int c = 0;
            foreach (Match match in matches)
            {
                MatchList[c] = match.Groups["url"].Value;
                c++;
            }
            return MatchList;
        }
    }
}