//  Copyright (c) Microsoft Corporation.  All rights reserved.

namespace Microsoft.VisualStudio.TestTools.TestCaseMigrator
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Diagnostics;

    /// <summary>
    /// 
    /// Wizard Class reponsible for Navigation between Different Wizard Pages.
    /// 
    /// </summary>
    internal class WizardController : NotifyPropertyChange, IDisposable
    {
        #region Fields

        // Wizard Part responsible for all operation related to a wizard Page.
        private IWizardPart m_wizardPart;

        // All Information collected in the wizard needed for migration of the worktems.
        private WizardInfo m_wizardInfo;

        // Wizard State to Wizard Part Hash Table
        private IDictionary<WizardPage, IWizardPart> m_wizardPageHashTable;

        // An ordered list of Wizard States needed for sequential navgation of Wizard Pages
        private IList<WizardPage> m_wizardStates;

        private bool m_isMigrationCompleted;

        private bool m_isFinishListning;

        private string m_currentDirectory;

        private WizardPage m_loadWizardState;

        private bool m_updateCurrentWizardPageBeforeLoadingNext;

        protected BackgroundWorker m_worker;

        #endregion

        #region Constants

        public const string IsMigrationCompletedTag = "IsMigrationCompleted";
        private const int FieldsWizardPartPositionInWizardFlow = 4;

        #endregion

        #region Constructor

        /// <summary>
        /// Constructor to initialize Wizard Info, Wizard Page Hash table and List of Wizard States.
        /// Also loads the first wizard page
        /// </summary>
        public WizardController()
        {
            m_wizardInfo = new WizardInfo();
            m_wizardInfo.PropertyChanged += new PropertyChangedEventHandler(WizardInfo_PropertyChanged);

            InitializeWizardHashTable();

            InitializeWizardStates();

            LoadWizardPart(WizardPage.Welcome, false);

            m_isFinishListning = true;

            // 
            m_currentDirectory = System.IO.Directory.GetCurrentDirectory();
        }

        #endregion

        #region Properties

        /// <summary>
        /// The index of Active Wizard part in list of Wizard Parts
        /// </summary>
        private int CurrentWizardIndex
        {
            get
            {
                return m_wizardStates.IndexOf(WizardPart.WizardPage);
            }
        }

        /// <summary>
        /// Collection of Wizard Parts
        /// </summary>
        public ObservableCollection<IWizardPart> WizardParts
        {
            get;
            set;
        }

        /// <summary>
        /// Can we finish the wizard configuration and take appropraite action based on the configuration
        /// </summary>
        public bool CanFinish
        {
            get
            {
                return m_wizardPageHashTable[WizardPage.ConfirmSettings].CanShow;
            }
        }

        /// <summary>
        /// Needed by The UI to know the Wizard Page to show.
        /// </summary>
        public IWizardPart WizardPart
        {
            get
            {
                return m_wizardPart;
            }
            private set
            {
                if (m_wizardPart != null)
                {
                    m_wizardPart.IsActiveWizardPart = false;
                }

                value.IsActiveWizardPart = true;
                m_wizardPart = value;
                NotifyPropertyChanged("WizardPart");
            }
        }

        /// <summary>
        /// Wizard Information
        /// </summary>
        public WizardInfo WizardInfo
        {
            get
            {
                return m_wizardInfo;
            }
        }

        /// <summary>
        /// Can we Save the Wizard Configuration
        /// </summary>
        public bool CanSave
        {
            get
            {
                return !string.IsNullOrEmpty(m_wizardInfo.OutputSettingsFilePath);
            }
        }

        /// <summary>
        /// Is Wizard Part ressponsibel for Wizard Actions is active?
        /// </summary>
        public bool IsSummaryPage
        {
            get
            {
                return WizardPart != null && WizardPart.WizardPage == WizardPage.MigrationProgress;
            }
        }

        /// <summary>
        /// Is confirm Wizard Configuration Wizard part Active?
        /// </summary>
        public bool IsConfirmPage
        {
            get
            {
                return WizardPart != null && WizardPart.WizardPage == WizardPage.ConfirmSettings;
            }
        }

        /// <summary>
        /// Is Finish Button visible
        /// </summary>
        public bool IsFinishVisible
        {
            get
            {
                return !(IsSummaryPage || IsConfirmPage);
            }
        }

        /// <summary>
        /// Are Wizard Actions Done?
        /// </summary>
        public bool IsMigrationCompleted
        {
            get
            {
                return m_isMigrationCompleted;
            }
            set
            {
                m_isMigrationCompleted = value;
                NotifyPropertyChanged(IsMigrationCompletedTag);
            }
        }

        #endregion

        #region public methods

        /// <summary>
        /// Shows the Help
        /// </summary>
        public void ShowHelp()
        {
            try
            {
                Process.Start(m_currentDirectory + "\\TestCaseMigrator(Excel-MHT)-Readme.doc");
            }
            catch (Win32Exception)
            {
                MessageHelper.ShowError("Unable to show help document",
                                        "Unable to open " + m_currentDirectory + "\\TestCaseMigrator(Excel-MHT)-Readme.doc",
                                        "Please verify that Help document exists there");
            }
        }

        /// <summary>
        /// Load the Previous Wizard Part
        /// </summary>
        public void GoBack()
        {
            if (CurrentWizardIndex > 0)
            {
                LoadWizardPart(m_wizardStates[CurrentWizardIndex - 1], false);
            }
        }

        /// <summary>
        /// Loads the Next Wizard Part
        /// </summary>
        public void GoNext()
        {
            LoadWizardPart(m_wizardStates[CurrentWizardIndex + 1], true);
        }

        /// <summary>
        /// Finishes the Wizard and Loads the last Wizard Part which is also responsible for the migration of Workitems
        /// </summary>
        public void SaveAndMigrate(bool checkCurrentPartState)
        {
            LoadWizardPart(WizardPage.MigrationProgress, checkCurrentPartState);

            if (WizardPart.WizardPage == WizardPage.MigrationProgress)
            {
                MigrationProgressPart part = WizardPart as MigrationProgressPart;
            }
        }

        /// <summary>
        /// Save Wizard State
        /// </summary>
        public void Save()
        {
            using (new AutoWaitCursor())
            {
                WizardAction saveSettings = new SaveSettingsAction(m_wizardInfo);
                saveSettings.Start();
            }
        }

        /// <summary>
        /// Load Confirm Settings Wizard part
        /// </summary>
        public void LoadConfirmPage()
        {
            LoadWizardPart(WizardPage.ConfirmSettings, true);
        }

        /// <summary>
        /// Load Wizard Part
        /// </summary>
        /// <param name="part"></param>
        public void LoadWizardPart(IWizardPart part)
        {
            int newIndex = m_wizardStates.IndexOf(part.WizardPage);
            bool hasStateToBeUpdated = newIndex > CurrentWizardIndex;

            LoadWizardPart(part.WizardPage, hasStateToBeUpdated);
        }

        /// <summary>
        /// Stop Wizard Actions in progress
        /// </summary>
        public void StopWizardActions()
        {
            MigrationProgressPart part = WizardPart as MigrationProgressPart;
            if (part.IsActiveWizardPart)
            {
                part.StopAll();
            }
        }

        #endregion

        #region private methods

        /// <summary>
        /// Initializes the Wiard State  to Wizard Part hash Table
        /// </summary>
        private void InitializeWizardHashTable()
        {
            m_wizardPageHashTable = m_wizardInfo.WizardPageHashTable;
            m_wizardPageHashTable.Add(WizardPage.Welcome, new WelcomePart());
            m_wizardPageHashTable.Add(WizardPage.SelectDataSource, new SelectDataSourcePart());
            m_wizardPageHashTable.Add(WizardPage.SelectDestinationServer, new SelectDestinationServerPart());
            m_wizardPageHashTable.Add(WizardPage.SettingsFile, new SettingsFilePart());
            m_wizardPageHashTable.Add(WizardPage.FieldsSelection, new FieldsSelectionPart());
            m_wizardPageHashTable.Add(WizardPage.FieldMapping, new FieldMappingPart());
            m_wizardPageHashTable.Add(WizardPage.DataMapping, new DataMappingPart());
            m_wizardPageHashTable.Add(WizardPage.MiscSettings, new MiscSettingsPart());
            m_wizardPageHashTable.Add(WizardPage.ConfirmSettings, new ConfirmSettingsPart());
            m_wizardPageHashTable.Add(WizardPage.MigrationProgress, new MigrationProgressPart());

            WizardParts = new ObservableCollection<IWizardPart>();
            foreach (IWizardPart part in m_wizardPageHashTable.Values)
            {
                if (part.WizardPage != WizardPage.MigrationProgress)
                {
                    WizardParts.Add(part);
                }
                part.PropertyChanged += new PropertyChangedEventHandler(WizardPart_PropertyChanged);
            }
        }

        /// <summary>
        /// Initializes the List of Wizard States
        /// </summary>
        private void InitializeWizardStates()
        {
            m_wizardStates = new List<WizardPage>
            { 
                WizardPage.Welcome,
                WizardPage.SelectDataSource,
                WizardPage.SelectDestinationServer,
                WizardPage.SettingsFile,
                WizardPage.FieldsSelection,
                WizardPage.FieldMapping,
                WizardPage.DataMapping,
                WizardPage.MiscSettings,
                WizardPage.ConfirmSettings,
                WizardPage.MigrationProgress
            };
        }

        /// <summary>
        /// Listens to the Wizard part's notifications
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void WizardPart_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            // MigrateWorkitem Part's State notification
            if (e.PropertyName == MigrationProgressPart.MigrationStateTag)
            {
                MigrationProgressPart part = sender as MigrationProgressPart;
                if (part.MigrationState != WizardActionState.InProgress)
                {
                    IsMigrationCompleted = true;
                }
            }
            // Wizard Part's Canfinish Notification
            else if (m_isFinishListning && e.PropertyName == BaseWizardPart.CanFinishPropertyName)
            {
                m_isFinishListning = false;
                // If Wizard part can finish
                if (WizardPart.CanFinish)
                {
                    // then Set CanShow of all Visible Part as true
                    foreach (IWizardPart part in m_wizardPageHashTable.Values)
                    {
                        part.CanShow = true;
                    }
                }
                else
                {
                    // else resets their CanShow
                    CanShowOtherWizardParts();
                }

                //Send Notifications
                NotifyPropertyChanged("CanFinish");
                m_isFinishListning = true;
            }
        }

        /// <summary>
        /// Loads Wizard Part for corresponding Wizard State. Also checks whether to update current wizard part or not.
        /// </summary>
        /// <param name="wizardState"></param>
        /// <param name="updateCurrentWizardState"></param>
        /// <returns></returns>
        private void LoadWizardPart(WizardPage wizardState, bool updateCurrentWizardState)
        {
            m_loadWizardState = wizardState;
            m_updateCurrentWizardPageBeforeLoadingNext = updateCurrentWizardState;

            m_worker = new BackgroundWorker();

            // Setting the event handlers of background worker
            m_worker.DoWork += new DoWorkEventHandler(LoadWizardPartInBackgroundThread);
            m_worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Worker_LoadWizardPartInBackgroundThreadCompleted);
            m_worker.RunWorkerAsync(null);
        }

        [STAThread]
        private void LoadWizardPartInBackgroundThread(object sender, DoWorkEventArgs e)
        {
            IWizardPart previousPart = WizardPart;
            MessageEventArgs args = null;
            using (new AutoWaitCursor())
            {
                if (!m_updateCurrentWizardPageBeforeLoadingNext || WizardPart.UpdateWizardPart())
                {
                    IWizardPart part = m_wizardPageHashTable[m_loadWizardState];

                    WizardPart = part;

                    // trying to initialize the Wizard part
                    part.Initialize(m_wizardInfo);

                    // if initializaton was successfull and we can show this wizard part
                    if (part.CanShow)
                    {
                        // Disable Navigations if this is last(summary) page
                        DisableNavigationIfLastPage();

                        // Checking whether w can show other wizard pages
                        CanShowOtherWizardParts();

                        // Send notifications
                        NotifyPropertyChanged("CanSave");
                        NotifyPropertyChanged("IsSummaryPage");
                        NotifyPropertyChanged("IsConfirmPage");
                        NotifyPropertyChanged("IsFinishVisible");
                        NotifyPropertyChanged("CanFinish");
                    }
                    else
                    {
                        // else if unable to Initiale the WizardPart then restore back to last WizardPart
                        // Also get the error args 
                        WizardPart.CanShow = true;
                        args = WizardPart.ErrorArgs;
                        WizardPart = previousPart;
                    }
                }
                else
                {
                    // There is some error in updating the current wizardPart. get the error args
                    args = WizardPart.ErrorArgs;
                }
            }

            // if some error occured then display it
            if (args != null && MessageHelper.ShowMessageWindow != null)
            {
                MessageHelper.ShowMessageWindow(args);
            }
        }

        private void Worker_LoadWizardPartInBackgroundThreadCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            // If any exception is thrown during the work then sets the state as failed and set the corresponding message.
            if (e.Error != null)
            {
                throw e.Error;
            }
            m_worker.Dispose();
        }

        /// <summary>
        /// Disables Navigation to other Wizard parts if this is last Wizard Part
        /// </summary>
        private void DisableNavigationIfLastPage()
        {
            if (WizardPart.WizardPage == WizardPage.MigrationProgress)
            {
                for (int i = 0; i < WizardParts.Count; i++)
                {
                    WizardParts[i].CanShow = false;
                }
            }
        }

        /// <summary>
        /// Checks whether we can show other wizard Parts or not
        /// </summary>
        private void CanShowOtherWizardParts()
        {
            for (int i = CurrentWizardIndex + 1; i < m_wizardStates.Count; i++)
            {
                IWizardPart part = m_wizardPageHashTable[m_wizardStates[i]];

                if (WizardPart.CanFinish)
                {
                    part.CanShow = true;
                }
                else if (!part.CanInitialize)
                {
                    // Disable all subsequent wizard pages
                    for (int j = i; j < m_wizardStates.Count; j++)
                    {
                        m_wizardPageHashTable[m_wizardStates[j]].CanShow = false;
                    }
                    break;
                }
            }
            // Always set CanShow of nxt Wizard page as true
            if (CurrentWizardIndex < m_wizardStates.Count - 1)
            {
                m_wizardPageHashTable[m_wizardStates[CurrentWizardIndex + 1]].CanShow = true;
            }
        }

        private void WizardInfo_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "DataSourceType")
            {
                if (m_wizardInfo.DataSourceType == DataSourceType.MHT)
                {
                    if (!m_wizardStates.Contains(WizardPage.FieldsSelection))
                    {
                        App.CallMethodInUISynchronizationContext(AddFieldsWizardPart, null);
                    }
                }
                else if (m_wizardInfo.DataSourceType == DataSourceType.Excel)
                {
                    if (m_wizardStates.Contains(WizardPage.FieldsSelection))
                    {
                        App.CallMethodInUISynchronizationContext(RemoveFieldsWizardPart, null);
                    }
                }
            }
        }

        private void RemoveFieldsWizardPart(object obj)
        {
            m_wizardStates.Remove(WizardPage.FieldsSelection);
            WizardParts.Remove(m_wizardPageHashTable[WizardPage.FieldsSelection]);
        }

        private void AddFieldsWizardPart(object page)
        {
            m_wizardStates.Insert(FieldsWizardPartPositionInWizardFlow, WizardPage.FieldsSelection);
            WizardParts.Insert(FieldsWizardPartPositionInWizardFlow, m_wizardPageHashTable[WizardPage.FieldsSelection]);
        }

        #endregion

        #region IDisposible Implementation

        /// <summary>
        /// Disposes the Data Source
        /// </summary>
        public void Dispose()
        {
            m_wizardInfo.Dispose();
            ExcelParser.Quit();
            MHTParser.Quit();
        }

        #endregion
    }
}