using System;
using System.Linq;

using UnityEditor;
using UnityEngine;

using Codice.Client.BaseCommands;
using Codice.Client.BaseCommands.EventTracking;
using Codice.Client.Common;
using Codice.Client.Common.FsNodeReaders.Watcher;
using Codice.Client.Common.Threading;
using Codice.CM.Common;
using Codice.LogWrapper;
using GluonGui;
using PlasticGui;
using PlasticGui.WorkspaceWindow.NotificationBar;
using Unity.PlasticSCM.Editor.AssetMenu;
using Unity.PlasticSCM.Editor.AssetUtils;
using Unity.PlasticSCM.Editor.Configuration;
using Unity.PlasticSCM.Editor.Configuration.CloudEdition.Welcome;
using Unity.PlasticSCM.Editor.Inspector;
using Unity.PlasticSCM.Editor.Tool;
using Unity.PlasticSCM.Editor.UI;
using Unity.PlasticSCM.Editor.UI.Avatar;
using Unity.PlasticSCM.Editor.UI.Progress;
using Unity.PlasticSCM.Editor.UI.StatusBar;
using Unity.PlasticSCM.Editor.Views.CreateWorkspace;
using Unity.PlasticSCM.Editor.Views.Welcome;
using Unity.PlasticSCM.Editor.WebApi;

using GluonCheckIncomingChanges = PlasticGui.Gluon.WorkspaceWindow.CheckIncomingChanges;
using GluonNewIncomingChangesUpdater = PlasticGui.Gluon.WorkspaceWindow.NewIncomingChangesUpdater;
using PlasticAssetModificationProcessor = Unity.PlasticSCM.Editor.AssetUtils.Processor.AssetModificationProcessor;

