using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Xml.Serialization;

namespace BS.Shared.Core
{
    public class Utils
    {
        private static readonly Dictionary<char, string> criticals = new Dictionary<char, string>
            {
                { '"', "&quot;" }, 
                { '€', "&euro;" }, 
                { '¡', "&iexcl;" }, 
                { '¢', "&cent;" }, 
                { '£', "&pound;" }, 
                { '¤', "&curren;" }, 
                { '¥', "&yen;" }, 
                { '¦', "&brvbar;" }, 
                { '§', "&sect;" }, 
                { '¨', "&uml;" }, 
                { '©', "&copy;" }, 
                { 'ª', "&ordf;" }, 
                { '«', "&laquo;" }, 
                { '¬', "&not;" }, 
                { '­', "&shy;" }, 
                { '®', "&reg;" }, 
                { '¯', "&macr;" }, 
                { '°', "&deg;" }, 
                { '±', "&plusmn;" }, 
                { '²', "&sup2;" }, 
                { '³', "&sup3;" }, 
                { '´', "&acute;" }, 
                { 'µ', "&micro;" }, 
                { '¶', "&para;" }, 
                { '·', "&middot;" }, 
                { '¸', "&cedil;" }, 
                { '¹', "&sup1;" }, 
                { 'º', "&ordm;" }, 
                { '»', "&raquo;" }, 
                { '¼', "&frac14;" }, 
                { '½', "&frac12;" }, 
                { '¾', "&frac34;" }, 
                { '¿', "&iquest;" }, 
                { 'À', "&Agrave;" }, 
                { 'Á', "&Aacute;" }, 
                { 'Â', "&Acirc;" }, 
                { 'Ã', "&Atilde;" }, 
                { 'Ä', "&Auml;" }, 
                { 'Å', "&Aring;" }, 
                { 'Æ', "&AElig;" }, 
                { 'Ç', "&Ccedil;" }, 
                { 'È', "&Egrave;" }, 
                { 'É', "&Eacute;" }, 
                { 'Ê', "&Ecirc;" }, 
                { 'Ë', "&Euml;" }, 
                { 'Ì', "&Igrave;" }, 
                { 'Í', "&Iacute;" }, 
                { 'Î', "&Icirc;" }, 
                { 'Ï', "&Iuml;" }, 
                { 'Ð', "&ETH;" }, 
                { 'Ñ', "&Ntilde;" }, 
                { 'Ò', "&Ograve;" }, 
                { 'Ó', "&Oacute;" }, 
                { 'Ô', "&Ocirc;" }, 
                { 'Õ', "&Otilde;" }, 
                { 'Ö', "&Ouml;" }, 
                { '×', "&times;" }, 
                { 'Ø', "&Oslash;" }, 
                { 'Ù', "&Ugrave;" }, 
                { 'Ú', "&Uacute;" }, 
                { 'Û', "&Ucirc;" }, 
                { 'Ü', "&Uuml;" }, 
                { 'Ý', "&Yacute;" }, 
                { 'Þ', "&THORN;" }, 
                { 'ß', "&szlig;" }, 
                { 'à', "&agrave;" }, 
                { 'á', "&aacute;" }, 
                { 'â', "&acirc;" }, 
                { 'ã', "&atilde;" }, 
                { 'ä', "&auml;" }, 
                { 'å', "&aring;" }, 
                { 'æ', "&aelig;" }, 
                { 'ç', "&ccedil;" }, 
                { 'è', "&egrave;" }, 
                { 'é', "&eacute;" }, 
                { 'ê', "&ecirc;" }, 
                { 'ë', "&euml;" }, 
                { 'ì', "&igrave;" }, 
                { 'í', "&iacute;" }, 
                { 'î', "&icirc;" }, 
                { 'ï', "&iuml;" }, 
                { 'ð', "&eth;" }, 
                { 'ñ', "&ntilde;" }, 
                { 'ò', "&ograve;" }, 
                { 'ó', "&oacute;" }, 
                { 'ô', "&ocirc;" }, 
                { 'õ', "&otilde;" }, 
                { 'ö', "&ouml;" }, 
                { '÷', "&divide;" }, 
                { 'ø', "&oslash;" }, 
                { 'ù', "&ugrave;" }, 
                { 'ú', "&uacute;" }, 
                { 'û', "&ucirc;" }, 
                { 'ü', "&uuml;" }, 
                { 'ý', "&yacute;" }, 
                { 'þ', "&thorn;" }, 
                { 'ÿ', "&yuml;" }
                
                
                // {'>', "&lt;"},
                
                
                
                // {'<', "&gt;"},
            };

