using System;
using System.Collections;
using System.Runtime.InteropServices;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
using System.Reflection;
using Foo.Platform;
using System.Diagnostics;

namespace Foo.Settings
{
    [Guid("6FBB970D-0825-471e-9839-448FB2B6A842")]
    [InterfaceType(ComInterfaceType.InterfaceIsDual)]
    [ComVisible(true)]
    public interface IOoganizerSettings
    {
        // reading in configuration data
        configuration ReadConfigXMLFile();        

        // write custom configuration data to default location        
        void WriteConfigXMLFileDefaultLoc(configuration config);        
    }

    /// <summary>
    /// Serializable Xml Object used to store all Configuration data - place any new class objects in here
    /// </summary>
    [Guid("EA2D77C2-AA17-4142-8FA7-7D6FA35316F4")]
    [ClassInterface(ClassInterfaceType.AutoDual)]
    [ComVisible(true)]
    [XmlRoot("configuration", Namespace = "http://www.ooganizer.com", IsNullable = false)]
    public class configuration
    {
        // Member Tags <>
        public ButtonHookSettings ButtonHook = null;

        /// <summary>
        /// configuration - Constructor
        /// </summary>
        public configuration()
        {
            ButtonHook = new ButtonHookSettings();
        }
    }    

    /// <summary>
    /// Serializable Xml Object used to store ButtonHookSettings
    /// </summary>
    [Guid("2C055265-715A-4d10-B744-ADCE796BED1C")]
    [ClassInterface(ClassInterfaceType.AutoDual)]
    [ComVisible(true)]
    [XmlRoot("ButtonHookSettings", Namespace = "http://www.ooganizer.com", IsNullable = false)]
    public class ButtonHookSettings
    {
        // Member Tags <>
        public AllowedProcessNamesW AllowedProcessNames = null;
        public AllowedWindowTitlesW AllowedWindowTitles = null;
        public AllowedWindowClassesW AllowedWindowClasses = null;
        public LoggingSettings LogSettings = null;

        /// <summary>
        /// AllowedProcessNames - (Wrapper) around ArrayList to work with COM and XML
        /// </summary>
        [Guid("06403D5A-E309-42d3-B639-1F9DB99880A4")]
        [ClassInterface(ClassInterfaceType.AutoDual)]
        [ComVisible(true)]
        public class AllowedProcessNamesW
        {            
            private ArrayList m_ArrayList;
            public AllowedProcessNamesW()
            {
                m_ArrayList = new ArrayList();
            }

            [XmlElement("ProcessName")]
            public ProcessName[] ProcessNames
            {
                get
                {
                    ProcessName[] processNames = new ProcessName[m_ArrayList.Count];
                    m_ArrayList.CopyTo(processNames);
                    return processNames;
                }
                set
                {
                    if (value == null) return;
                    ProcessName[] processNames = (ProcessName[])value;
                    m_ArrayList.Clear();
                    foreach (ProcessName processName in processNames)
                        m_ArrayList.Add(processName);
                }
            }  

            public int AddProcessName(ProcessName processName)
            {
                return m_ArrayList.Add(processName);
            }
        }

        [Guid("EC4E542F-2877-4bb4-8FEF-30F1A1C1B53B")]
        [ClassInterface(ClassInterfaceType.AutoDual)]
        [ComVisible(true)]
        public class ProcessName
        {            
            public ProcessName()
            {
                this.CustomBHTop = 0;
                this.CustomBHRight = 0;
            }

            public ProcessName(string ProcessExe, string Resolver, string ResolverPrms, string Launcher, string Closer, string ShowHider, string Versions, int CustomBHTop, int CustomBHRight)
            {
                this._ProcessExe = ProcessExe;
                this.Resolver = Resolver;
                this.ResolverPrms = ResolverPrms;
                this.Launcher = Launcher;
                this.Closer = Closer;
                this.ShowHider = ShowHider;
                this.Versions = Versions;
                this.CustomBHTop = CustomBHTop;
                this.CustomBHRight = CustomBHRight;
            }
            
            private string _ProcessExe;
            [XmlText]
            public string ProcessExe
            {
                get
                {
                    if (!String.IsNullOrEmpty(_ProcessExe))
                        return _ProcessExe.ToUpper();
                    else
                        return String.Empty;
                }
                set
                {
                    _ProcessExe = value;
                }
            }

            [XmlAttribute("Resolver")]
            public string Resolver;

            [XmlAttribute("ResolverPrms")]
            public string ResolverPrms;

            [XmlAttribute("Launcher")]
            public string Launcher;

            [XmlAttribute("Closer")]
            public string Closer;

            [XmlAttribute("ShowHider")]
            public string ShowHider;

            [XmlAttribute("Versions")]
            public string Versions;

            [XmlAttribute("CustomBHTop")]
            public int CustomBHTop;

            [XmlAttribute("CustomBHRight")]
            public int CustomBHRight;
        }
        
