using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Yaulw.Thread;

namespace Installables.All
{
    /// <summary>
    /// Calls GenericInstaller in order to perform an install/uninstall
    /// * Does so in a threaded fashion *
    /// </summary>
    public class GenericInstall
    {
        // Let the Caller know that a) the thread Completed b) it did so successfully
        public static bool s_PerformInstallCompleted = false;
        public static bool s_PerformInstallCompletedSuccessfully = false;

        // Let the Caller know that a) the thread Completed b) it did so successfully
        public static bool s_PerformUninstallCompleted = false;
        public static bool s_PerformUninstallCompletedSuccessfully = false;
        
        // Private ComponentMgmtObjects
        private static GenericInstaller<Component_Binary_Manager> s_binaryInstaller = null;
        private static GenericInstaller<Component_Setup_Manager> s_setupInstaller = null;
        private static SetupEvents s_SetupEvents = new SetupEvents();

        /// <summary>
        /// Perform a Threaded Install
        /// </summary>        
        public static void PerformInstall()
        {            
            TStarter.ThreadMethod tMethod = delegate()
            {
                s_PerformInstallCompleted = false;
                s_PerformInstallCompletedSuccessfully = false;                
                bool bBinaryInstallNeeded = false;
                bool bSetupInstallNeeded = false;

                // # Let's see if we need to update/install Binary Components
                if(s_binaryInstaller == null)
                   s_binaryInstaller = new GenericInstaller<Component_Binary_Manager>();
                bBinaryInstallNeeded = s_binaryInstaller.IsInstallNeeded(ref s_SetupEvents);

                // # Let's see if we need to update/install Setup Components
                if(s_setupInstaller == null)
                   s_setupInstaller = new GenericInstaller<Component_Setup_Manager>();
                bSetupInstallNeeded = s_setupInstaller.IsInstallNeeded(ref s_SetupEvents);

                // # Let's Install, Baby...
                if (bBinaryInstallNeeded || bSetupInstallNeeded)
                {
                    // # Trigger Start Install Event
                    s_SetupEvents.Raise_Before_Install_Event();

                    bool bBinarySuccess = true;
                    bool bSetupbSuccess = true;                    

                    // update/install Binary Components
                    if (bBinaryInstallNeeded)
                        bBinarySuccess = s_binaryInstaller.Install();

                    // update/install Setup Components
                    if (bSetupInstallNeeded)
                        bSetupbSuccess = s_setupInstaller.Install();

                    // # Trigger End Install Event
                    if (bBinaryInstallNeeded || bSetupInstallNeeded)
                        s_SetupEvents.Raise_After_Install_Event();

                    // # We fail if one of them fails
                    s_PerformInstallCompletedSuccessfully = bBinarySuccess && bSetupbSuccess;
                }

                // # Let External Callers know that this Thread Completed
                s_PerformInstallCompleted = true;
            };
            TStarter.StartThread(tMethod, "PerformInstall", System.Threading.ApartmentState.MTA, false, System.Threading.ThreadPriority.Normal);
        }

        /// <summary>
        /// Perform a Threaded Uninstall
        /// </summary>
        public static void PerformUninstall(string ComponentsSeperatedbySemiColonOrBlankForAll)
        {
            s_PerformUninstallCompleted = false;
            s_PerformUninstallCompletedSuccessfully = false;
        }

        /// <summary>
        /// Call this function to let every object know that either PerformInstall or PerformUninstall was called,
        /// the Gui then may have made some other modifications, and we are now completed
        /// </summary>
        public static void SetupMainCompleted()
        {
            // Notify all components that Setup is now complete
            s_SetupEvents.Raise_Ending_Setup_Event();

            // # Imp! - We must call dispose on our Setup Objects
            if (s_binaryInstaller != null)
            {
                s_binaryInstaller.Dispose();
                s_binaryInstaller = null;
            }
            if (s_setupInstaller != null)
            {
                s_setupInstaller.Dispose();
                s_setupInstaller = null;
            }
        }
    }