namespace Unity.PlasticSCM.Editor
{
    /// <summary>
    /// The Plastic SCM window.
    /// </summary>
    public class PlasticWindow : EditorWindow,
        PlasticGui.WorkspaceWindow.CheckIncomingChanges.IAutoRefreshIncomingChangesView,
        GluonCheckIncomingChanges.IAutoRefreshIncomingChangesView,
        CreateWorkspaceView.ICreateWorkspaceListener
    {
        internal WorkspaceWindow WorkspaceWindowForTesting { get { return mWorkspaceWindow; } }
        internal ViewSwitcher ViewSwitcherForTesting { get { return mViewSwitcher; } }
        internal CmConnection CmConnectionForTesting { get { return CmConnection.Get(); } }

        /// <summary>
        /// Open the Plastic SCM window.
        /// </summary>
        public static void Open()
        {
            ShowWindow.Plastic();
        }

        internal void SetupWindowTitle(PlasticNotification.Status status)
        {
            Texture windowIcon = PlasticNotification.GetIcon(status);

            // The titleContent icon does not update unless we also update the title text
            // Temporarily doing it by adding space characters
            string title = UnityConstants.PLASTIC_WINDOW_TITLE;
            title += String.Concat(Enumerable.Repeat(" ", (int)status));

            titleContent = new GUIContent(title, windowIcon);
        }

        internal void DisableCollabIfEnabledWhenLoaded()
        {
            mDisableCollabIfEnabledWhenLoaded = true;
        }

        internal void ShowPendingChanges()
        {
            ((IAssetMenuOperations)mAssetOperations).ShowPendingChanges();
        }

        internal void Add()
        {
            ((IAssetMenuOperations)mAssetOperations).Add();
        }

        internal void Checkout()
        {
            ((IAssetMenuOperations)mAssetOperations).Checkout();
        }

        internal void Checkin()
        {
            ((IAssetMenuOperations)mAssetOperations).Checkin();
        }

        internal void Undo()
        {
            ((IAssetMenuOperations)mAssetOperations).Undo();
        }

        internal void ShowDiff()
        {
            ((IAssetMenuOperations)mAssetOperations).ShowDiff();
        }

        internal void ShowHistory()
        {
            ((IAssetMenuOperations)mAssetOperations).ShowHistory();
        }

        internal void AddFilesFilterPatterns(
            FilterTypes type, 
            FilterActions action, 
            FilterOperationType operation)
        {
            ((IAssetFilesFilterPatternsMenuOperations)mAssetOperations)
                .AddFilesFilterPatterns(type, action, operation);
        }

        void PlasticGui.WorkspaceWindow.CheckIncomingChanges.IAutoRefreshIncomingChangesView.IfVisible()
        {
            mViewSwitcher.AutoRefreshIncomingChangesView();
        }

        void GluonCheckIncomingChanges.IAutoRefreshIncomingChangesView.IfVisible()
        {
            mViewSwitcher.AutoRefreshIncomingChangesView();
        }

        void CreateWorkspaceView.ICreateWorkspaceListener.OnWorkspaceCreated(
            WorkspaceInfo wkInfo, bool isGluonMode)
        {
            mWkInfo = wkInfo;
            mIsGluonMode = isGluonMode;
            mWelcomeView = null;

            PlasticPlugin.Enable();

            if (mIsGluonMode)
                ConfigurePartialWorkspace.AsFullyChecked(mWkInfo);

            InitializePlastic();
            Repaint();
        }

        internal void RefreshWorkspaceUI()
        {
            InitializePlastic();
            Repaint();

            OnFocus();
        }

        void OnEnable()
        {
            PlasticPlugin.Enable();

            wantsMouseMove = true;

            if (mException != null)
                return;

            minSize = new Vector2(
                UnityConstants.PLASTIC_WINDOW_MIN_SIZE_WIDTH,
                UnityConstants.PLASTIC_WINDOW_MIN_SIZE_HEIGHT);

            SetupWindowTitle(PlasticNotification.Status.None);

            RegisterApplicationFocusHandlers(this);

            InitializePlastic();
        }

        void OnDisable()
        {
            // We need to disable FSWatcher because otherwise it hangs
            // when you move the window between monitors with different scale
            MonoFileSystemWatcher.IsEnabled = false;

            if (mException != null)
                return;

            if (mWkInfo == null)
            {
                ClosePlasticWindow(this);
                return;
            }

            mViewSwitcher.OnDisable();

            ClosePlasticWindow(this);
        }

        void OnDestroy()
        {
            if (mException != null)
                return;

            if (mWkInfo == null)
                return;

            if (!mWorkspaceWindow.IsOperationInProgress())
                return;

            bool bCloseWindow = GuiMessage.ShowQuestion(
                PlasticLocalization.GetString(PlasticLocalization.Name.OperationRunning),
                PlasticLocalization.GetString(PlasticLocalization.Name.ConfirmClosingRunningOperation),
                PlasticLocalization.GetString(PlasticLocalization.Name.YesButton));

            if (bCloseWindow)
                return;

            mForceToOpen = true;
            ShowPlasticWindow(this);
        }

        void OnFocus()
        {
            if (mException != null)
                return;

            if (mWkInfo == null)
                return;

            // We don't want to auto-refresh the views when the window
            // is focused due to a right mouse button click because
            // if there is no internet connection a dialog appears and
            // it prevents being able to open the context menu in order
            // to close the Plastic SCM window
            if (Mouse.IsRightMouseButtonPressed(Event.current))
                return;

            mViewSwitcher.AutoRefreshPendingChangesView();
            mViewSwitcher.AutoRefreshIncomingChangesView();
        }

        void OnGUI()
        {
            if (mException != null)
            {
                DoExceptionErrorArea();
                return;
            }

            try
            {
                // IMPORTANT: disable collab (if needed)
                // must be executed before the next if statement
                // where we check if collab is enabled
                if (mDisableCollabIfEnabledWhenLoaded)
                {
                    mDisableCollabIfEnabledWhenLoaded = false;
                    DisableCollabIfEnabled(ProjectPath.FromApplicationDataPath(
                        Application.dataPath));
                }

                if (CollabPlugin.IsEnabled())
                {
                    // execute Close() once after all inspectors update
                    // to avoid our window to be drawn in back color
                    EditorApplication.delayCall = Close;
                    return;
                }

                bool isPlasticExeAvailable = IsExeAvailable.ForMode(mIsGluonMode);
                bool clientNeedsConfiguration = UnityConfigurationChecker.NeedsConfiguration();

                var welcomeView = GetWelcomeView();

                if (clientNeedsConfiguration && welcomeView.autoLoginState == AutoLogin.State.Off)
                {
                    welcomeView.autoLoginState = AutoLogin.State.Started;
                }

                if (welcomeView.autoLoginState == AutoLogin.State.OrganizationChoosed)
                {
                    OnEnable();
                    welcomeView.autoLoginState = AutoLogin.State.InitializingPlastic;
                }

                if (NeedsToDisplayWelcomeView(clientNeedsConfiguration, mWkInfo))
                {
                    welcomeView.OnGUI(clientNeedsConfiguration);
                    return;
                }

                //TODO: Codice - beta: hide the switcher until the update dialog is implemented
                //DrawGuiModeSwitcher.ForMode(
                //    isGluonMode, plasticClient, changesTreeView, editorWindow);

                DoTabToolbar(
                    isPlasticExeAvailable,
                    mWkInfo,
                    mViewSwitcher,
                    mIsGluonMode);

                mViewSwitcher.TabViewGUI();

                if (mWorkspaceWindow.IsOperationInProgress())
                    DrawProgressForOperations.For(
                        mWorkspaceWindow, mWorkspaceWindow.Progress,
                        position.width);

                mStatusBar.OnGUI(
                    mWkInfo,
                    mWorkspaceWindow,
                    mViewSwitcher,
                    mViewSwitcher,
                    mIncomingChangesNotifier,
                    mIsGluonMode);
            }
            catch (Exception ex)
            {
                if (IsExitGUIException(ex))
                    throw;

                GUI.enabled = true;

                if (IsIMGUIPaintException(ex))
                {
                    ExceptionsHandler.LogException("PlasticWindow", ex);
                    return;
                }

                mException = ex;

                DoExceptionErrorArea();

                ExceptionsHandler.HandleException("OnGUI", ex);
            }
        }

        void Update()
        {
            if (mException != null)
                return;

            if (mWkInfo == null)
                return;

            try
            {
                double currentUpdateTime = EditorApplication.timeSinceStartup;
                double elapsedSeconds = currentUpdateTime - mLastUpdateTime;

                mViewSwitcher.Update();
                mWorkspaceWindow.OnParentUpdated(elapsedSeconds);

                if (mWelcomeView != null)
                    mWelcomeView.Update();

                mLastUpdateTime = currentUpdateTime;
            }
            catch (Exception ex)
            {
                mException = ex;

                ExceptionsHandler.HandleException("Update", ex);
            }
        }

        internal void InitializePlastic()
        {
            if (mForceToOpen)
            {
                mForceToOpen = false;
                return;
            }

            try
            {
                if (UnityConfigurationChecker.NeedsConfiguration())
                    return;

                mWkInfo = FindWorkspace.InfoForApplicationPath(
                    Application.dataPath, PlasticApp.PlasticAPI);

                if (mWkInfo == null)
                    return;

                PlasticPlugin.EnableForWorkspace();

                SetupCloudProjectIdIfNeeded(mWkInfo, PlasticApp.PlasticAPI);

                DisableVCSIfEnabled(mWkInfo.ClientPath);

                mIsGluonMode = PlasticApp.PlasticAPI.IsGluonWorkspace(mWkInfo);

                InitializeNewIncomingChanges(mWkInfo, mIsGluonMode);

                ViewHost viewHost = new ViewHost();

                mStatusBar = new StatusBar();

                mViewSwitcher = new ViewSwitcher(
                    mWkInfo,
                    viewHost,
                    mIsGluonMode,
                    mDeveloperNewIncomingChangesUpdater,
                    mGluonNewIncomingChangesUpdater,
                    mIncomingChangesNotifier,
                    PlasticPlugin.AssetStatusCache,
                    PlasticPlugin.WorkspaceOperationsMonitor,
                    mStatusBar,
                    this);

                mCooldownAutoRefreshPendingChangesAction = new CooldownWindowDelayer(
                    mViewSwitcher.AutoRefreshPendingChangesView,
                    UnityConstants.AUTO_REFRESH_PENDING_CHANGES_DELAYED_INTERVAL);

                mWorkspaceWindow = new WorkspaceWindow(
                    mWkInfo,
                    viewHost,
                    mViewSwitcher,
                    mViewSwitcher,
                    mDeveloperNewIncomingChangesUpdater,
                    this);

                mViewSwitcher.SetWorkspaceWindow(mWorkspaceWindow);
                mViewSwitcher.ShowInitialView();

                PlasticPlugin.WorkspaceOperationsMonitor.RegisterWindow(
                    mWorkspaceWindow,
                    viewHost,
                    mDeveloperNewIncomingChangesUpdater);

                UnityStyles.Initialize(Repaint);

                AssetOperations.IAssetSelection projectViewAssetSelection =
                    new ProjectViewAssetSelection();

                mAssetOperations =
                    new AssetOperations(
                        mWkInfo,
                        mWorkspaceWindow,
                        mViewSwitcher,
                        mViewSwitcher,
                        viewHost,
                        mDeveloperNewIncomingChangesUpdater,
                        PlasticPlugin.AssetStatusCache,
                        mViewSwitcher,
                        mViewSwitcher,
                        this,
                        projectViewAssetSelection,
                        mIsGluonMode);

                DrawInspectorOperations.BuildOperations(
                    mWkInfo,
                    mWorkspaceWindow,
                    mViewSwitcher,
                    mViewSwitcher,
                    viewHost,
                    mDeveloperNewIncomingChangesUpdater,
                    mViewSwitcher,
                    mViewSwitcher,
                    this,
                    mIsGluonMode);

                mLastUpdateTime = EditorApplication.timeSinceStartup;

                mViewSwitcher.ShowBranchesViewIfNeeded();

                if (!EditionToken.IsCloudEdition())
                    return;

                InitializeNotificationBarUpdater(
                    mWkInfo, mStatusBar.NotificationBar);
            }
            catch (Exception ex)
            {
                mException = ex;

                ExceptionsHandler.HandleException("InitializePlastic", ex);
            }
        }

        void InitializeNewIncomingChanges(
            WorkspaceInfo wkInfo,
            bool bIsGluonMode)
        {
            if (bIsGluonMode)
            {
                Gluon.IncomingChangesNotifier gluonNotifier =
                    new Gluon.IncomingChangesNotifier(this);
                mGluonNewIncomingChangesUpdater =
                    NewIncomingChanges.BuildUpdaterForGluon(
                        wkInfo,
                        this,
                        gluonNotifier,
                        new GluonCheckIncomingChanges.CalculateIncomingChanges());
                mIncomingChangesNotifier = gluonNotifier;
                return;
            }

            Developer.IncomingChangesNotifier developerNotifier =
                new Developer.IncomingChangesNotifier(this);
            mDeveloperNewIncomingChangesUpdater =
                NewIncomingChanges.BuildUpdaterForDeveloper(
                    wkInfo, this, developerNotifier);
            mIncomingChangesNotifier = developerNotifier;
        }

        void InitializeNotificationBarUpdater(
            WorkspaceInfo wkInfo,
            INotificationBar notificationBar)
        {
            mNotificationBarUpdater = new NotificationBarUpdater(
                notificationBar,
                PlasticApp.PlasticWebRestApi,
                new UnityPlasticTimerBuilder(),
                new NotificationBarUpdater.NotificationBarConfig(),
                ScreenResolution.Get());
            mNotificationBarUpdater.Start();
            mNotificationBarUpdater.SetWorkspace(wkInfo);
        }

        void OnApplicationActivated()
        {
            if (mException != null)
                return;

            Reload.IfWorkspaceConfigChanged(
                PlasticApp.PlasticAPI, mWkInfo, mIsGluonMode,
                ExecuteFullReload);

            if (mWkInfo == null)
                return;

            ((IWorkspaceWindow)mWorkspaceWindow).UpdateTitle();

            NewIncomingChanges.LaunchUpdater(
                mDeveloperNewIncomingChangesUpdater,
                mGluonNewIncomingChangesUpdater);

            // When Unity Editor window is activated it writes some files to its Temp folder.
            // This causes the fswatcher to process those events.
            // We need to wait until the fswatcher finishes processing the events,
            // otherwise the NewChangesInWk method will return TRUE, causing
            // the pending changes view to unwanted auto-refresh.
            // So, we need to delay the auto-refresh call in order
            // to give the fswatcher enough time to process the events.
            // Note that the OnFocus event is not affected by this issue.
            mCooldownAutoRefreshPendingChangesAction.Ping();

            mViewSwitcher.AutoRefreshIncomingChangesView();
        }

        void OnApplicationDeactivated()
        {
            if (mException != null)
                return;

            if (mWkInfo == null)
                return;

            NewIncomingChanges.StopUpdater(
                mDeveloperNewIncomingChangesUpdater,
                mGluonNewIncomingChangesUpdater);
        }

        void ExecuteFullReload()
        {
            mException = null;

            DisposeNewIncomingChanges(this);

            DisposeNotificationBarUpdater(this);

            InitializePlastic();
        }

        void DoExceptionErrorArea()
        {
            string labelText = PlasticLocalization.GetString(
                PlasticLocalization.Name.UnexpectedError);

            string buttonText = PlasticLocalization.GetString(
                PlasticLocalization.Name.ReloadButton);

            DrawActionHelpBox.For(
                Images.GetErrorDialogIcon(), labelText, buttonText,
                ExecuteFullReload);
        }

        internal WelcomeView GetWelcomeView()
        {
            if (mWelcomeView != null)
                return mWelcomeView;

            mWelcomeView = new WelcomeView(
                this,
                this,
                PlasticApp.PlasticAPI,
                CmConnection.Get(),
                PlasticApp.PlasticWebRestApi);

            return mWelcomeView;
        }

        static void DoSearchField(ViewSwitcher viewSwitcher)
        {
            if (viewSwitcher.IsViewSelected(ViewSwitcher.SelectedTab.PendingChanges))
            {
                viewSwitcher.PendingChangesTab.DrawSearchFieldForPendingChangesTab();
                return;
            }
            if (viewSwitcher.IsViewSelected(ViewSwitcher.SelectedTab.Changesets))
            {
                viewSwitcher.ChangesetsTab.DrawSearchFieldForChangesetsTab();
                return;
            }
            if (viewSwitcher.IsViewSelected(ViewSwitcher.SelectedTab.History))
            {
                viewSwitcher.HistoryTab.DrawSearchFieldForHistoryTab();
                return;
            }
            if (viewSwitcher.IsViewSelected(ViewSwitcher.SelectedTab.Branches))
            {
                viewSwitcher.BranchesTab.DrawSearchFieldForBranchesTab();
                return;
            }
        }

        static void DoTabToolbar(
            bool isPlasticExeAvailable,
            WorkspaceInfo workspaceInfo,
            ViewSwitcher viewSwitcher,
            bool isGluonMode)
        {
            EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);

            viewSwitcher.TabButtonsGUI();

            GUILayout.FlexibleSpace();

            DoSearchField(viewSwitcher);

            DoLaunchButtons(
                isPlasticExeAvailable,
                workspaceInfo,
                viewSwitcher,
                isGluonMode);

            EditorGUILayout.EndHorizontal();
        }

