using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;
using System.Data;
using System.Collections;
using System.Text.RegularExpressions;
using System.Security.Cryptography;
using System.Collections.Specialized;
using System.Runtime.InteropServices;
using Microsoft.Win32;

namespace CrossProduct.Core
{
    /// <summary>
    /// generic utility functions available to all crossproducts
    /// </summary>
    public class Utils
    {        

        static private byte[] Key = { 0x31, 0x02, 0x03, 0x14, 0x05, 0x06, 0x17, 0x08, 0x09, 0x17, 0x11, 0x12, 0x13, 0x14, 0x35, 0x16, 0x31, 0x02, 0x03, 0x14, 0x05, 0x06, 0x17, 0x08, 0x09, 0x17, 0x11, 0x12, 0x13, 0x14, 0x35, 0x16 };
        static private byte[] IV = { 0x21, 0x22, 0x33, 0x14, 0x05, 0x06, 0x17, 0x08, 0x09, 0x47, 0x11, 0x52, 0x13, 0x74, 0x15, 0x26 };

        /// <summary>
        /// Initializes a new instance of the <see cref="Utilities"/> class.
        /// </summary>
        /// Change Made On: 6/4/2009
        public Utils()
        {
        }

        /// <summary>
        /// Gets the product version.
        /// </summary>
        /// <returns></returns>
        /// Changed On: 1/24/20081                
        static public byte[] GetProductVersion()    // Named this way on purpose.
        {
            return Key;
        }

        /// <summary>
        /// Gets the product serial.
        /// </summary>
        /// <returns></returns>
        /// Changed On: 1/24/2008                
        static public byte[] GetProductSerial()     // Named this way on purpose.
        {
            return IV;
        }

        /// <summary>
        /// Determines whether [is alpha numberic] [the specified input].
        /// </summary>
        /// <param name="Input">The input.</param>
        /// <returns>
        /// 	<c>true</c> if [is alpha numberic] [the specified input]; otherwise, <c>false</c>.
        /// </returns>
        static public bool IsAlphaNumberic(string Input)
        {
            foreach (char c in Input)
            {
                if (char.IsDigit(c))
                    continue;
                if (char.IsLetter(c))
                    continue;
                if (char.IsWhiteSpace(c))
                    continue;
                return false;
            }
            return true;
        }

        /// <summary>
        /// Left trim a string to a Max Lenth.
        /// </summary>
        /// <param name="Input">The input.</param>
        /// <param name="MaxLen">The max len.</param>
        /// <returns></returns>
        static public string LeftTrimString(string Input, int MaxLen)
        {
            int pLen = Input.Length;
            string Output = Input;
            if (pLen > MaxLen)
            {
                Output = Input.Remove(0, pLen - MaxLen);
            }
            return Output;
        }

        /// <summary>
        /// Uses Symmetric encryption to encrypt a String.
        /// </summary>
        /// <param name="sLicenseKey">The s license key.</param>
        /// <param name="Key">The key.</param>
        /// <param name="IV">The IV.</param>
        /// <returns></returns>
        static public string EncryptKey(string sLicenseKey, byte[] Key, byte[] IV)
        {
            SymmetricAlgorithm CryptoSvc = new RijndaelManaged();
            CryptoSvc.Key = Key;
            CryptoSvc.IV = IV;
            ICryptoTransform encrypto = CryptoSvc.CreateEncryptor();

            byte[] bytIn = StringToByteArray(sLicenseKey);

            System.IO.MemoryStream ms = new System.IO.MemoryStream();

            CryptoStream cs = new CryptoStream(ms, encrypto,
                CryptoStreamMode.Write);

            cs.Write(bytIn, 0, bytIn.Length);
            cs.FlushFinalBlock();

            byte[] bytOut = ms.GetBuffer();
            int i = 0;
            for (i = 0; i < bytOut.Length; i++)
                if (bytOut[i] == 0)
                    break;

            string Encrypted = System.Convert.ToBase64String(bytOut, 0, i);
            return Encrypted;
        }

