using System;
using System.Data;
using System.IO;
using System.Text;
using System.Xml;
using System.Configuration;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Windows.Forms;

using Hydra.Framework.Configuration;

namespace Hydra.Framework
{
    //
    //**********************************************************************
    /// <summary>
    /// Implementation class to support retrieval of configuration parameters
    /// </summary>
    //**********************************************************************
    //
    public static class Config
    {
        #region Private Constants
        //
        // **********************************************************************
        /// <summary>
        /// Product Root Registry Key
        /// </summary>
        // **********************************************************************
        //
        private const string ROOT = "Hydra";

        //
        // **********************************************************************
        /// <summary>
        /// Product Registry Key
        /// </summary>
        // **********************************************************************
        //
        private const string PRODUCT = "Hydra";

        //
        // **********************************************************************
        /// <summary>
        /// Reference to the Installed Configuration File
        /// </summary>
        // **********************************************************************
        //
        private const string LOCAL_CONFIGURATION_FILE = "Configuration";

        //
        // **********************************************************************
        /// <summary>
        /// Reference to the Installed Version StateName
        /// </summary>
        // **********************************************************************
        //
        private const string INTALLED_VERSION = "Version";

        //
        // **********************************************************************
        /// <summary>
        /// Database Connection Configuration Section
        /// </summary>
        // **********************************************************************
        //
        public const string SECTION_DATABASE = "Database";

        //
        // **********************************************************************
        /// <summary>
        /// CISCO Mobility Services Settings for v5.2
        /// </summary>
        // **********************************************************************
        //
        public const string SECTION_CISCO_MSE_52 = "MSE52";

        //
        // **********************************************************************
        /// <summary>
        /// CISCO Mobility Services Settings for v6.0
        /// </summary>
        // **********************************************************************
        //
        public const string SECTION_CISCO_MSE_60 = "MSE60";

        //
        // **********************************************************************
        /// <summary>
        /// Persisted Cache Settings
        /// </summary>
        // **********************************************************************
        //
        public const string SECTION_PERSISTED_CACHE = "PersistedCache";
        #endregion

        #region Private Member Variables
        #endregion