        static void OpenBranchListViewAndSendEvent(
            WorkspaceInfo wkInfo,
            ViewSwitcher viewSwitcher)
        {
            viewSwitcher.ShowBranchesView();
            TrackFeatureUseEvent.For(
               PlasticGui.Plastic.API.GetRepositorySpec(wkInfo),
               TrackFeatureUseEvent.Features.OpenBranchesView);
        }

        static void ShowBranchesContextMenu(
             WorkspaceInfo wkInfo,
            ViewSwitcher viewSwitcher,
            bool isGluonMode)
        {
            GenericMenu menu = new GenericMenu();

            string branchesListView = PlasticLocalization.GetString(
                PlasticLocalization.Name.Branches);

            menu.AddItem(
               new GUIContent(branchesListView),
               false,
               () => OpenBranchListViewAndSendEvent(wkInfo, viewSwitcher));

            string branchExplorer = PlasticLocalization.GetString(
            PlasticLocalization.Name.BranchExplorerMenu);

            menu.AddItem(
              new GUIContent(branchExplorer),
              false,
              () => LaunchTool.OpenBranchExplorer(wkInfo, isGluonMode));

            menu.ShowAsContext();
        }

        static void ShowSettingsContextMenu(
            WorkspaceInfo wkInfo,
            bool isGluonMode)
        {
            GenericMenu menu = new GenericMenu();

            string openToolText = isGluonMode ?
                PlasticLocalization.GetString(PlasticLocalization.Name.LaunchGluonButton) :
                PlasticLocalization.GetString(PlasticLocalization.Name.LaunchPlasticButton);

            menu.AddItem(
               new GUIContent(openToolText),
               false,
               () => LaunchTool.OpenGUIForMode(wkInfo, isGluonMode));

            if (EditionToken.IsCloudEdition())
            {
                menu.AddItem(new GUIContent(
                    PlasticLocalization.GetString(
                       PlasticLocalization.Name.InviteMembers)),
                false,
               InviteMemberButton_clicked,
               PlasticGui.Plastic.API.GetRepositorySpec(wkInfo));
            }

            menu.AddSeparator("");

            menu.AddItem(
                new GUIContent(
                    PlasticLocalization.GetString(
                PlasticLocalization.Name.Options)),
                false,
                () => SettingsService.OpenProjectSettings(UnityConstants.PROJECT_SETTINGS_MENU_TITLE));

            // If the user has the simplified UI key of type .txt in the Assets folder
            // TODO: Remove when Simplified UI is complete
            if (AssetDatabase.FindAssets("simplifieduikey t:textasset", new[] { "Assets" }).Any())
                menu.AddItem(new GUIContent("Try Simplified UI"),
                    false,
                    TrySimplifiedUIButton_Clicked,
                    null);

            //TODO: Localization
            menu.AddItem(
                new GUIContent(PlasticAssetModificationProcessor.ForceCheckout ?
                PlasticLocalization.GetString(PlasticLocalization.Name.DisableForcedCheckout) :
                PlasticLocalization.GetString(PlasticLocalization.Name.EnableForcedCheckout)),
                false,
                ForceCheckout_Clicked,
                null);

            menu.AddSeparator("");

            menu.AddItem(
                new GUIContent(
                    PlasticLocalization.GetString(
                        PlasticLocalization.Name.TurnOffPlasticSCM)),
                false,
                TurnOffPlasticButton_Clicked,
                null);

            menu.ShowAsContext();
        }