        /// <summary>
        /// MD5 encrypt a string and then encode result as base64 string.
        /// </summary>
        /// <param name="sSource">The s source.</param>
        /// <param name="TrimLength">Length of the trim.</param>
        /// <returns></returns>
        static public string MD5EncryptMsg(string sSource, int TrimLength)
        {
            string s64Trimmed = "";

            // Create encryption code.
            ASCIIEncoding ascii = new ASCIIEncoding();
            Byte[] data1ToHash = ascii.GetBytes(sSource);
            byte[] hashvalue = (new MD5CryptoServiceProvider()).ComputeHash(data1ToHash);
            string s64Encoded = Convert.ToBase64String(hashvalue);

            if (s64Encoded.Length > TrimLength)
            {
                s64Trimmed = s64Encoded.Substring(0, TrimLength);
            }
            else
            {
                s64Trimmed = s64Encoded;
            }
            return s64Trimmed;
        }

        /// <summary>
        /// Uses Symmetric Decryption to decrypt a key.
        /// </summary>
        /// <param name="Source">The source.</param>
        /// <param name="Key">The key.</param>
        /// <param name="IV">The IV.</param>
        /// <returns></returns>
        static public string DecryptKey(string Source, byte[] Key, byte[] IV)
        {
            try
            {
                SymmetricAlgorithm CryptoSvc = new RijndaelManaged();
                CryptoSvc.Key = Key;
                CryptoSvc.IV = IV;
                ICryptoTransform encrypto = CryptoSvc.CreateDecryptor();

                byte[] bytIn = System.Convert.FromBase64String(Source);

                System.IO.MemoryStream ms = new System.IO.MemoryStream(bytIn,
                    0, bytIn.Length);


                CryptoStream cs = new CryptoStream(ms, encrypto,
                    CryptoStreamMode.Read);

                System.IO.StreamReader sr = new System.IO.StreamReader(cs);
                return (sr.ReadToEnd());
            }
            catch (Exception)
            {
                return "";
            }
        }

        /// <summary>
        /// Strings to byte array.
        /// </summary>
        /// <param name="source">The source.</param>
        /// <returns></returns>
        /// Changed On: 1/24/2008                
        static public byte[] StringToByteArray(string source)
        {
            ASCIIEncoding Encoder = new ASCIIEncoding();
            byte[] Buffer = Encoder.GetBytes(source);
            return Buffer;
        }

        /// <summary>
        /// Convert standard string to base 64 encoded string.
        /// </summary>
        /// <param name="source">The source.</param>
        /// <returns></returns>
        static public string StringToBase64String(string source)
        {
            try
            {
                // Convert our Message to Base 64 encoded string.
                byte[] ba = StringToByteArray(source);
                string b64Encoded = Convert.ToBase64String(ba);
                return b64Encoded;
            }
            catch (Exception e)
            {
                throw new Exception("Can not encode message.  Error = " + e.Message);
            }
        }

        /// <summary>
        /// Convert a byte array to an ascii string.
        /// </summary>
        /// <param name="source">The source.</param>
        /// <returns></returns>
        static public string ByteArrayToString(byte[] source)
        {
            ASCIIEncoding Encoder = new ASCIIEncoding();
            char[] CharArray = Encoder.GetChars(source);
            string ret = new string(CharArray);
            return ret;
        }

        /// <summary>
        /// Creates a random 32 character GUID for use as a unique key
        /// in HL7 tables.
        /// </summary>
        /// <returns></returns>
        static public string CreateGUID()
        {
            Guid g = Guid.NewGuid();
            string s = g.ToString("N");		// 32 character guid.
            return s;
        }

        /// <summary>
        /// Opens a text file and reads into a string, which it returns.
        /// </summary>
        /// <param name="filePath">The file path.</param>
        /// <returns></returns>
        static public string LoadTextFileIntoString(string filePath)
        {
            FileInfo aFile = new FileInfo(filePath);
            if (aFile.Exists == false)
            {
                throw new ApplicationException("File not found: " + filePath);
            }

            FileStream fs = null;
            StreamReader r = null;
            string s = "";

            try
            {
                fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
                r = new StreamReader(fs);
                r.BaseStream.Seek(0, SeekOrigin.Begin);
                s = r.ReadToEnd();
            }
            catch (Exception e)
            {
                string t = e.Message;
            }
            finally
            {
                r.Close();
                fs.Close();
            }

            return s;
        }