        #region Public Methods
        //
        // **********************************************************************
        /// <summary>
        /// Configurations the file.
        /// </summary>
        /// <returns></returns>
        // **********************************************************************
        //
        public static string ConfigurationFile()
        {
            RegistrySettings key = null;
            string localConfigurationFile = string.Empty;

            try
            {
                key = new RegistrySettings(ROOT, PRODUCT);
                if (key != null)
                {
                    if (key.ValueExists(LOCAL_CONFIGURATION_FILE))
                    {
                        localConfigurationFile = key.GetValue(LOCAL_CONFIGURATION_FILE).ToString();
                    }
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }

            return localConfigurationFile;
        }

        //
        // **********************************************************************
        /// <summary>
        /// Versions this instance.
        /// </summary>
        /// <returns></returns>
        // **********************************************************************
        //
        public static string Version()
        {
            RegistrySettings key = null;
            string version = string.Empty;

            try
            {
                key = new RegistrySettings(ROOT, PRODUCT);
                if (key != null)
                {
                    if (key.ValueExists(INTALLED_VERSION))
                    {
                        version = key.GetValue(INTALLED_VERSION).ToString();
                    }
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }

            return version;
        }

        //
        // **********************************************************************
        /// <summary>
        /// Loads the configuration.
        /// </summary>
        /// <returns></returns>
        // **********************************************************************
        //
        public static CustomClass LoadConfiguration()
        {
            CustomClass customClass = null;

            try
            {
                customClass = LoadConfiguration(ConfigurationFile());
            }
            catch (Exception ex)
            {
                throw ex;
            }

            return customClass;
        }

        //
        // **********************************************************************
        /// <summary>
        /// Load the Xml Configuration document and populate our CustomClass with a dynamic property
        /// for each of the supported configuration sections. We're only supporting three sections here.
        /// The default appSettings, plus our standard ApplicationConfiguration
        /// and CommonConfiguration section handlers. These handlers are derived from IConfigurationSectionHandler.
        /// They have extended support for the Description attribute in addition to the Key, Value
        /// pair attributes in the Xml configuration file.
        /// This could easily be extended to include support for any section under the configuration
        /// section that has the <add key="value" value="value"/> structure (assuming you haven't written a
        /// completely new Xml structure for your custom configuration section).
        /// </summary>
        /// <param name="configurationFile">The configuration file.</param>
        /// <returns></returns>
        // **********************************************************************
        //
        public static CustomClass LoadConfiguration(string configurationFile)
        {
            CustomClass customClass = null;
            XmlDocument xmlDoc = null;

            try
            {
                customClass = new CustomClass();
                xmlDoc = new XmlDocument();
                xmlDoc.Load(configurationFile);
                XmlNode configuration = xmlDoc.SelectSingleNode("properties");

                //
                // Build the node list
                //
                XmlNodeList sectionList = configuration.ChildNodes;
                for (int y = 0; y < sectionList.Count; y++)
                {
                    XmlNodeList settingsList = xmlDoc.SelectNodes("properties/" + sectionList[y].Name + "/add");
                    if (settingsList.Count != 0 && settingsList != null)
                    {
                        //
                        // Add a property to customClass for each node found.				
                        //
                        for (int i = 0; i < settingsList.Count; i++)
                        {
                            //
                            // Mandatory Attributes
                            //
                            XmlAttribute atrribKey = settingsList[i].Attributes["key"];
                            XmlAttribute attribValue = settingsList[i].Attributes["value"];

                            //
                            // Optional Attributes
                            //
                            XmlAttribute attribDescription = settingsList[i].Attributes["description"];
                            XmlAttribute attribIsMandatory = settingsList[i].Attributes["isMandatory"];
                            XmlAttribute attribValidation = settingsList[i].Attributes["validation"];

                            if (atrribKey != null && !string.IsNullOrEmpty(atrribKey.Value) &&
                                attribValue != null && !string.IsNullOrEmpty(attribValue.Value))
                            {
                                //
                                // If there's no description for the key - assign the name to the description.
                                // The description attribute is displayed below the name in the property grid.
                                //
                                if (attribDescription == null)
                                {
                                    attribDescription = atrribKey;
                                }

                                //
                                // We'll at least test to see if it's a boolean property and set the type here
                                // to force the property grid to display a dropdown list of True or False.
                                //
                                Type propType = (attribValue.Value.ToLower() == "true" || attribValue.Value.ToLower() == "false") ? typeof(System.Boolean) : typeof(System.String);

                                //
                                // Now add the property
                                //
                                customClass.AddProperty(atrribKey.Value.ToString(), attribValue.Value.ToString(), attribDescription.Value.ToString(), sectionList[y].Name, propType, false, false, true, Configuration.ValidationType.Any);
                            }
                        }
                    }
                }
                xmlDoc = null;
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return customClass;
        }

        //
        // **********************************************************************
        /// <summary>
        /// Loads the configuration.
        /// </summary>
        /// <param name="configurationFile">The configuration file.</param>
        /// <param name="section">The section.</param>
        /// <returns></returns>
        // **********************************************************************
        //
        public static CustomClass LoadConfiguration(string configurationFile, string section)
        {
            CustomClass customClass = null;
            XmlDocument xmlDoc = null;

            try
            {
                customClass = new CustomClass();
                xmlDoc = new XmlDocument();
                xmlDoc.Load(configurationFile);
                XmlNode configuration = xmlDoc.SelectSingleNode("properties");

                //
                // Retrieve configuration settings based on a known section
                //
                XmlNodeList settingsList = xmlDoc.SelectNodes("properties/" + section + "/add");
                if (settingsList.Count != 0 && settingsList != null)
                {
                    //
                    // Add a property to customClass for each node found.				
                    //
                    for (int i = 0; i < settingsList.Count; i++)
                    {
                        //
                        // Mandatory Attributes
                        //
                        XmlAttribute atrribKey = settingsList[i].Attributes["key"];
                        XmlAttribute attribValue = settingsList[i].Attributes["value"];

                        //
                        // Optional Attributes
                        //
                        XmlAttribute attribDescription = settingsList[i].Attributes["description"];
                        XmlAttribute attribIsMandatory = settingsList[i].Attributes["isMandatory"];
                        XmlAttribute attribValidation = settingsList[i].Attributes["validation"];

                        if (atrribKey != null && !string.IsNullOrEmpty(atrribKey.Value) &&
                            attribValue != null && !string.IsNullOrEmpty(attribValue.Value))
                        {
                            //
                            // If there's no description for the key - assign the name to the description.
                            // The description attribute is displayed below the name in the property grid.
                            //
                            if (attribDescription == null)
                            {
                                attribDescription = atrribKey;
                            }

                            //
                            // We'll at least test to see if it's a boolean property and set the type here
                            // to force the property grid to display a dropdown list of True or False.
                            //
                            Type propType = (attribValue.Value.ToLower() == "true" || attribValue.Value.ToLower() == "false") ? typeof(System.Boolean) : typeof(System.String);

                            //
                            // Now add the property
                            //
                            customClass.AddProperty(atrribKey.Value.ToString(), attribValue.Value.ToString(), attribDescription.Value.ToString(), section, propType, false, false, true, Configuration.ValidationType.Any);
                        }
                    }
                }
                xmlDoc = null;
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return customClass;
        }

        //
        // **********************************************************************
        /// <summary>
        /// We're only supporting three sections here at the moment.
        /// The default appSettings, plus our standard ApplicationConfiguration
        /// and CommonConfiguration section handlers. These handlers have extended support for
        /// the Description attribute in addition to the Key, Value pair attributes
        /// in the Xml configuration file.
        /// </summary>
        /// <param name="configurationFile">The configuration file.</param>
        /// <param name="customClass">The custom class.</param>
        // **********************************************************************
        //
        public static void SaveConfiguration(string configurationFile, CustomClass customClass)
        {
            try
            {
                //
                // Reload the configuration file
                //
                XmlDocument xmlDoc = new XmlDocument();
                xmlDoc.Load(configurationFile);

                //
                // Save a backup version
                //
                xmlDoc.Save(configurationFile + "_bak");

                //
                // Populate our property collection. 
                //
                PropertyDescriptorCollection props = customClass.GetProperties();

                //
                // Repolulate the three supported sections
                //
                RepopulateXmlSection(xmlDoc, props);
                xmlDoc.Save(configurationFile);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        //
        // **********************************************************************
        /// <summary>
        /// Saves the configuration.
        /// </summary>
        /// <param name="customClass">The custom class.</param>
        // **********************************************************************
        //
        public static void SaveConfiguration(CustomClass customClass)
        {
            try
            {
                //
                // Reload the configuration file
                //
                XmlDocument xmlDoc = new XmlDocument();
                xmlDoc.Load(ConfigurationFile());

                //
                // Save a backup version
                //
                xmlDoc.Save(ConfigurationFile() + "_bak");

                //
                // Populate our property collection. 
                //
                PropertyDescriptorCollection props = customClass.GetProperties();

                //
                // Repolulate the three supported sections
                //
                RepopulateXmlSection(xmlDoc, props);
                xmlDoc.Save(ConfigurationFile());
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        #endregion

        #region Private Methods
        //
        // **********************************************************************
        /// <summary>
        /// Gets the section list.
        /// </summary>
        /// <param name="props">The props.</param>
        /// <returns></returns>
        // **********************************************************************
        //
        private static List<string> GetSectionList(PropertyDescriptorCollection props)
        {
            List<string> myList = new List<string>();

            try
            {
                if (props != null &&
                    props.Count > 0)
                {
                    foreach (PropertyDescriptor p in props)
                    {
                        if (!string.IsNullOrEmpty(p.Category))
                        {
                            if (!myList.Contains(p.Category))
                                myList.Add(p.Category);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }

            return myList;
        }

        //
        // **********************************************************************
        /// <summary>
        /// Repopulates the Xml section in the Xml Document by finding the matching key name
        /// values in the property collection and Xml node list.
        /// </summary>
        /// <param name="sectionName">StateName of the section.</param>
        /// <param name="xmlDoc">The XML doc.</param>
        /// <param name="props">The props.</param>
        // **********************************************************************
        //
        private static void RepopulateXmlSection(XmlDocument xmlDoc, PropertyDescriptorCollection props)
        {
            try
            {
                List<string> myList = GetSectionList(props);
                if (myList != null)
                {
                    foreach (string section in myList)
                    {
                        XmlNodeList nodes = xmlDoc.SelectNodes("properties/" + section + "/add");
                        for (int i = 0; i < nodes.Count; i++)
                        {
                            //
                            // Find the property in the property collection with the same name as the current node in the Xml document
                            //
                            DynamicProperty property = (DynamicProperty)props[nodes[i].Attributes["key"].Value];
                            if (property != null)
                            {
                                //
                                // Set the node value to the property value (which will have been set in the Property grid.
                                //
                                nodes[i].Attributes["value"].Value = property.GetValue(null).ToString();

                                //
                                // double check here that there is in fact a description attribute
                                //
                                if (nodes[i].Attributes["description"] != null)
                                    nodes[i].Attributes["description"].Value = property.Description;

                                //
                                // double check here that there is in fact a isMandatory attribute
                                //
                                if (nodes[i].Attributes["isMandatory"] != null)
                                    nodes[i].Attributes["isMandatory"].Value = property.IsMandatory.ToString();

                                //
                                // double check here that there is in fact a validation attribute
                                //
                                if (nodes[i].Attributes["validation"] != null)
                                    nodes[i].Attributes["validation"].Value = property.ValidationType.ToString();
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }

        }
        #endregion
    }
}
