using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Win32;

namespace Sdaleo
{
    #region RegKey Enums

    internal enum HKEYRoot
    {
        ClassesRoot,
        CurrentUser,
        LocalMachine,
    }

    internal enum KeySub
    {
        Software,
        Custom
    }

    #endregion

    /// <summary>
    /// Wrapper Class around Registry Functionallity,
    /// allows for easy reading/writing to the registry, especially HKEY_CURRENT_USER\Software
    /// ~If you use this class directly make sure to call dispose(),
    ///  otherwise, use the Public Static Helper Functions to do the work for you
    /// </summary>
    internal class RegKey : IDisposable
    {
        private RegistryKey _RegKey;
        private bool _disposed = false;

        #region Constructors

        /// <summary>
        /// Opens the specified Key in CURRENT_USER\Software\Key.
        /// </summary>
        /// <param name="Key">Specify Key to Open</param>
        /// <param name="bReadOnly">Set to true to Open, false to Create if not Exists</param>
        internal RegKey(string Key, bool bReadOnly = true) : this(HKEYRoot.CurrentUser, KeySub.Software, Key, bReadOnly) { }

        /// <summary>
        /// Use this to Open or Automatically Create a Registry Key
        /// </summary>
        /// <param name="root">specify registry root</param>
        /// <param name="sub">specify a sub or custom</param>
        /// <param name="Key">key you want to open / create </param>
        /// <param name="bReadOnly">true if you want to force only reading it</param>
        private RegKey(HKEYRoot root, KeySub sub, string Key, bool bReadOnly)
        {
            string KeyToOpen = String.Empty;
            if (sub == KeySub.Custom)
                KeyToOpen = Key;
            else
                KeyToOpen = sub.ToString() + "\\" + Key;

            // Read Only Permission
            RegistryKeyPermissionCheck permission = RegistryKeyPermissionCheck.ReadSubTree;
            if (!bReadOnly)
                permission = RegistryKeyPermissionCheck.ReadWriteSubTree;

            // Open or Create, if it doesn't exist and we have writable set
            switch (root)
            {
                case HKEYRoot.ClassesRoot:
                    if (bReadOnly)
                        _RegKey = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(KeyToOpen);
                    else
                        _RegKey = Microsoft.Win32.Registry.ClassesRoot.CreateSubKey(KeyToOpen, permission);
                    break;

                case HKEYRoot.CurrentUser:
                    if (bReadOnly)
                        _RegKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(KeyToOpen, permission);
                    else
                        _RegKey = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(KeyToOpen, permission);
                    break;

                case HKEYRoot.LocalMachine:
                    if (bReadOnly)
                        _RegKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(KeyToOpen, permission);
                    else
                        _RegKey = Microsoft.Win32.Registry.LocalMachine.CreateSubKey(KeyToOpen, permission);
                    break;
            }
        }

        /// <summary>
        /// Finalizer
        /// </summary>
        ~RegKey()
        {
#if NET40  
            if (_RegKey != null)
                _RegKey.Dispose();
#endif
            _RegKey = null;
        }

        #endregion

        #region GetValue Registry Functions

        /// <summary>
        /// Generic Registry KeyValue Getter Function
        /// </summary>
        /// <typeparam name="T">Should be a System Type like string, bool, int32, double, decimal, etc...</typeparam>
        /// <param name="strKeyValue">The Value to get from the Registry</param>
        /// <param name="DefaultValue">Default value to use if nothing was retrieved * Error occured *</param>
        /// <returns>Value found or Default Value</returns>
        internal T GetVal<T>(string strKeyValue, T DefaultValue)
        {
            T retVal = DefaultValue;
            try
            {
                if (_RegKey != null)
                {
                    object keyvalue = _RegKey.GetValue(strKeyValue);
                    string strvalue = string.Empty;
                    if (ObjTool.IsNotNullAndNotEmpty(keyvalue))
                    {
                        #region First Handle the Value as a string

                        // Handle Multi-Strings
                        if (keyvalue is object[])
                        {
                            string keyVal2 = string.Empty;
                            string[] strVal2 = (string[])keyvalue;
                            foreach (string str in strVal2)
                            {
                                keyVal2 += str;
                            }
                            strvalue = keyVal2;
                        }
                        else
                        {
                            // Handle Single-Strings
                            strvalue = keyvalue.ToString();
                        }

                        #endregion

                        // Now cast the Object Return type * Handle each object differently *
                        retVal = ObjTool.ConvertStringToObj<T>(strvalue);
                    }
                }
            }
            catch (Exception) { /* ignore */ }
            return retVal;
        }