        /// <summary>
        /// Builds a list of comma seperated and bracket delimited column names
        /// from a passed enumeration.
        /// Replaces "_" with a space in the column name.
        /// Replaces "_0_" with a '#' character in the column name.
        /// Replaces "_1_" with a ' - ' string in the column name.
        /// (Bit ugly, but allows us to use Enumerations for Column names
        /// which greatly helps us reduce ordinal indexing errors when
        /// reading DataReader results.  It's a tradeoff).
        /// </summary>
        /// <param name="src">The SRC.</param>
        /// <returns></returns>
        static public string BuildColumnList(Array src)
        {
            StringBuilder b = new StringBuilder();

            foreach (string s in src)
            {
                string s0 = s.Replace("_0_", "#");
                string s1 = s0.Replace("_1_", " - ");
                string s2 = s1.Replace('_', ' ');
                b.AppendFormat("[{0}], ", s2);
            }

            // Remove trailing comma
            string t = b.ToString();
            RemoveTrailingComma(ref t);
            return t;
        }

        /// <summary>
        /// Removes the trailing comma from a string if it has one.
        /// </summary>
        /// <param name="id">The id.</param>
        static public void RemoveTrailingComma(ref string id)
        {
            if (id.LastIndexOf(",") != -1)
                id = id.Remove(id.LastIndexOf(","), 1);
        }

        /// <summary>
        /// Formats DateTime object to "MM/DD/YYYY HH:MM:SS AM"
        /// This is suitable for inserts into Sql Server.
        /// </summary>
        /// <param name="dt">The dt.</param>
        /// <returns></returns>
        static public string SqlDTString(DateTime dt)
        {
            if (dt.Year < 1753)  // sql server limit check.
            {
                return "01/01/1753 01:01:01 AM";
            }

            string s = dt.ToShortDateString() + " " + dt.ToLongTimeString();
            return s;
        }

        /// <summary>
        /// Formats DateTime object to "MM/DD/YYYY HH:MM:SS"
        /// Where HH is in military time.
        /// This is suitable for inserts into Advantage Server.
        /// Note, Advantage requires the leading 0 on hours, minutes, seconds
        /// </summary>
        /// <param name="dt">The dt.</param>
        /// <returns></returns>
        static public string AdvDTStringJustDate(DateTime dt)
        {
            // medisoft 15 is having a fit with the time, they only want the date
            //string td = dt.TimeOfDay.ToString();
            //string s = dt.ToShortDateString() + " " + td;
            //return s;
            return dt.ToShortDateString() + " 00:00:00";
        }

        /// <summary>
        /// returns only the time for the given date in a format that Advantage can use
        /// </summary>
        /// <param name="dt">The dt.</param>
        /// <returns></returns>
        static public string AdvTimeOnlyString(DateTime dt)
        {
            return dt.ToLongTimeString();
        }

        /// <summary>
        /// Convert "Mm/Dd/YYYY" and "HHMM" strings to a valid DateTime structure.
        /// </summary>
        /// <param name="Date">The date.</param>
        /// <param name="Time">The time.</param>
        /// <returns></returns>
        static public DateTime CreateDtFromStrings(string Date, string Time)
        {
            DateTime iDate = DateTime.Now;
            try
            {
                iDate = DateTime.Parse(Date);

                string HH = Time.Substring(0, 2);
                int iHours = Int32.Parse(HH);

                string MM = Time.Substring(2, 2);
                int iMins = Int32.Parse(MM);

                iDate = iDate.AddHours((double)iHours);
                iDate = iDate.AddMinutes((double)iMins);

                return iDate;
            }
            catch (Exception e)
            {                
                return iDate;
            }
        }

        /// <summary>
        /// Creates a comma seperated string list of the passed in collection.
        /// </summary>
        /// <param name="src">The SRC.</param>
        /// <returns></returns>
        static public string BuildValueList(StringCollection src)
        {
            StringBuilder b = new StringBuilder();

            foreach (string s in src)
            {
                b.AppendFormat("{0},", s);
            }

            // Remove trailing comma
            string t = b.ToString();
            RemoveTrailingComma(ref t);
            return t;
        }

