using System;
using System.Collections.Generic;
using System.Data.OleDb;
using System.Linq;
using System.IO;
using System.Text;

namespace SqlDynamite
{
    public static class DataUtil
    {
        /// <summary>
        /// Gets all OLE DB drivers for the local machine.
        /// </summary>
        public static IEnumerable<string> GetOleDbDrivers()
        {
            List<string> list = new List<string>();
            var reader = OleDbEnumerator.GetRootEnumerator();
            while (reader.Read())
            {
                for (int index = 0; index < reader.FieldCount; index++)
                {
                    if (reader.GetName(index) == "SOURCES_DESCRIPTION")
                    {
                        list.Add(reader.GetString(index));
                        break;
                    }
                }
            }
            reader.Close();
            return list.Distinct();
        }

        /// <summary>
        /// Gets all ODBC drivers for the local machine.
        /// </summary>
        public static IEnumerable<string> GetOdbcDrivers()
        {
            IEnumerable<string> list = null;
            PlatformID platform = Environment.OSVersion.Platform;
            string userName = Environment.UserName;
            if (platform == PlatformID.Win32NT)
            {
                list = LoadRegistryDrivers(Type.GetType("Microsoft.Win32.RegistryView").GetField(IntPtr.Size == 8 ? "Registry64" : "Registry32").GetValue(null));
            }
            else if ((platform == PlatformID.Unix && Environment.OSVersion.Version.Major >= 8)
                    || platform == PlatformID.MacOSX)
            {
				list = LoadUnixDrivers("/Library/ODBC/odbcinst.ini").Concat(LoadUnixDrivers($"/Users/{userName}/Library/ODBC/odbcinst.ini")).Distinct();
            }
            else if (platform == PlatformID.Unix && Environment.OSVersion.Version.Major <= 7)
            {
                list = LoadUnixDrivers("/etc/odbcinst.ini").Concat(LoadUnixDrivers($"/home/{userName}/.odbcinst.ini")).Distinct();
            }
            return list;
        }

        private static IEnumerable<string> LoadRegistryDrivers(object registryView)
        {
            IEnumerable<string> list = null;
            object registryHive = Type.GetType("Microsoft.Win32.RegistryHive").GetField("LocalMachine").GetValue(null);
            var types = new[] { Type.GetType("Microsoft.Win32.RegistryHive"), Type.GetType("Microsoft.Win32.RegistryView") };
            object localMachine = Type.GetType("Microsoft.Win32.RegistryKey").GetMethod("OpenBaseKey", types).Invoke(null, new object[] { registryHive, registryView });
            object reg = localMachine.GetType().GetMethod("OpenSubKey", new[] { typeof(string) }).Invoke(localMachine, new object[] { "Software" });
            if (reg != null)
            {
                reg = reg.GetType().GetMethod("OpenSubKey", new[] { typeof(string), typeof(bool) }).Invoke(reg, new object[] { "ODBC", false });
                if (reg != null)
                {
                    reg = reg.GetType().GetMethod("OpenSubKey", new[] { typeof(string), typeof(bool) }).Invoke(reg, new object[] { "ODBCINST.INI", false });
                    if (reg != null)
                    {
                        reg = reg.GetType().GetMethod("OpenSubKey", new[] { typeof(string), typeof(bool) }).Invoke(reg, new object[] { "ODBC Drivers", false });
                        if (reg != null)
                        {
                            list = reg.GetType().GetMethod("GetValueNames").Invoke(reg, null) as string[];
                            reg.GetType().GetMethod("Close").Invoke(localMachine, null);
                        }
                    }
                }
            }
            return list;
        }

        private static IEnumerable<string> LoadUnixDrivers(string fileName)
        {
            if (File.Exists(fileName))
            {
                List<string> result = new List<string>();
                string[] strings = File.ReadAllLines(fileName);
                string[] unusedEntries = { "[ODBC Drivers]", "[ODBC Connection Pooling]" };
                foreach (string str in strings)
                {
                    if (str.StartsWith("[", StringComparison.OrdinalIgnoreCase) &&
                        str.EndsWith("]", StringComparison.OrdinalIgnoreCase) &&
                        !unusedEntries.Contains(str))
                    {
                        result.Add(str.TrimStart('[').TrimEnd(']'));
                    }
                }
                return result;
            }
            return new string[] { };
        }

        /// <summary>
        /// Adds indentation and line breaks to output of JavaScriptSerializer
        /// </summary>
        public static string FormatJson(string jsonString)
        {
            var stringBuilder = new StringBuilder();

            bool escaping = false;
            bool inQuotes = false;
            int indentation = 0;

            foreach (char character in jsonString)
            {
                if (escaping)
                {
                    escaping = false;
                    stringBuilder.Append(character);
                }
                else
                {
                    if (character == '\\')
                    {
                        escaping = true;
                        stringBuilder.Append(character);
                    }
                    else if (character == '\"')
                    {
                        inQuotes = !inQuotes;
                        stringBuilder.Append(character);
                    }
                    else if (!inQuotes)
                    {
                        if (character == ',')
                        {
                            stringBuilder.Append(character);
                            stringBuilder.Append("\r\n");
                            stringBuilder.Append('\t', indentation);
                        }
                        else if (character == '[' || character == '{')
                        {
                            stringBuilder.Append(character);
                            stringBuilder.Append("\r\n");
                            stringBuilder.Append('\t', ++indentation);
                        }
                        else if (character == ']' || character == '}')
                        {
                            stringBuilder.Append("\r\n");
                            stringBuilder.Append('\t', --indentation);
                            stringBuilder.Append(character);
                        }
                        else if (character == ':')
                        {
                            stringBuilder.Append(character);
                            stringBuilder.Append('\t');
                        }
                        else
                        {
                            stringBuilder.Append(character);
                        }
                    }
                    else
                    {
                        stringBuilder.Append(character);
                    }
                }
            }

            return stringBuilder.ToString();
        }
    }
}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        