        static void DoLaunchButtons(
            bool isPlasticExeAvailable,
            WorkspaceInfo wkInfo,
            ViewSwitcher viewSwitcher,
            bool isGluonMode)
        {
            //TODO: Codice - beta: hide the diff button until the behavior is implemented
            /*GUILayout.Button(PlasticLocalization.GetString(
                PlasticLocalization.Name.DiffWindowMenuItemDiff),
                EditorStyles.toolbarButton,
                GUILayout.Width(UnityConstants.REGULAR_BUTTON_WIDTH));*/

            if (viewSwitcher.IsViewSelected(ViewSwitcher.SelectedTab.Changesets))
            {
                viewSwitcher.ChangesetsTab.DrawDateFilter();
            }
            if (viewSwitcher.IsViewSelected(ViewSwitcher.SelectedTab.Branches))
            {
                viewSwitcher.BranchesTab.DrawDateFilter();
            }

            Texture refreshIcon = Images.GetRefreshIcon();
            string refreshIconTooltip = PlasticLocalization.GetString(
                PlasticLocalization.Name.RefreshButton);

            if (DrawLaunchButton(refreshIcon, refreshIconTooltip))
            {
                viewSwitcher.RefreshSelectedView();
            }

            if (viewSwitcher.IsViewSelected(ViewSwitcher.SelectedTab.PendingChanges))
            {
                Texture2D icon = Images.GetUndoIcon();
                string tooltip = PlasticLocalization.GetString(
                    PlasticLocalization.Name.UndoSelectedChanges);

                if (DrawLaunchButton(icon, tooltip))
                {
                    TrackFeatureUseEvent.For(
                        PlasticGui.Plastic.API.GetRepositorySpec(wkInfo),
                        TrackFeatureUseEvent.Features.UndoIconButton);

                    viewSwitcher.PendingChangesTab.UndoForMode(wkInfo, isGluonMode);
                }
            }

            if (isGluonMode)
            {
                string label = PlasticLocalization.GetString(PlasticLocalization.Name.ConfigureGluon);
                if (DrawActionButton.For(label))
                    LaunchTool.OpenWorkspaceConfiguration(wkInfo, isGluonMode);
            }
            else
            {
                Texture2D icon = Images.GetBranchIcon();
                string tooltip = PlasticLocalization.GetString(PlasticLocalization.Name.Branches);
                if (DrawLaunchButton(icon, tooltip))
                {
                    ShowBranchesContextMenu(
                        wkInfo,
                        viewSwitcher,
                        isGluonMode);
                }
            }

            //TODO: Add settings button tooltip localization
            if (DrawLaunchButton(Images.GetSettingsIcon(), string.Empty))
            {
                ShowSettingsContextMenu(
                    wkInfo,
                    isGluonMode);
            }
        }