        /// <summary>
        /// Formats DateTime object to "MM/DD/YYYY HH:MM:SS"
        /// Where HH is in military time.
        /// This is suitable for inserts into Advantage Server.
        /// Note, Advantage requires the leading 0 on hours, minutes, seconds
        /// </summary>
        /// <param name="dt"></param>
        /// <returns></returns>
        static public string AdvDTString(DateTime dt)
        {
            string td = dt.TimeOfDay.ToString();
            string s = dt.ToShortDateString() + " " + td;
            return s;
        }

        /// <summary>
        /// Advs the DT string date only.
        /// </summary>
        /// <param name="dt">The dt.</param>
        /// <returns></returns>
        static public string AdvDTStringDateOnly(DateTime dt)
        {
            if (dt == new DateTime())
            {
                return new DateTime(0, 0, 0).ToShortDateString() + " 00:00:00";
            }
            else
            {
                return new DateTime(dt.Year, dt.Month, dt.Day).ToShortDateString() + " 00:00:00";
            }
        }

        /// <summary>
        /// Firsts the day of last month.
        /// </summary>
        /// <param name="date">The date.</param>
        /// <returns>the first day of the last month of the month given. eg. date is in december, will return november 1</returns>
        public static DateTime FirstDayOfLastMonth(DateTime date)
        {
            if ((date.Month - 1) == 0)
            {
                //this is january, return last december
                return new DateTime(date.Year - 1, 12, 1);
            }
            else
            {
                return new DateTime(date.Year, date.Month - 1, 1);
            }
        }

        /// <summary>
        /// Lasts the day of last month.
        /// </summary>
        /// <param name="date">The date.</param>
        /// <returns>the last day of the last month of the month given. eg. date is in december, will return november 31</returns>
        public static DateTime LastDayOfLastMonth(DateTime date)
        {
            return date.AddMonths(-1).AddDays(-1);
        }

        /// <summary>
        /// Firsts the day of week.
        /// </summary>
        /// <param name="date">The date.</param>
        /// <returns>the date of the sunday before the given date</returns>
        public static DateTime FirstDayOfWeek(DateTime date)
        {
            //get the day of week for the given date, subtract it to get sunday
            int dayOfWeek = (int)date.DayOfWeek;
            return DateTime.Now.AddDays(-dayOfWeek);
        }

        /// <summary>
        /// Lasts the day of week.
        /// </summary>
        /// <param name="date">The date.</param>
        /// <returns>the date of the saturday following the given date</returns>
        public static DateTime LastDayOfWeek(DateTime date)
        {
            //get the day of week for the given date and add 7-it to get to the next saturday
            int dayOfWeek = (int)date.DayOfWeek;
            return DateTime.Now.AddDays(7 - dayOfWeek);
        }

        /// <summary>
        /// Firsts the day of next month.
        /// </summary>
        /// <param name="date">The date.</param>
        /// <returns>the first day of the next month of the given date</returns>
        public static DateTime FirstDayOfNextMonth(DateTime date)
        {
            return new DateTime(date.Year, date.Month + 1, 1);
        }

        /// <summary>
        /// Lasts the day of next month.
        /// </summary>
        /// <param name="date">The date.</param>
        /// <returns>the last day of the next month of the given date</returns>
        public static DateTime LastDayOfNextMonth(DateTime date)
        {
            return FirstDayOfNextMonth(date).AddMonths(1).AddDays(-1);
        }

        /// <summary>
        /// Firsts the day of current month.
        /// </summary>
        /// <param name="date">The date.</param>
        /// <returns>the first day of the current month</returns>
        public static DateTime FirstDayOfCurrentMonth(DateTime date)
        {
            return new DateTime(date.Year, date.Month, 1);
        }

        /// <summary>
        /// Lasts the day of current month.
        /// </summary>
        /// <param name="date">The date.</param>
        /// <returns>the last day of the current month</returns>
        public static DateTime LastDayOfCurrentMonth(DateTime date)
        {
            return FirstDayOfCurrentMonth(date).AddMonths(1).AddDays(-1);
        }     


    }
}