        /// <summary>
        /// Binary Registry KeyValue Getter Function
        /// </summary>
        /// <param name="strKeyValue">The Value to get from the Registry</param>
        /// <returns>an array of Bytes found in the Registry, or null if none found, or error occured</returns>
        internal byte[] GetVal(string strKeyValue)
        {
            byte[] retVal = null;
            try
            {
                string StringValue = GetVal<String>(strKeyValue, String.Empty);
                if (!String.IsNullOrEmpty(StringValue))
                {
                    Byte[] ByteValue = new Byte[StringValue.Length];
                    for (int i = 0; i < ByteValue.Length; ++i)
                    {
                        string strByte = Convert.ToString(StringValue[i]);
                        ByteValue[i] = byte.Parse(strByte);
                    }
                    retVal = ByteValue;
                }
            }
            catch (Exception) { /* ignore */ }
            return retVal;
        }

        #endregion

        #region SetValue Registry Functions

        /// <summary>
        /// Generic Registry KeyValue Setter Function
        /// </summary>
        /// <typeparam name="T">Should be a System Type like string, bool, int32, double, decimal, etc...</typeparam>        
        /// <param name="strKeyValue">The Value to write to in the Registry</param>        
        /// <param name="Value">The Value to set to the Registry</param>
        /// <returns>true if successful, false otherwise</returns>
        internal bool SetVal<T>(string strKeyValue, T Value)
        {
            bool bRetVal = false;
            try
            {
                if (_RegKey != null)
                {
                    if (Value is Int32)
                    {
                        _RegKey.SetValue(strKeyValue, Value, RegistryValueKind.DWord);
                    }
                    else if (Value is Int64)
                    {
                        _RegKey.SetValue(strKeyValue, Value, RegistryValueKind.QWord);
                    }
                    else
                    {
                        _RegKey.SetValue(strKeyValue, Value, RegistryValueKind.String);
                    }
                    bRetVal = true;
                }
            }
            catch (Exception) { /* ignore */ }
            return bRetVal;
        }

        /// <summary>
        /// Generic Registry KeyValue Setter Function
        /// </summary>
        /// <param name="strKeyValue">The Value to write to in the Registry</param>        
        /// <param name="ByteValue">A binary array of bytes to write to the Registry</param>        
        /// <returns>true if successful, false otherwise</returns>
        internal bool SetVal(string strKeyValue, byte[] ByteValue)
        {
            try
            {
                _RegKey.SetValue(strKeyValue, ByteValue, RegistryValueKind.Binary);
                return true;
            }
            catch (Exception) { /* ignore */ }
            return false;
        }

        #endregion

        #region Public Static Helper Functions

        /// <summary>
        /// Generic Static Registry Writer Function * Allows writing to HKEY\CURRENT_USER\SOFTWARE *
        /// </summary>
        /// <typeparam name="T">Should be a System Type like string, bool, int32, double, decimal, etc...</typeparam>
        /// <param name="SubKey">SubKey i.e. Microsoft, Microsoft\Windows</param>
        /// <param name="KeyValue">Value to write to</param>
        /// <param name="Value">Value to write</param>
        /// <returns>true if successful, false otherwise</returns>
        internal static bool SetKey<T>(String SubKey, String KeyValue, T Value)
        {
            bool bRetVal = false;
            using (RegKey reg = new RegKey(HKEYRoot.CurrentUser, KeySub.Software, SubKey, false))
            {
                bRetVal = reg.SetVal<T>(KeyValue, Value);
            }
            return bRetVal;
        }

        /// <summary>
        /// Generic Static Registry Writer For Binary Function * Allows writing to HKEY\CURRENT_USER\SOFTWARE *
        /// </summary>
        /// <param name="SubKey">SubKey i.e. Microsoft, Microsoft\Windows</param>
        /// <param name="KeyValue">Value to write to</param>
        /// <param name="ByteValue">Binary Value to write</param>
        /// <returns>true if successful, false otherwise</returns>
        internal static bool SetKey(String SubKey, String KeyValue, byte[] ByteValue)
        {
            bool bRetVal = false;
            using (RegKey reg = new RegKey(HKEYRoot.CurrentUser, KeySub.Software, SubKey, false))
            {
                bRetVal = reg.SetVal(KeyValue, ByteValue);
            }
            return bRetVal;
        }

        /// <summary>
        /// Generic Static Registry Writer Function * Allows writing to any key *
        /// </summary>
        /// <typeparam name="T">Should be a System Type like string, bool, int32, double, decimal, etc...</typeparam>
        /// <param name="rootKey">RootKey as defined in this Class</param>
        /// <param name="SubKey">SubKey i.e. Software, Software\Microsoft</param>
        /// <param name="KeyValue">Value to write to</param>
        /// <param name="Value">Value to write</param>
        /// <returns>true if successful, false otherwise</returns>
        internal static bool SetKey<T>(HKEYRoot rootKey, String SubKey, String KeyValue, T Value)
        {
            bool bRetVal = false;
            using (RegKey reg = new RegKey(rootKey, KeySub.Custom, SubKey, false))
            {
                bRetVal = reg.SetVal<T>(KeyValue, Value);
            }
            return bRetVal;
        }