        static bool DrawLaunchButton(Texture icon, string tooltip)
        {
            return GUILayout.Button(
                new GUIContent(icon, tooltip),
                EditorStyles.toolbarButton, 
                GUILayout.Width(26));
        }

        static void InviteMemberButton_clicked(object obj)
        {
            RepositorySpec repSpec = (RepositorySpec)obj; 

            string organizationName = ServerOrganizationParser.
                GetOrganizationFromServer(repSpec.Server);

            CurrentUserAdminCheckResponse response = null;

            IThreadWaiter waiter = ThreadWaiter.GetWaiter(50);
            waiter.Execute(
                /*threadOperationDelegate*/
                delegate
                {
                    ServerProfile serverProfile = CmConnection.Get().
                        GetProfileManager().GetProfileForServer(repSpec.Server);

                    string authToken = serverProfile != null ?
                        CmConnection.Get().
                            BuildWebApiTokenForCloudEditionForUser(
                                serverProfile.Server, 
                                serverProfile.GetSEIDWorkingMode(), 
                                serverProfile.SecurityConfig):
                        CmConnection.Get().
                            BuildWebApiTokenForCloudEditionForUser(
                                repSpec.Server, 
                                ClientConfig.Get().GetSEIDWorkingMode(), 
                                ClientConfig.Get().GetSecurityConfig());

                    if (string.IsNullOrEmpty(authToken))
                        authToken = CmConnection.Get().
                            BuildWebApiTokenForCloudEditionDefaultUser();

                    if (string.IsNullOrEmpty(authToken))
                    {
                        return;
                    }

                    response = WebRestApiClient.PlasticScm.IsUserAdmin(
                        organizationName,
                        authToken);
                },
                /*afterOperationDelegate*/
                delegate
                {
                    if (waiter.Exception != null)
                    {
                        ExceptionsHandler.LogException(
                            "IsUserAdmin",
                            waiter.Exception);

                        OpenCloudDashboardUsersGroupsUrl(organizationName);
                        return;
                    }

                    if (response == null)
                    {
                        mLog.DebugFormat(
                            "Error checking if the user is the organization admin for {0}",
                            organizationName);

                        OpenCloudDashboardUsersGroupsUrl(organizationName);
                        return;
                    }

                   if (response.Error != null)
                    {
                        mLog.DebugFormat(
                          "Error checking if the user is the organization admin: {0}",
                          string.Format("Unable to get IsUserAdminResponse: {0} [code {1}]",
                              response.Error.Message,
                              response.Error.ErrorCode));

                        OpenCloudDashboardUsersGroupsUrl(organizationName);
                        return;
                    }
                  
                    if (response.IsCurrentUserAdmin)
                    {
                        OpenCloudDashboardUsersGroupsUrl(response.OrganizationName);
                        return;
        }

                    GuiMessage.ShowInformation(
                        PlasticLocalization.GetString(PlasticLocalization.Name.InviteMembersTitle),
                        PlasticLocalization.GetString(PlasticLocalization.Name.InviteMembersMessage));
                });
        }