        public static bool AreAllNull(params object[] pObjects)
        {
            foreach (object iO in pObjects)
            {
                if (iO != null)
                {
                    return false;
                }
            }

            return true;
        }

        public static bool AreAllNullOrEmpty(params object[] pObjects)
        {
            foreach (object iObject in pObjects)
            {
                if (iObject is string)
                {
                    if (!string.IsNullOrEmpty(iObject as string))
                    {
                        return false;
                    }
                }
                else if (iObject is IEnumerable)
                {
                    if ((iObject as IEnumerable).GetEnumerator().MoveNext())
                    {
                        return false;
                    }
                }
                else if (iObject != null)
                {
                    return false;
                }
            }

            return true;
        }

        public static string Compress(string text)
        {
            byte[] buffer = Encoding.UTF8.GetBytes(text);
            MemoryStream ms = new MemoryStream();
            using (GZipStream zip = new GZipStream(ms, CompressionMode.Compress, true))
            {
                zip.Write(buffer, 0, buffer.Length);
            }

            ms.Position = 0;
            MemoryStream outStream = new MemoryStream();

            byte[] compressed = new byte[ms.Length];
            ms.Read(compressed, 0, compressed.Length);

            byte[] gzBuffer = new byte[compressed.Length + 4];
            Buffer.BlockCopy(compressed, 0, gzBuffer, 4, compressed.Length);
            Buffer.BlockCopy(BitConverter.GetBytes(buffer.Length), 0, gzBuffer, 0, 4);
            return Convert.ToBase64String(gzBuffer);
        }

        public static string Decompress(string compressedText)
        {
            byte[] gzBuffer = Convert.FromBase64String(compressedText);
            using (MemoryStream ms = new MemoryStream())
            {
                int msgLength = BitConverter.ToInt32(gzBuffer, 0);
                ms.Write(gzBuffer, 4, gzBuffer.Length - 4);

                byte[] buffer = new byte[msgLength];

                ms.Position = 0;
                using (GZipStream zip = new GZipStream(ms, CompressionMode.Decompress))
                {
                    zip.Read(buffer, 0, buffer.Length);
                }

                return Encoding.UTF8.GetString(buffer);
            }
        }

        public static string EnumName<T>(T pObject)
        {
            return Enum.GetName(typeof(T), pObject);
        }

        public static T EnumParse<T>(string pValue)
        {
            return (T)Enum.Parse(typeof(T), pValue);
        }

        public static IEnumerable<T> GetAllEnumValues<T>()
        {
            return Enum.GetValues(typeof(T)).Cast<T>();
        }

        public static List<DayOfWeek> GetDayOfWeeks(DayOfWeek lFirst)
        {
            var lResult = new List<DayOfWeek>();

            DateTime lStart = DateTime.Now;
            while (lStart.DayOfWeek != lFirst)
            {
                lStart = lStart.AddDays(1);
            }

            for (int i = 1; i < 8; i++)
            {
                lResult.Add(lStart.DayOfWeek);
                lStart = lStart.AddDays(1);
            }

            return lResult;
        }

        public static int GetWholeWorkWeekCount(DateTime pStart, DateTime pEnd)
        {
            return GetWholeWorkWeeks(pStart, pEnd).Count();
        }

        public static List<DateTimeSpan> GetWholeWorkWeeks(DateTime pStart, DateTime pEnd)
        {
            while (pStart.DayOfWeek != DayOfWeek.Monday)
            {
                pStart = pStart.AddDays(1);
            }

            List<DateTimeSpan> lResult = new List<DateTimeSpan>();
            while ((pStart = pStart.AddDays(4)) <= pEnd.Date)
            {
                lResult.Add(new DateTimeSpan { StartDateTime = pStart.AddDays(-4), EndDateTime = pStart.AddDays(1).AddMilliseconds(-1) });
                pStart = pStart.AddDays(3);
            }

            return lResult;
        }

        public static bool IsAnyNull(params object[] pObjects)
        {
            foreach (object iO in pObjects)
            {
                if (iO == null)
                {
                    return true;
                }
            }

            return false;
        }

        public static bool IsAnyNullOrEmpty(params string[] pStrings)
        {
            foreach (string iString in pStrings)
            {
                if (string.IsNullOrEmpty(iString))
                {
                    return true;
                }
            }

            return false;
        }

        public static Queue<T> MakeQueue<T>(params T[] items)
        {
            return new Queue<T>(items);
        }