        /// <summary>
        /// AllowedWindowTitles - (Wrapper) around ArrayList to work with COM and XML
        /// </summary>        
        [Guid("5B9CC560-9E53-4ae2-8701-E60F99A9A80D")]
        [ClassInterface(ClassInterfaceType.AutoDual)]
        [ComVisible(true)]
        public class AllowedWindowTitlesW
        {
            private ArrayList m_ArrayList;
            public AllowedWindowTitlesW()
            {
                m_ArrayList = new ArrayList();
                m_ArrayList.Add("");
            }
            [XmlElement("WindowTitle")]
            public string[] WindowTitle
            {
                get
                {
                    string[] windowTitles = new string[m_ArrayList.Count];
                    m_ArrayList.CopyTo(windowTitles);
                    return windowTitles;
                }
                set
                {
                    if (value == null) return;
                    string[] windowTitles = (string[])value;
                    m_ArrayList.Clear();
                    foreach (string windowTitle in windowTitles)
                        m_ArrayList.Add(windowTitle);
                }
            }         
        }
        
        /// <summary>
        /// AllowedWindowClasses - (Wrapper) around ArrayList to work with COM and XML
        /// </summary>        
        [Guid("FE3ECBC4-9082-43b5-8274-7DC3A4FB4855")]
        [ClassInterface(ClassInterfaceType.AutoDual)]
        [ComVisible(true)]
        public class AllowedWindowClassesW
        {
            private ArrayList m_ArrayList;
            public AllowedWindowClassesW()
            {
                m_ArrayList = new ArrayList();
                m_ArrayList.Add("");
            }

            [XmlElement("WindowClass")]
            public string[] WindowClass
            {
                get
                {
                    string[] windowClasses = new string[m_ArrayList.Count];
                    m_ArrayList.CopyTo(windowClasses);
                    return windowClasses;
                }
                set
                {
                    if (value == null) return;
                    string[] windowClasses = (string[])value;
                    m_ArrayList.Clear();
                    foreach (string windowClass in windowClasses)
                        m_ArrayList.Add(windowClass);
                }
            }
        }        
        
        /// <summary>
        /// LoggingSettings = Set Log Settings (Location & Detail) for ButtonHook
        /// </summary>
        [Guid("A5A6EBA5-DC51-4bba-B7DC-0202153F57DF")]
        [ClassInterface(ClassInterfaceType.AutoDual)]
        [ComVisible(true)]
        public class LoggingSettings
        {            
            public uint LoggingDetail;

            private string _LogPath;
            public string LogPath
            {
                get
                {
                    if (!String.IsNullOrEmpty(_LogPath))
                        return Platform.Env.GetLogDirectory();
                    else
                        return _LogPath;
                }
                set
                {
                    try
                    {
                        if (Directory.Exists(value))
                            _LogPath = value;                        
                    }
                    catch(Exception){}
                }
            }

            public LoggingSettings()
            {
                _LogPath = String.Empty;
                LoggingDetail = (int)_LoggingDetail.LOGGING_NONE;
            }

            // same enum Definition in ButtonHook
            public enum _LoggingDetail { LOGGING_NONE = 0, LOGGING_LOW = 1, LOGGING_MEDIUM = 2, LOGGING_HIGH = 3};
        }

        /// <summary>
        /// ButtonHookSettings - Constructor
        /// </summary>
        public ButtonHookSettings()
        {
            AllowedProcessNames = new AllowedProcessNamesW();
            AllowedWindowTitles = new AllowedWindowTitlesW();
            AllowedWindowClasses = new AllowedWindowClassesW();
            LogSettings = new LoggingSettings();
        }
    }

    [Guid("80adff74-7124-42e6-ac2c-e19f51a47432")]
    [ClassInterface(ClassInterfaceType.None)]
    [ProgId("Ooganizer.SettingsAcc")]
    [ComVisible(true)]
    public class OoganizerSettingsAcc : IOoganizerSettings
    { 
        private XmlSerializer m_ConfigSerializer = null;
        public const string DEFAULT_CONFIG_XML_FILE_NAME = "AppConfig.xml";
        private string m_AssemblyLocation = "";// location of running assembly
        private configuration m_Configuration; // holds the latest configuration read in or written out,
                                               // for easy access to the configuration

        /// <summary>
        /// Construcs the SettingsAccessor Object, Responsible for
        /// interfacing with the App.Config file
        /// </summary>
        public OoganizerSettingsAcc()
        {
            m_ConfigSerializer = new XmlSerializer(typeof(configuration));

            if (m_ConfigSerializer == null)
                throw new OutOfMemoryException();
            
            // The location of this assembly is in the Platform Directory
            m_AssemblyLocation = Platform.InstallationSpec.InstallPath;
        }

        #region IOoganizerSettings