        static void OpenCloudDashboardUsersGroupsUrl(string organization)
        {
            Application.OpenURL("https://www.plasticscm.com/dashboard/cloud/" +
                organization +
                "/users-and-groups");
        }

        static void TrySimplifiedUIButton_Clicked(object obj)
        {
            PlasticSCMWindow.ShowWindow();
        }

        static void TurnOffPlasticButton_Clicked(object obj)
        {
            ShowWindow.Plastic();
            TurnOffPlasticWindow.ShowWindow();
            PlasticPlugin.Disable();
        }

        static void ForceCheckout_Clicked(object obj)
        {
            PlasticAssetModificationProcessor.SetForceCheckoutOption(
                !PlasticAssetModificationProcessor.ForceCheckout);
        }

        static void SetupCloudProjectIdIfNeeded(
            WorkspaceInfo wkInfo,
            IPlasticAPI plasticApi)
        {
            if (SetupCloudProjectId.HasCloudProjectId())
                return;

            SetupCloudProjectId.ForWorkspace(wkInfo, plasticApi);

            mLog.DebugFormat("Setup CloudProjectId on Project: {0}",
                wkInfo.ClientPath);
        }

        static void DisableVCSIfEnabled(string projectPath)
        {
            if (!VCSPlugin.IsEnabled())
                return;

            VCSPlugin.Disable();

            mLog.DebugFormat("Disabled VCS Plugin on Project: {0}",
                projectPath);
        }