    /// <summary>
    /// Generic Install Class used for IManageComponents
    /// </summary>
    /// <typeparam name="T">IManageComponents responsible for Installing</typeparam>
    public class GenericInstaller<T> : IDisposable where T : IManageComponents, IDisposable, new()
    {
        #region Private Members

        private T _ComponentMGR = default(T);
        private IInstallComponent[] _ComponentsToPerformWorkOn = null;
        private bool _disposed = false;

        #endregion

        #region Construction

        /// <summary>
        /// Construct the T Object
        /// </summary>
        public GenericInstaller()
        {
            _ComponentMGR = new T();
        }

        /// <summary>
        /// Finalizer
        /// </summary>
        ~GenericInstaller()
        {
            Dispose(true);
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// 
        /// </summary>
        /// <param name="setupEventObj"></param>
        /// <returns></returns>
        public bool IsInstallNeeded(ref SetupEvents setupEventObj)
        {
            if (_ComponentMGR.AreNewerComponentsAvailable())
            {
                Common.Log.Info(String.Format("Newer Components available for Type {0}.", _ComponentMGR.GetType().Name));
                _ComponentsToPerformWorkOn = _ComponentMGR.GetNewerComponents(ref setupEventObj);
                if (_ComponentsToPerformWorkOn != null && _ComponentsToPerformWorkOn.Length > 0)
                {
                    Common.Log.Info(String.Format("Found {0} Newer Components.", _ComponentsToPerformWorkOn.Length));
                    return true;
                }
            }

            Common.Log.Info(String.Format("No Newer Components available for Type {0}.", _ComponentMGR.GetType().Name));
            return false;            
        }

        /// <summary>
        /// Perform Install on the IManageComponents specified when Constructing the GenericInstaller Object
        /// </summary> 
        /// <returns>true if no error occurs, false otherwise</returns>
        public bool Install()
        {
            bool bErrorsOccured = false;                                            
            List<ComponentConfig.Component> successfullyInstalledComponents = new List<ComponentConfig.Component>();
            if (_ComponentsToPerformWorkOn != null && _ComponentsToPerformWorkOn.Length > 0)
            {                    
                // # Start Install                
                foreach (IInstallComponent installComp in _ComponentsToPerformWorkOn)
                {
                    ComponentConfig.Component component = installComp.GetComponent();
                    if (installComp.BEFORE_INSTALLING_COMPONENT())
                    {
                        Common.Log.Info(String.Format("Installing Component {0} with Version {1}.", component.UniqueLabel, component.Version));
                        bool bInstallSuccess = installComp.INSTALL_COMPONENT();
                        bInstallSuccess = bInstallSuccess && installComp.AFTER_INSTALLING_COMPONENT();
                        if (bInstallSuccess)
                        {
                            Common.Log.Info(String.Format("Component {0} with version {1} was correctly installed.", component.UniqueLabel, component.Version));
                            successfullyInstalledComponents.Add(component);
                        }
                        else
                        {
                            bErrorsOccured = true;
                            string msg = String.Format("Component {0} with version {1} was not correctly installed.", component.UniqueLabel, component.Version);
                            Common.Log.Error(msg);
                        }
                    }
                }
                // # End Install   
                
                // Add any installed components to the Installed Configuration
                foreach (ComponentConfig.Component component in successfullyInstalledComponents)
                         _ComponentMGR.GetAllInstalledComponentsW().AddUpdateComponent(component.UniqueLabel, component.Version, component.FileName);

                // Write out the installed Configuration
                Common.WriteOutChangesToInstalledConfig();
            }

            Common.Log.Info(String.Format("Exiting Install() for Type {0} with bErrorsOccured set to = {1}", _ComponentMGR.GetType().Name, bErrorsOccured));
            return !bErrorsOccured;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="setupEventObj"></param>
        /// <returns></returns>
        public bool IsUninstallNeeded(ref SetupEvents setupEventObj, string ComponentsSeperatedbySemiColonOrBlankForAll)
        {            
            if(_ComponentMGR.GetAllInstalledComponents() != null)
            {
                Common.Log.Info(String.Format("Installed Components available for Type {0}.", _ComponentMGR.GetType().Name));
                _ComponentsToPerformWorkOn = _ComponentMGR.GetAllInstalledComponents(ComponentsSeperatedbySemiColonOrBlankForAll, ref setupEventObj);
                if (_ComponentsToPerformWorkOn != null && _ComponentsToPerformWorkOn.Length > 0)
                {
                    Common.Log.Info(String.Format("Found {0} Components to Uninstall.", _ComponentsToPerformWorkOn.Length));
                    return true;
                }
            }

            Common.Log.Info(String.Format("No Installed Components to Uninstall for Type {0}.", _ComponentMGR.GetType().Name));
            return false;            
        }

        /// <summary>
        /// Perform Uninstall on the IManageComponents specified when Constructing the GenericInstaller Object
        /// </summary>     
        /// <param name="ComponentsSeperatedbySemiColonOrBlankForAll">coma-seperated Unique Labels of components to uninstall, blank for all</param>
        /// <returns>true if no error occurs, false otherwise</returns>
        public bool Uninstall(ref SetupEvents setupEventObj, string ComponentsSeperatedbySemiColonOrBlankForAll)
        {
            bool bErrorsOccured = false;
            List<ComponentConfig.Component> successfullyUninstalledComponents = new List<ComponentConfig.Component>();
            if (_ComponentsToPerformWorkOn != null && _ComponentsToPerformWorkOn.Length > 0)
            {                    
                // # Start Uninstall                
                foreach (IInstallComponent uninstallComp in _ComponentsToPerformWorkOn)
                {
                    if (!uninstallComp.SUPPORTS_UNINSTALL()) // Not all components support uninstall * although they should, we should allow for this contigency                                            
                        continue;

                    ComponentConfig.Component component = uninstallComp.GetComponent();
                    if (uninstallComp.BEFORE_UNINSTALLING_COMPONENT())
                    {
                        Common.Log.Info(String.Format("About to Uninstall Component {0} with Version {1}.", component.UniqueLabel, component.Version));
                        bool bUninstallSuccess = uninstallComp.UNINSTALL_COMPONENT();
                        if (!bUninstallSuccess)
                            bErrorsOccured = true;

                        bUninstallSuccess = bUninstallSuccess && uninstallComp.AFTER_UNINSTALLING_COMPONENT();
                        if (bUninstallSuccess)
                        {
                            Common.Log.Info(String.Format("Component {0} with version {1} was uninstalled.", component.UniqueLabel, component.Version));
                            successfullyUninstalledComponents.Add(component);
                        }
                        else
                        {
                            string msg = String.Format("Component {0} with version {1} was not uninstalled.", component.UniqueLabel, component.Version);
                            Common.Log.Error(msg);
                        }
                    }
                }
                // # End Uninstall

                // Remove any uninstalled components from the Installed Configuration
                foreach (ComponentConfig.Component component in successfullyUninstalledComponents)
                         _ComponentMGR.GetAllInstalledComponentsW().RemoveComponent(component.UniqueLabel);

                // Write out the installed Configuration
                Common.WriteOutChangesToInstalledConfig();
            }
            
            Common.Log.Info(String.Format("Exiting Uninstall() for Type {0} with bErrorsOccured set to = {1}", _ComponentMGR.GetType().Name, bErrorsOccured));
            return !bErrorsOccured;
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Dispose of all extracted files
        /// </summary>
        public void Dispose()
        {
            Dispose(true);

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

        /// <summary>
        /// Make sure to call Dispose on the ComponentMGR for it to perform any cleanup
        /// </summary>
        /// <param name="disposing">if true, dispose ComponentMGR</param>
        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    _ComponentMGR.Dispose();
                    _ComponentMGR = default(T);
                }

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

        #endregion
    }
}