        /// <summary>
        /// Reads in the Config File from the Resource.
        /// </summary>
        /// <returns>the configuration object</returns>
        public configuration ReadConfigXMLFile()
        {
            string strFileNameWithPath = m_AssemblyLocation + '\\' + DEFAULT_CONFIG_XML_FILE_NAME;

            // If a physical xml file exists in the current directory (allow it to overwrite our settings)
            if (File.Exists(strFileNameWithPath + '\\' + DEFAULT_CONFIG_XML_FILE_NAME))
            {
                return ReadConfigXMLFile(strFileNameWithPath, false);
            }
            else
            {
                // Else force reading of the Resource
                return ReadConfigXMLFile(strFileNameWithPath, true);
            }                       
        }        

        /// <summary>
        /// Writes the specified configruation to the default location and configuration file
        /// </summary>
        /// <param name="config">configuration object to write out</param>
        public void WriteConfigXMLFileDefaultLoc(configuration config)
        {
            string strFileNameWithPath = m_AssemblyLocation + '\\' + DEFAULT_CONFIG_XML_FILE_NAME;
            WriteConfigXMLFile(strFileNameWithPath, config);
        }

        #endregion

        #region XML Writer Functions - useful for * Development and Debugging *

        /// <summary>
        /// Writes the specified configuration to the specified configuration file
        /// </summary>
        /// <param name="strFileNameWithPath">absolute path to file</param>
        /// <param name="config">configuration object to write out</param>
        public void WriteConfigXMLFile(string strFileNameWithPath, configuration config)
        {
            try
            {
                if (!Directory.Exists(Path.GetDirectoryName(strFileNameWithPath)))
                {
                    Log.Error(string.Format("{0}() - Directory for {1} does not exist", MethodBase.GetCurrentMethod().Name, strFileNameWithPath));
                    return;
                }
                 
                TextWriter writer = new StreamWriter(strFileNameWithPath);
                m_ConfigSerializer.Serialize(writer, config);
                m_Configuration = config;
            }
            catch (Exception e)
            {
                Log.Error(string.Format("{0}() - error thrown", MethodBase.GetCurrentMethod().Name), e);
                return;
            }
        }

        /// <summary>
        /// Use this to write a blank xml config file that you can then use
        /// to store your configuration
        /// </summary>
        /// <param name="strFileNameWithPath">absolute path to file</param>
        public void WriteBlankConfigXMLFile(string strFileNameWithPath)
        {
            try
            {                
                if (!Directory.Exists(Path.GetDirectoryName(strFileNameWithPath)))
                {
                    Log.Error(string.Format("{0}() - Directory for {1} does not exist", MethodBase.GetCurrentMethod().Name, strFileNameWithPath));
                    return;
                }

                TextWriter writer = new StreamWriter(strFileNameWithPath);
                configuration settings = new configuration();
                m_ConfigSerializer.Serialize(writer, settings);
            }
            catch (Exception e)
            {
                Log.Error(string.Format("{0}() - error thrown", MethodBase.GetCurrentMethod().Name), e);
                return;
            }
        }

        #endregion

        #region Main ReadConfig Function - Does most of the work

        /// <summary>
        /// Reads in the config file from the passed in location or from the Resource
        /// </summary>
        /// <param name="strFileNameWithPath">absolute path to file (not used when bReadFromResource is true)</param>
        /// <param name="bReadFromResource">true to read Resource XML, false to read from passed in strFileNameWithPath</param>
        /// <returns></returns>
        private configuration ReadConfigXMLFile(string strFileNameWithPath, bool bReadFromResource)
        {
            const string SETTING_XML_RESOURCE_NAME = "Foo.Platform.AppConfig.xml";
            try
            {
               
                if (bReadFromResource)
                {                    
                    Assembly assembly = Assembly.GetExecutingAssembly();

                    // * Debugging Only * Make sure resource Name exists
                    #if DEBUG
                    string[] resourceNames = Assembly.GetExecutingAssembly().GetManifestResourceNames();
                    bool bFound = false;
                    foreach (string resourceName in resourceNames)
                    {
                        if (resourceName == SETTING_XML_RESOURCE_NAME)
                            bFound = true;
                    }
                    Debug.Assert(bFound);
                    #endif 

                    TextReader reader = new StreamReader(assembly.GetManifestResourceStream(SETTING_XML_RESOURCE_NAME));
                    m_Configuration = (configuration)m_ConfigSerializer.Deserialize(reader);
                    return m_Configuration;                                         
                }
                else
                {
                    TextReader reader = new StreamReader(strFileNameWithPath);
                    m_Configuration = (configuration)m_ConfigSerializer.Deserialize(reader);
                    return m_Configuration;
                }                                
            }
            catch (Exception e)
            {
                Log.Error(string.Format("{0}() - error thrown", MethodBase.GetCurrentMethod().Name), e);
                throw (new Exception(e.Message));
            }
        }

        #endregion

        // Declare the Log4net Variable
        private static log4net.ILog Log = Logger.GetLog4NetInterface(MethodBase.GetCurrentMethod().DeclaringType);
    }
}