        static void DisposeNewIncomingChanges(PlasticWindow window)
        {
            NewIncomingChanges.DisposeUpdater(
                window.mDeveloperNewIncomingChangesUpdater,
                window.mGluonNewIncomingChangesUpdater);

            window.mDeveloperNewIncomingChangesUpdater = null;
            window.mGluonNewIncomingChangesUpdater = null;
        }

        static void DisposeNotificationBarUpdater(PlasticWindow window)
        {
            if (window.mNotificationBarUpdater == null)
                return;

            window.mNotificationBarUpdater.Dispose();
            window.mNotificationBarUpdater = null;
        }

        static void RegisterApplicationFocusHandlers(PlasticWindow window)
        {
            EditorWindowFocus.OnApplicationActivated += window.OnApplicationActivated;
            EditorWindowFocus.OnApplicationDeactivated += window.OnApplicationDeactivated;
        }

        static void UnRegisterApplicationFocusHandlers(PlasticWindow window)
        {
            EditorWindowFocus.OnApplicationActivated -= window.OnApplicationActivated;
            EditorWindowFocus.OnApplicationDeactivated -= window.OnApplicationDeactivated;
        }

        static bool IsExitGUIException(Exception ex)
        {
            return ex is ExitGUIException;
        }

        static bool IsIMGUIPaintException(Exception ex)
        {
            if (!(ex is ArgumentException))
                return false;

            return ex.Message.StartsWith("Getting control") &&
                   ex.Message.Contains("controls when doing repaint");
        }

        static void ClosePlasticWindow(PlasticWindow window)
        {
            UnRegisterApplicationFocusHandlers(window);

            PlasticPlugin.WorkspaceOperationsMonitor.UnRegisterWindow();

            DisposeNewIncomingChanges(window);

            DisposeNotificationBarUpdater(window);

            AvatarImages.Dispose();
        }