        /// <summary>
        /// Generic Static Registry Writer For Binary Function * Allows writing to any key *
        /// </summary>
        /// <param name="rootKey">RootKey as defined in this Class</param>
        /// <param name="SubKey">SubKey i.e. Software, Software\Microsoft</param>
        /// <param name="KeyValue">Value to write to</param>
        /// <param name="ByteValue">Binary Value to write</param>
        /// <returns>true if successful, false otherwise</returns>
        internal static bool SetKey(HKEYRoot rootKey, String SubKey, String KeyValue, byte[] ByteValue)
        {
            bool bRetVal = false;
            using (RegKey reg = new RegKey(rootKey, KeySub.Custom, SubKey, false))
            {
                bRetVal = reg.SetVal(KeyValue, ByteValue);
            }
            return bRetVal;
        }

        /// <summary>
        /// Generic Static Registry Reader Function * Allows reading from HKEY\CURRENT_USER\SOFTWARE *
        /// </summary>
        /// <typeparam name="T">Should be a System Type like string, bool, int32, double, decimal, etc...</typeparam>
        /// <param name="SubKey">SubKey i.e. Microsoft, Microsoft\Windows</param>
        /// <param name="KeyValue">Value to write to</param>
        /// <param name="DefaultValue">Default Value to return if none found or error occured</param>
        /// <returns>true if successful, false otherwise</returns>
        internal static T GetKey<T>(String SubKey, String KeyValue, T DefaultValue)
        {
            T tRetVal = default(T);
            using (RegKey reg = new RegKey(HKEYRoot.CurrentUser, KeySub.Software, SubKey, true))
            {
                tRetVal = reg.GetVal<T>(KeyValue, DefaultValue);
            }
            return tRetVal;
        }

        /// <summary>
        /// Generic Static Registry Reader For Binary Function * Allows reading from HKEY\CURRENT_USER\SOFTWARE *
        /// </summary>
        /// <param name="SubKey">SubKey i.e. Microsoft, Microsoft\Windows</param>
        /// <param name="KeyValue">Value to write to</param>
        /// <returns>true if successful, false otherwise</returns>
        internal static Byte[] GetKey(String SubKey, String KeyValue)
        {
            Byte[] retByte = null;
            using (RegKey reg = new RegKey(HKEYRoot.CurrentUser, KeySub.Software, SubKey, true))
            {
                retByte = reg.GetVal(KeyValue);
            }
            return retByte;
        }

        /// <summary>
        /// Generic Static Registry Reader Function * Allows reading from any key *
        /// </summary>
        /// <typeparam name="T">Should be a System Type like string, bool, int32, double, decimal, etc...</typeparam>
        /// <param name="rootKey">RootKey as defined in this Class</param>
        /// <param name="SubKey">SubKey i.e. Software, Software\Microsoft</param>
        /// <param name="KeyValue">Value to write to</param>
        /// <param name="DefaultValue">Default Value to return if none found or error occured</param>
        /// <returns>true if successful, false otherwise</returns>
        internal static T GetKey<T>(HKEYRoot rootKey, String SubKey, String KeyValue, T DefaultValue)
        {
            T tRetVal = default(T);
            using (RegKey reg = new RegKey(rootKey, KeySub.Custom, SubKey, true))
            {
                tRetVal = reg.GetVal<T>(KeyValue, DefaultValue);
            }
            return tRetVal;
        }

        /// <summary>
        /// Generic Static Registry Reader For Binary Function * Allows reading from any key *
        /// </summary>
        /// <typeparam name="T">Should be a System Type like string, bool, int32, double, decimal, etc...</typeparam>
        /// <param name="rootKey">RootKey as defined in this Class</param>
        /// <param name="SubKey">SubKey i.e. Software, Software\Microsoft</param>
        /// <param name="KeyValue">Value to write to</param>
        /// <returns>true if successful, false otherwise</returns>
        internal static Byte[] GetKey(HKEYRoot rootKey, String SubKey, String KeyValue)
        {
            Byte[] retByte = null;
            using (RegKey reg = new RegKey(rootKey, KeySub.Custom, SubKey, true))
            {
                retByte = reg.GetVal(KeyValue);
            }
            return retByte;
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            Dispose(true);

            // Use SupressFinalize in case a subclass
            // of this type implements a finalizer
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
#if NET40  
                    if (_RegKey != null)
                        _RegKey.Dispose();
#endif
                }

                // Indicate that the instance has been disposed.
                _RegKey = null;
                _disposed = true;
            }
        }

        #endregion
    }
}