        public static string MyHtmlEncode(string pString)
        {
            StringBuilder htmlk = new StringBuilder();
            int ia = 0;
            for (int i = 0; i < pString.Length; i++)
            {
                char c = pString[i];
                if (criticals.ContainsKey(c))
                {
                    htmlk.Append(pString.Substring(ia, i - ia));
                    htmlk.Append(criticals[c]);
                    ia = i + 1;
                }
            }

            htmlk.Append(pString.Substring(ia));
            return htmlk.ToString();
        }

        public static NullCompareResult NullCompare(object pO1, object pO2)
        {
            NullCompareResult lResult = new NullCompareResult();

            lResult.Different = (pO1 == null && pO2 != null) || (pO1 != null && pO2 == null);
            lResult.BothNotNull = pO1 != null && pO2 != null;

            return lResult;
        }

        public static byte[] ReadFully(Stream stream)
        {
            byte[] buffer = new byte[32768];
            using (MemoryStream ms = new MemoryStream())
            {
                while (true)
                {
                    int read = stream.Read(buffer, 0, buffer.Length);
                    if (read <= 0)
                    {
                        return ms.ToArray();
                    }

                    ms.Write(buffer, 0, read);
                }
            }
        }

        public static void ReadWriteStream(Stream readStream, Stream writeStream)
        {
            int Length = 256;
            byte[] buffer = new byte[Length];
            int bytesRead = readStream.Read(buffer, 0, Length);

            while (bytesRead > 0)
            {
                writeStream.Write(buffer, 0, bytesRead);
                bytesRead = readStream.Read(buffer, 0, Length);
            }

            readStream.Close();
            writeStream.Close();
        }

        public static T XMLDeserialize<T>(string pPath)
        {
            T lResult = default(T);
            using (var uFS = File.Open(pPath, FileMode.Open))
            {
                var lSerialzer = new XmlSerializer(typeof(T));
                lResult = (T)lSerialzer.Deserialize(uFS);
            }

            return lResult;
        }

        public static T XMLDeserializeFromString<T>(string serializedXML)
        {
            T lResult = default(T);

            if (!String.IsNullOrEmpty(serializedXML))
            {
                var lSerialzer = new XmlSerializer(typeof(T));
                StringReader r = new StringReader(serializedXML);
                lResult = (T)lSerialzer.Deserialize(r);
            }

            return lResult;
        }

        public static object XMLDeserializeFromString(string serializedXML, Type type)
        {
            object lResult = null;

            if (!String.IsNullOrEmpty(serializedXML))
            {
                var lSerialzer = new XmlSerializer(type);
                StringReader r = new StringReader(serializedXML);
                lResult = lSerialzer.Deserialize(r);
            }

            return lResult;
        }

        public static void XMLSerialize<T>(string pPath, T pObject)
        {
            if (!Directory.Exists(Path.GetDirectoryName(pPath)))
            {
                Directory.CreateDirectory(Path.GetDirectoryName(pPath));
            }

            using (var uFS = File.Create(pPath))
            {
                var lSerialzer = new XmlSerializer(typeof(T));
                lSerialzer.Serialize(uFS, pObject);
            }
        }

        public static string XMLSerializeToString(object pObject)
        {
            var lSerialzer = new XmlSerializer(pObject.GetType());
            StringWriter strw = new StringWriter();
            lSerialzer.Serialize(strw, pObject);

            string str = strw.ToString();
            int test = str.Length;
            return str;
        }

	    public static void SaveExceptionAsTextFile(string pPath, Exception e)
	    {
#if DEBUG
		    var directoryName = Path.GetDirectoryName(pPath);
			if (directoryName != null)
		    {
				if (!Directory.Exists(directoryName))
				    return;
		    }
		    var innerException = e.InnerException;
		    var sw = new StreamWriter(pPath, true);

			sw.WriteLine(e.Message);
			sw.WriteLine(e.StackTrace + "\n");

		    while (innerException != null)
		    {
			    sw.WriteLine(innerException.Message);
				sw.WriteLine(innerException.StackTrace + "\n");
			    innerException = innerException.InnerException;
		    }

			sw.Flush();
			sw.Close();
#endif
	    }

		public static int ToInt<T>(T e) where T : struct, IComparable, IFormattable, IConvertible // where T : Enum is not possible
		{
			return (int)(Object)e;
		}
    }

    public struct NullCompareResult
    {
        public bool BothNotNull;

        public bool Different;
    }
}