        static void ShowPlasticWindow(PlasticWindow window)
        {
            EditorWindow dockWindow = FindEditorWindow.ToDock<PlasticWindow>();

            PlasticWindow newPlasticWindow = InstantiateFrom(window);

            if (DockEditorWindow.IsAvailable())
                DockEditorWindow.To(dockWindow, newPlasticWindow);

            newPlasticWindow.Show();

            newPlasticWindow.Focus();
        }

        static bool NeedsToDisplayWelcomeView(
            bool clientNeedsConfiguration,
            WorkspaceInfo wkInfo)
        {
            if (clientNeedsConfiguration)
                return true;

            if (wkInfo == null)
                return true;

            return false;
        }

        static void DisableCollabIfEnabled(string projectPath)
        {
            if (!CollabPlugin.IsEnabled())
                return;

            CollabPlugin.Disable();

            mLog.DebugFormat("Disabled Collab Plugin on Project: {0}",
                projectPath);
        }

        static PlasticWindow InstantiateFrom(PlasticWindow window)
        {
            PlasticWindow result = Instantiate(window);
            result.mWkInfo = window.mWkInfo;
            result.mWorkspaceWindow = window.mWorkspaceWindow;
            result.mViewSwitcher = window.mViewSwitcher;
            result.mCooldownAutoRefreshPendingChangesAction = window.mCooldownAutoRefreshPendingChangesAction;
            result.mDeveloperNewIncomingChangesUpdater = window.mDeveloperNewIncomingChangesUpdater;
            result.mGluonNewIncomingChangesUpdater = window.mGluonNewIncomingChangesUpdater;
            result.mException = window.mException;
            result.mLastUpdateTime = window.mLastUpdateTime;
            result.mIsGluonMode = window.mIsGluonMode;
            result.mIncomingChangesNotifier = window.mIncomingChangesNotifier;
            result.mStatusBar = window.mStatusBar;
            result.mWelcomeView = window.mWelcomeView;
            result.mNotificationBarUpdater = window.mNotificationBarUpdater;
            return result;
        }

        internal PlasticProjectSettingsProvider.IAutoRefreshView GetPendingChangesView()
        {
            return mViewSwitcher != null ? mViewSwitcher.PendingChangesTab : null;
        }

        static class Reload
        {
            internal static void IfWorkspaceConfigChanged(
                IPlasticAPI plasticApi,
                WorkspaceInfo lastWkInfo,
                bool lastIsGluonMode,
                Action reloadAction)
            {
                string applicationPath = Application.dataPath;

                bool isGluonMode = false;
                WorkspaceInfo wkInfo = null;

                IThreadWaiter waiter = ThreadWaiter.GetWaiter(10);
                waiter.Execute(
                    /*threadOperationDelegate*/ delegate
                    {
                        wkInfo = FindWorkspace.
                            InfoForApplicationPath(applicationPath, plasticApi);

                        if (wkInfo != null)
                            isGluonMode = plasticApi.IsGluonWorkspace(wkInfo);
                    },
                    /*afterOperationDelegate*/ delegate
                    {
                        if (waiter.Exception != null)
                            return;

                        if (!IsWorkspaceConfigChanged(
                                lastWkInfo, wkInfo,
                                lastIsGluonMode, isGluonMode))
                            return;

                        reloadAction();
                    });
            }

            static bool IsWorkspaceConfigChanged(
                WorkspaceInfo lastWkInfo,
                WorkspaceInfo currentWkInfo,
                bool lastIsGluonMode,
                bool currentIsGluonMode)
            {
                if (lastIsGluonMode != currentIsGluonMode)
                    return true;

                if (lastWkInfo == null || currentWkInfo == null)
                    return true;

                return !lastWkInfo.Equals(currentWkInfo);
            }
        }

        [SerializeField]
        bool mForceToOpen;

        [NonSerialized]
        WorkspaceInfo mWkInfo;

        Exception mException;

        internal IIncomingChangesNotifier mIncomingChangesNotifier;

        double mLastUpdateTime = 0f;

        CooldownWindowDelayer mCooldownAutoRefreshPendingChangesAction;
        internal ViewSwitcher mViewSwitcher;
        WelcomeView mWelcomeView;

        StatusBar mStatusBar;
        NotificationBarUpdater mNotificationBarUpdater;

        PlasticGui.WorkspaceWindow.NewIncomingChangesUpdater mDeveloperNewIncomingChangesUpdater;
        GluonNewIncomingChangesUpdater mGluonNewIncomingChangesUpdater;

        WorkspaceWindow mWorkspaceWindow;

        bool mIsGluonMode;
        bool mDisableCollabIfEnabledWhenLoaded;
        AssetOperations mAssetOperations;

        static readonly ILog mLog = LogManager.GetLogger("PlasticWindow");
    }
}