using Aitex.Core.Account;
using Aitex.Core.Common.DeviceData;
using Aitex.Core.RT.Event;
using Aitex.Core.RT.Log;
using Aitex.Core.Util;
using Aitex.Core.WCF;
using Caliburn.Micro;
using Caliburn.Micro.Core;
using MECF.Framework.Common.Account.Extends;
using MECF.Framework.Common.Account.Permissions;
using MECF.Framework.Common.MECF.Framework.Common.SCCore;
using MECF.Framework.Common.Utilities;
using MECF.Framework.UI.Client.CenterViews.LogOnOff;
using MECF.Framework.UI.Client.ClientBase;
using MECF.Framework.UI.Client.ClientBase.Dialog;
using MECF.Framework.UI.Client.Core;
using MECF.Framework.UI.Client.Ctrlib.Controls;
using MECF.Framework.UI.Core.Accounts;
using OpenSEMI.ClientBase;
using OpenSEMI.ClientBase.Command;
using OpenSEMI.ClientBase.Utility;
using Sicentury.Core.Collections;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Threading;
using Action = System.Action;

namespace SicUI.Client
{
    public class MainViewModel : TimeredMainViewModel
    {
        #region Variables

        private MainView _view;
        private AppMenu _alarmMenu;
        private AppMenu _currentMenuItem;
        private List<AppMenu> _menuItems;
        private List<AppMenu> _subMenuItems;
        private ObservableCollection<AppMenu> _historyItems;
        private Dictionary<Type, BaseModel> _models;
        private string _context;
        private bool _isReadOnlyMode;
        private readonly IEventAggregator _eventAggregator;
        private readonly IProgress<Credential> _prgShowLoginRequestConfirmDialog;
        private CancellationTokenSource _ctsLoginRequestConfirmDialog;
        private readonly IWindowManager _windowmanager;

        #endregion

        #region Constructors

        public MainViewModel(IEventAggregator eventAggregator, IWindowManager wmr)
        {
            ((ClientApp)BaseApp.Instance).ViewModelSwitcher = this;
            _models = new Dictionary<Type, BaseModel>();
            _eventAggregator = eventAggregator;

            //for login part
            Roles = RoleAccountProvider.Instance.GetRoles();

            EventLogList = new DelayedPresentRollingObservableCollection<EventItem>(1000);
            EventLogsView = CollectionViewSource.GetDefaultView(EventLogList);


            //WarnEventLogList = new ObservableCollection<EventItem>();

            SoftwareVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString();


            EventClient.Instance.OnEvent += Instance_OnEvent;
            EventClient.Instance.OnDisconnectedWithRT += Instance_OnDisconnectedWithRT;
            EventClient.Instance.Start();

            IsPM1Installed = (bool)QueryDataClient.Instance.Service.GetConfig("System.SetUp.IsPM1Installed");
            IsPM2Installed = (bool)QueryDataClient.Instance.Service.GetConfig("System.SetUp.IsPM2Installed");
            IsBufferInstalled = (bool)QueryDataClient.Instance.Service.GetConfig("System.SetUp.IsBufferInstalled");
            IsLLInstalled = (bool)QueryDataClient.Instance.Service.GetConfig("System.SetUp.IsLoadLockInstalled");

            var equipmentName = (string)QueryDataClient.Instance.Service.GetConfig("System.EquipmentName");
            var equipmentNo = (int)QueryDataClient.Instance.Service.GetConfig("System.EquipmentNo");
            EquipmentName = equipmentName.ToLower() == "unset" ? "Unset" : $"{equipmentName}-{equipmentNo:D4}";

            _prgShowLoginRequestConfirmDialog = new Progress<Credential>(ShowLoginRequestConfirmDialog);

            Reset();
            _windowmanager = wmr;
        }

        #endregion

        #region Menus

        public string EquipmentName { get; }

        public string NowDateTime { get; set; }

        private bool _isLogin = false;
        public bool IsLogin
        {
            get => _isLogin;
            set { _isLogin = value; NotifyOfPropertyChange(); }
        }

        private List<Role> _roles;
        public List<Role> Roles
        {
            get => _roles;
            set { _roles = value; RaisePropertyChangedEventImmediately("Roles"); }
        }

        private ICommand _menuItemClickCommand;
        public ICommand MenuItemClickCommand
        {
            get
            {
                if (_menuItemClickCommand == null)
                    _menuItemClickCommand = new BaseCommand<AppMenu>(SwitchMenuItem);
                return _menuItemClickCommand;
            }
        }

        private ICommand _mainMenuItemClickCommand;
        public ICommand MainMenuItemClickCommand
        {
            get
            {
                if (_mainMenuItemClickCommand == null)
                    _mainMenuItemClickCommand = new BaseCommand<AppMenu>(MainSwitchMenuItem);
                return _mainMenuItemClickCommand;
            }
        }

        public List<AppMenu> MenuItems
        {
            get => _menuItems;
            set { _menuItems = value; NotifyOfPropertyChange(); }
        }

        public List<AppMenu> SubMenuItems
        {
            get => _subMenuItems;
            set { _subMenuItems = value; NotifyOfPropertyChange(); }
        }

        public ObservableCollection<AppMenu> HistoryMenus
        {
            get => _historyItems;
            set { _historyItems = value; NotifyOfPropertyChange(); }
        }

        public string Context
        {
            get => _context;
            set { _context = value; NotifyOfPropertyChange(); }
        }

        
        public UserContext User => BaseApp.Instance.UserContext;


        #endregion

        #region Properties

        public BaseModel CurrentViewModel { get; private set; }

        public bool IsPM1Installed { get; set; }

        public bool IsPM2Installed { get; set; }

        public bool IsBufferInstalled { get; set; }

        public bool IsLLInstalled { get; set; }

        public bool IsPermission { get; set; }

        public bool IsAutoLogout { get; set; }

        public int LogoutTime { get; set; }

        public bool IsReadOnlyMode
        {
            get => _isReadOnlyMode;
            set
            {
                _isReadOnlyMode = value; 
                NotifyOfPropertyChange();
                if (_isReadOnlyMode)
                {
                    BaseApp.Instance.UserMode = UserMode.ReadOnly;

                    // 通知RecipeEditor视图或PMProcess视图锁定Recipe
                    _eventAggregator.PublishOnUIThread(CommonAggregateEvents.EnterReadOnlyMode);
                }
            }
        }

      

        //public ObservableCollection<EventItem> WarnEventLogList { get; set; }
        private DelayedPresentRollingObservableCollection<EventItem> EventLogList { get; }

        /// <summary>
        /// 用于在主界面显示Event Log的视图。
        /// 通过该视图筛选IsAlarm条目。
        /// </summary>
        public ICollectionView EventLogsView { get; }


        private bool _isShowAlarmEventOnly;

        /// <summary>
        /// IsAlarm CheckBox绑定到这里，直接从<see cref="EventLogsView"/>中过滤所需的数据。
        /// </summary>
        public bool IsShowAlarmEventOnly
        {
            get => _isShowAlarmEventOnly;
            set
            {
                _isShowAlarmEventOnly = value;
                if (_isShowAlarmEventOnly)
                {
                    EventLogsView.Filter = item =>
                    {
                        if (item is EventItem ei)
                        {
                            return ei.Level == EventLevel.Alarm;
                        }

                        return false;
                    };
                }
                else
                {
                    EventLogsView.Filter = null;
                }

                OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsShowAlarmEventOnly)));
            }
        }

        public Visibility AllEventsVisibility { get; set; }

        public Visibility WarnEventsVisibility { get; set; }

        [Subscription("Rt.Status")]
        public string RtStatus { get; set; }

        [Subscription("PM1.Status")]
        public string _PM1Status { get; set; }

        [Subscription("PM1.IsBypassInterlock")]
        public bool IsPM1InterlockBypassed { get; set; }

        [Subscription("PM1.IsBypassEnableTable")]
        public bool IsPM1EnableTableBypassed { get; set; }

        public bool IsPM1Warning => IsPM1InterlockBypassed || IsPM1EnableTableBypassed;

        public string PM1WarningMessage
        {
            get
            {
                if (IsPM1InterlockBypassed && IsPM1EnableTableBypassed)
                    return "PM1 Interlock and Enable Table are bypassed";

                if (IsPM1InterlockBypassed)
                    return "PM1 Interlock is bypassed";

                return "PM1 Enable Table is bypassed";
            }
        }

        public string PM1Status
        {
            get
            {
                if (IsPM1Installed)
                    return _PM1Status;
                else
                    return "NotInstall";
            }
            set => _PM1Status = value;
        }

        [Subscription("PM2.Status")]
        public string _PM2Status { get; set; }

        public string PM2Status
        {
            get
            {
                if (IsPM2Installed)
                    return _PM2Status;
                else
                    return "NotInstall";
            }
            set => _PM2Status = value;
        }

        [Subscription("PM2.IsBypassInterlock")]
        public bool IsPM2InterlockBypassed { get; set; }

        [Subscription("PM2.IsBypassEnableTable")]
        public bool IsPM2EnableTableBypassed { get; set; }

        public bool IsPM2Warning => IsPM2InterlockBypassed || IsPM2EnableTableBypassed;

        public string PM2WarningMessage
        {
            get
            {
                if(IsPM2InterlockBypassed && IsPM2EnableTableBypassed)
                    return "PM2 Interlock and Enable Table are bypassed";

                if(IsPM2InterlockBypassed)
                    return "PM2 Interlock is bypassed";

                return "PM2 Enable Table is bypassed";
            }
        }

        public string UnLoadStatus
        {
            get => "NotInstall";
            set
            {

            }
        }

        [Subscription("Buffer.Status")]
        public string BufferStatus { get; set; }

        [Subscription("LoadLock.Status")]
        public string LLStatus { get; set; }

        [Subscription("TM.Status")]
        public string TMStatus { get; set; }

        [Subscription("TM.IsBypassInterlock")]
        public bool IsTMInterlockBypassed { get; set; }

        [Subscription("TM.IsBypassEnableTable")]
        public bool IsTMEanbleTableBypassed { get; set; }

        public bool IsTMWarning => IsTMInterlockBypassed || IsTMEanbleTableBypassed;

        public string TMWarningMessage
        {
            get
            {
                if (IsTMInterlockBypassed && IsTMEanbleTableBypassed)
                    return "TM Interlock and Enable Table are bypassed";

                if (IsTMInterlockBypassed)
                    return "TM Interlock is bypassed";

                return "TM Enable Table is bypassed";
            }
        }

        [Subscription("System.IsOnline")]
        public bool IsOnlineSystem { get; set; }

        [Subscription("PM1.IsOnline")]
        public bool IsOnlinePM1 { get; set; }

        [Subscription("PM2.IsOnline")]
        public bool IsOnlinePM2 { get; set; }

        [Subscription("UnLoad.IsOnline")]
        public bool IsOnlineUnLoad { get; set; }
        [Subscription("LoadLock.IsOnline")]
        public bool IsOnlineLL { get; set; }

        [Subscription("TM.IsOnline")]
        public bool IsOnlineTM { get; set; }

        [Subscription("Buffer.IsOnline")]
        public bool IsOnlineBuffer { get; set; }

        [Subscription("System.SignalTowerData")]
        public AITSignalTowerData SignalTowerData { get; set; }


        [Subscription("PM1.AbortProcessRun")]
        public bool PM1PlcAbortProcessView { get; set; }

        [Subscription("PM2.AbortProcessRun")]
        public bool PM2PlcAbortProcessView { get; set; }


        public bool IsPlcAbortProcessView => PM1PlcAbortProcessView || PM2PlcAbortProcessView;

        public string SoftwareVersion
        {
            get;
            set;
        }

        public string RunTime => DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");


        [Subscription("System.HasActiveAlarm")]
        public bool SystemHasAlarm { get; set; }

        #endregion

        #region Methods

        private void Instance_OnDisconnectedWithRT()
        {
            MessageBox.Show("Disconnected with RT, UI will exit", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
            Environment.Exit(0);
        }

        private void Instance_OnEvent(EventItem evt)
        {
            switch (evt.Type)
            {
                case EventType.EventUI_Notify:
                    LogEvent(evt);
                    break;
                /*case EventType.Dialog_Nofity:
                    Execute.OnUIThread(() => DialogBox.ShowInfo(evt.ToString()));
                    break;*/
                case EventType.KickOut_Notify:
                    if (evt.Description == "ShutDown")
                    {
                        AccountClient.Instance.Service.LogoutEx(BaseApp.Instance.UserContext.Token);
                        ShutdownThread = ShutdownExecute;
                        ShutdownThread.BeginInvoke(ShutdownCallBack, ShutdownThread);
                    }
                    break;

                case EventType.Sound_Notify:
                    break;
                case EventType.UIMessage_Notify:
                    //PopUIMessage(obj);
                    break;
            }
        }

        #region Log

        public void ShowAlarmEvents()
        {
            AllEventsVisibility = Visibility.Hidden;
            WarnEventsVisibility = Visibility.Visible;
            NotifyOfPropertyChange(nameof(AllEventsVisibility));
            NotifyOfPropertyChange(nameof(WarnEventsVisibility));
        }

        public void ShowAllEvents()
        {
            AllEventsVisibility = Visibility.Visible;
            WarnEventsVisibility = Visibility.Hidden;
            NotifyOfPropertyChange(nameof(AllEventsVisibility));
            NotifyOfPropertyChange(nameof(WarnEventsVisibility));
        }

        private void LogEvent(EventItem obj)
        {
            if (obj.Type != EventType.EventUI_Notify)
                return;

            EventLogList.Add(obj);

        }

        #endregion

        public void SetModuleOnline(object sender, RoutedEventArgs args)
        {
            if (sender is ModuleStatusIndicator indicator)
            {
                if (indicator.HasWarning)
                {
                    DialogBox.ShowError($"Unable to set {indicator.Caption} online since {indicator.WarningTip}");
                    return;
                }

                if (MessageBoxResult.Yes == MessageBox.Show($"Set {indicator.Caption} Online?", "", MessageBoxButton.YesNo,
                        MessageBoxImage.Warning))
                {
                    InvokeClient.Instance.Service.DoOperation($"{indicator.ModuleName}.SetOnline");
                }
            }
        }

        public void SetModuleOffline(object sender, RoutedEventArgs args)
        {
            if (sender is ModuleStatusIndicator indicator)
            {
                if (MessageBoxResult.Yes == MessageBox.Show($"Set {indicator.Caption} Offline?", "", MessageBoxButton.YesNo,
                        MessageBoxImage.Warning))
                {
                    InvokeClient.Instance.Service.DoOperation($"{indicator.ModuleName}.SetOffline");
                }
            }
        }

        public void BuzzerOff()
        {
            InvokeClient.Instance.Service.DoOperation($"System.BuzzerOff");
        }

        public void ShutDownPLCRun()
        {

            var selection = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.Cancel, DialogType.WARNING,
                "Are you sure abort PLC process");
            if (selection == DialogButton.Cancel)
                return;
            

            if (IsPM1Installed)
                InvokeClient.Instance.Service.DoOperation("PM1.AbortProcessInterrupt");
            if (IsPM2Installed)
                InvokeClient.Instance.Service.DoOperation("PM2.AbortProcessInterrupt");
        }

        public void PLCProcessBuzzerOFF()
        {
            if (IsPM1Installed)
                InvokeClient.Instance.Service.DoOperation("PM1.AbortProcessBuzzerOFF");
            if (IsPM2Installed)
                InvokeClient.Instance.Service.DoOperation("PM2.AbortProcessBuzzerOFF");

            BuzzerOff();
        }

        #region Login/Logout Operations

        private Task<LoginResult> _loginTask;
        private LoginRequestWaitDialog _loginWaitDialog = null;

        /// <summary>
        /// 密码输入框中按下Enter键。
        /// </summary>
        /// <param name="args"></param>
        /// <param name="loginName"></param>
        /// <param name="password"></param>
        /// <param name="role"></param>
        public void PasswordBoxEnterKeyPressed(KeyEventArgs args, string loginName, PasswordBox password, Role role)
        {
            if (args.Key == Key.Enter)
                RequestLogin(loginName, password, role);
        }

        /// <summary>
        /// 显示远程登录请求确认对话框。
        /// </summary>
        /// <param name="credRequesting">正在请求登录的凭据</param>
        private void ShowLoginRequestConfirmDialog(Credential credRequesting)
        {
            var dlgConfirm = new LoginRequestConfirmationDialog(credRequesting, _ctsLoginRequestConfirmDialog)
            {
                Owner = Application.Current.MainWindow
            };
            var retDlg = dlgConfirm.ShowDialog();
            if (retDlg == true)
            {
                AccountClient.Instance.Service.ConfirmLoginRequest(credRequesting.AccountInfo.LoginName);
                var role = BaseApp.Instance.UserContext.Role;
                Logoff();

                // 降级为仅查看模式
                IsReadOnlyMode = true;
                BaseApp.Instance.UserContext.Role = role;
                BaseApp.Instance.UserContext.Credential = Credential.ReadOnlyOne;
            }
            else
            {
                AccountClient.Instance.Service.RejectLoginRequest(credRequesting.AccountInfo.LoginName);
            }
        }

        /// <summary>
        /// 显示登录确认等待对话框。
        /// </summary>
        /// <param name="cts"></param>
        /// <returns></returns>
        private Task ShowRequestLoginWaiter(CancellationTokenSource cts)
        {
            var ct = cts.Token;

            return Task.Run(() =>
            {
                /*bool? waitResult = null;*/

                // 如果超过一定时间没有从RT返回确认信息，则显示登录中的提示
                Thread.Sleep(500);

                if (ct.IsCancellationRequested)
                    return;

                Execute.OnUIThread(() =>
                {
                    _loginWaitDialog = new LoginRequestWaitDialog()
                    {
                        Owner = Application.Current.MainWindow
                    };
                    _loginWaitDialog.ShowDialog();
                });

                /*while (true)
                {
                    Thread.Sleep(500);

                    // 等待对话框关闭，不关心返回的DialogResult
                    // 关闭原因：
                    // 1. 点击Cancel按钮
                    // 2. 超时
                    // 3. 远端接受或拒绝请求
                    if (waitResult.HasValue)
                        break;

                    if (ct.IsCancellationRequested)
                        break;
                }

                // 强制关闭等待对话框
                Execute.OnUIThread(() =>
                {
                    loginWaitDialog?.Close();
                });*/

            }, ct);
        }

        public async void RequestLogin(string loginName, PasswordBox password, Role role)
        {
            if (_loginTask is { Status: TaskStatus.Running })
                return;

            #region Validate Parameters

            if (string.IsNullOrEmpty(loginName))
            {
                DialogBox.ShowError("User Name can not be empty.");
                return;
            }

            if (string.IsNullOrEmpty(password.Password))
            {
                DialogBox.ShowError("Password can not be empty.");
                return;
            }

            if (role == null || string.IsNullOrEmpty(role.RoleId))
            {
                DialogBox.ShowError("Role can not be empty.");
                return;
            }

            #endregion

            // 向RT请求登录
            try
            {
                var ctsWaitingTask = new CancellationTokenSource();
                var taskWaiting = ShowRequestLoginWaiter(ctsWaitingTask);
                _loginTask = AccountClient.Instance.Service.LoginEx(loginName, password.Password, role?.RoleId??"",
                    BaseApp.Instance.ClientInfo);

                // 等待确认超时，或已被接受/拒绝登录
                await Task.WhenAny(_loginTask, taskWaiting);

                if (_loginTask.Status == TaskStatus.RanToCompletion)
                {
                    // 被接受或拒绝登录
                    ctsWaitingTask.Cancel();
                    _loginWaitDialog?.Close();
                    
                    switch (_loginTask.Result.Result)
                    {
                        case LoginRequestResults.Error:
                            DialogBox.ShowError("Error occurred when login, see log for details.");
                            break;
                        case LoginRequestResults.WrongPwd:
                            DialogBox.ShowError("Invalid password.");
                            break;
                        case LoginRequestResults.NoMatchRole:
                            DialogBox.ShowError("The user does not belong to the role.");
                            break;
                        case LoginRequestResults.NoMatchUser:
                            DialogBox.ShowError("The user is not found.");
                            break;
                        case LoginRequestResults.Rejected:
                            DialogBox.ShowError("Login Request is rejected.");
                            break;
                        case LoginRequestResults.RequstingLogin:
                            DialogBox.ShowError(
                                "The same user is requesting to login from other place, please try again later.");
                            break;
                        case LoginRequestResults.Timeout:
                            DialogBox.ShowError("Timeout to wait login request confirmation.");
                            break;
                        case LoginRequestResults.Confirmed:
                            var cred = _loginTask.Result.Credential;
                            BaseApp.Instance.UserMode = UserMode.Normal;
                            BaseApp.Instance.UserContext.Credential = cred;
                            BaseApp.Instance.UserContext.Role = role;
                            BaseApp.Instance.UserContext.LastAccessTime = DateTime.Now;
                            BaseApp.Instance.UserContext.IsLogin = true;

                            //Load menu by role
                            //filer menu if necessary... 
                            BaseApp.Instance.MenuManager.LoadMenu(
                                RoleAccountProvider.Instance.GetAccessibleMenusByRole(role.RoleId));

                            IsAutoLogout = role.IsAutoLogout;
                            LogoutTime = role.LogoutTime;

                            IsPermission = RoleAccountProvider.Instance.GetMenuPermission(role.RoleId, "Header") == MenuPermissionEnum.MP_READ_WRITE;

                            InitMenu(); //bind menu to main view

                            IsLogin = true; //control the display logic of main view 
                            IsReadOnlyMode = false;

                            LOG.Info($"{loginName} login as {role.RoleName}");

                            break;


                        default:
                            DialogBox.ShowError("Unknown login result, see log for details.");
                            break;
                    }

                }
                else if (taskWaiting.Status == TaskStatus.RanToCompletion)
                {
                    // 等待确认超时
                    // 注意：该请求在RT的存活时间比UI超时时间长，因此需要显式取消请求，否则下次登录请求需要等CredentialManager超时后才能重新发起
                    AccountClient.Instance.Service.CancelLoginRequest(loginName);
                }
                else
                {
                    // 两个任务均失败，显式调用下列方法强制RT清理凭据。
                    AccountClient.Instance.Service.CancelLoginRequest(loginName);
                }
            }
            catch (InvalidOperationException ex)
            {
                LOG.Error(ex.Message);
            }
            catch (Exception ex)
            {
                LOG.Error(ex.Message);
            }
        }

        public void ShowLogoutDialog()
        {
            if (!IsReadOnlyMode)
            {
                var logoffViewmodel = new LogoffViewModel();
                _windowmanager.ShowDialog(logoffViewmodel);

                BaseApp.Instance.UserMode = logoffViewmodel.DialogResult;

                switch (logoffViewmodel.DialogResult)
                {
                    case UserMode.Logoff:
                        Logoff();
                        break;
                    case UserMode.Exit:
                        AccountClient.Instance.Service.LogoutEx(BaseApp.Instance.UserContext.Token);

                        BaseApp.Instance.UserMode = UserMode.Exit;
                        LOG.Info(
                            $"{BaseApp.Instance.UserContext.LoginName} exit as {BaseApp.Instance.UserContext.Role.RoleName}");

                        TryClose();
                        break;
                    case UserMode.Shutdown:
                        //配置立即生效。
                        var roleId = BaseApp.Instance.UserContext.Role.RoleId;
                        var canShutDown =
                            RoleAccountProvider.Instance.GetMenuPermission(roleId, "Operation.Behaviour.ShutDown") ==
                            MenuPermissionEnum.MP_READ_WRITE;
                        if (canShutDown)
                            InvokeClient.Instance.Service.DoOperation("System.ShutDown");
                        else
                        {
                            DialogBox.ShowError("No Permission for ShutDown");
                        }

                        break;
                }

                _eventAggregator.PublishOnUIThread(logoffViewmodel.DialogResult);
            }
            else
            {
                Logoff();
                IsReadOnlyMode = false;
            }
        }

        public void Logoff()
        {
            BaseApp.Instance.UserMode = UserMode.Logoff;
            if (BaseApp.Instance.UserContext.IsLogin)
            {
                try
                {
                    AccountClient.Instance.Service.LogoutEx(BaseApp.Instance.UserContext.Token);
                    LOG.Info(
                        $"{BaseApp.Instance.UserContext.LoginName} logoff as {BaseApp.Instance.UserContext.Role.RoleName}");
                    BaseApp.Instance.UserContext.Clear();

                    ClearItems();
                }
                catch (Exception exp)
                {
                    LOG.Write(exp);
                }
            }
            IsLogin = false;    //no independent login page
            Roles = RoleAccountProvider.Instance.GetRoles();
        }

        #endregion

        public void Reset()
        {
            InvokeClient.Instance.Service.DoOperation("System.Reset");

            //清除GEM报警
            InvokeClient.Instance.Service.DoOperation("GEM_ClearAllAlarm");
        }


        #region Overrided Functions

        public override void CanClose(Action<bool> callback)
        {
            if (BaseApp.Instance.UserMode == UserMode.Normal)
            {
                callback(false);
                Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, (ThreadStart)ShowLogoutDialog);
            }
            else
                callback(true);
        }

        protected override void OnInitialize()
        {
            //display system version or other info...
            DisplayName = "Sic - GE Manual";

            base.OnInitialize();
            StartTimer();

            ConfigChangeCheck();
            //MaintenanceCheck();
            
            if (Debugger.IsAttached)
            {
                RequestLogin("admin", new PasswordBox() { Password = "admin" }, new Role("0", "Manager", false, 1000, null));
            }

            InvokeClient.Instance.Service.DoOperation("GEM_SetSoftwareVersion", SoftwareVersion);
        }

        private void MaintenanceCheck()
        {
            string info = (string)QueryDataClient.Instance.Service.GetData("MaintainManager.MaintenanceCheck");

            if (info != "")
            {
                DialogBox.ShowInfo("There are UnMaintainItems in Plan\r\n" + info
                    + "please check as soon as possible.");
            }
        }

        private void ConfigChangeCheck()
        {
            List<ConfigChangedInfo> s = (List<ConfigChangedInfo>)QueryDataClient.Instance.Service.GetConfig("SystemConfig.DataChangedList");
            if (s != null && s.Count > 0)
            {
                string tips = "";
                foreach (var t in s)
                {
                    var value = QueryDataClient.Instance.Service.GetConfig(t.Name);
                    if (value != null)
                    {
                        tips += t.Name + (t.Enable ? " " : " HAS ") + "CHANGED:\r\n(Previous)\t" + t.OldValue + " => " + value.ToString() + "\t(Latest)\r\n";
                    }
                }
                if (tips != "")
                {
                    DialogBox.ShowInfo("There are some changes in SystemConfig,\r\n" +
                        "please make sure those changes are OK:\r\n" +
                        "****************************************************\r\n" + tips);
                    InvokeClient.Instance.Service.DoOperation("SystemConfig.Confirm");
                }
            }

        }

        protected override void OnActivate()
        {
            base.OnActivate();
            ShowAllEvents();
            EnableTimer(true);

        }

        protected override void OnViewLoaded(object view)
        {
            base.OnViewLoaded(view);
            _view = view as MainView;
            _view.tbLoginName.Focus();
            _view.SplashScreen?.Complete();
        }

        protected override void OnDeactivate(bool close)
        {
            base.OnDeactivate(close);
            EnableTimer(false);
        }

        #endregion

        #region

        #region Sync ShutDown Thread

        public delegate void ShutDownSysncThread();
        private ShutDownSysncThread ShutdownThread = null;
        private readonly ShutdownViewModel ShutdownWindow = null;

        private void ShutdownExecute()
        {
            BaseApp.Instance.UserMode = UserMode.Shutdown;
            BaseApp.Instance.UserContext.IsLogin = false;
            LOG.Info($"{BaseApp.Instance.UserContext.LoginName} shutdown as {BaseApp.Instance.UserContext.Role.RoleName}");

            TryClose();
        }

        private void ShutdownCallBack(IAsyncResult result)
        {
            if (ShutdownWindow != null)
            {
                ShutdownWindow.TryClose();
            }
            ShutdownThread.EndInvoke(result);
        }

        #endregion

        #region Menu Control and page switch

        private void InitMenu()
        {
            MenuItems = BaseApp.Instance.MenuManager.MenuItems;
            SubMenuItems = new List<AppMenu>();
            HistoryMenus = new ObservableCollection<AppMenu>();

            if (MenuItems.Count > 0)
            {
                AppMenu _default = null;
                foreach (AppMenu menuitem in MenuItems)
                {
                    if (menuitem.MenuItems.Count > 0)
                    {
                        if (menuitem.AlarmModule == "System")
                        {
                            _alarmMenu = menuitem;
                            break;
                        }

                        if (_default == null)
                            _default = menuitem.MenuItems[0];
                    }
                }

                var pmMenus = MenuItems.Where(x => x.ResKey.StartsWith("PM")).ToList();
                if (pmMenus.Any())
                {
                    foreach (var menu in pmMenus)
                    {
                        // rename PM menu according to config
                        if (menu.ResKey.StartsWith("PM"))
                        {
                            var name = (string)QueryDataClient.Instance.Service.GetConfig(
                                $"PM.{menu.ResKey}.DisplayName");
                            if (!string.IsNullOrEmpty(name))
                                menu.ResKey = name;
                        }
                    }
                }


                SwitchMenuItem(_default);
            }
        }

        public void MainSwitchMenuItem(AppMenu menuViewItem)
        {
            if (menuViewItem.MenuItems.Count <= 0) 
                return;
            SwitchMenuItem(menuViewItem.LastSelectedSubMenu ?? menuViewItem.MenuItems[0]);
        }

        public void SwitchMenuItem(AppMenu menuViewItem)
        {
            if (string.IsNullOrEmpty(menuViewItem.ViewModel)) 
                return;

            if (menuViewItem.Model == null)
            {
                menuViewItem.Model = (BaseModel)AssemblyUtil.CreateInstance(AssemblyUtil.GetType(menuViewItem.ViewModel));
                ((BaseModel)menuViewItem.Model).Permission = menuViewItem.Permission;

                // Change display name to the one set in config if it's PM.Main
                var displayName = menuViewItem.ResKey;
                if (menuViewItem.ResKey.ToLower() == "main" && menuViewItem.System.StartsWith("PM"))
                {
                    var name = (string)QueryDataClient.Instance.Service.GetConfig(
                        $"PM.{menuViewItem.System}.DisplayName");
                    if (!string.IsNullOrEmpty(name))
                        displayName = name;
                }

                ((BaseModel)menuViewItem.Model).DisplayName = displayName;  //将名称传入viewmodel中

                if (menuViewItem.Model is ISupportMultipleSystem smsViewModel)
                    smsViewModel.SystemName = menuViewItem.System;
            }

            ActivateItem(((BaseModel)menuViewItem.Model));
            CurrentViewModel = ((BaseModel)menuViewItem.Model);

            //if (((BaseModel)menuViewItem.Model).Page != PageID.MAX_PAGE)
            //    BaseApp.Instance.SetCurrentPage(((BaseModel)menuViewItem.Model).Page);

            HandleSubAndHistoryMenu(menuViewItem);

            if (_currentMenuItem != null)
            {
                _currentMenuItem.Selected = false;
                _currentMenuItem.Parent.Selected = false;
            }

            menuViewItem.Selected = true;
            menuViewItem.Parent.Selected = true;
            menuViewItem.Parent.LastSelectedSubMenu = menuViewItem;

            _currentMenuItem = menuViewItem;

            #region Change Background

            if (!string.IsNullOrEmpty(_currentMenuItem.System) && (_currentMenuItem.System.StartsWith("PM1") || _currentMenuItem.System.StartsWith("PM2")))
            {
                var scPath = $"PM.{_currentMenuItem.System}.BackgroundColor";
                var scConfig = QueryDataClient.Instance.Service.GetConfig(scPath);
                if (scConfig == null) return;
                var hexColor = scConfig.ToString();
                try
                {
                    if (string.IsNullOrEmpty(hexColor))
                    {
                        LOG.Error($"Unable to find value of config item {scPath}");
                        hexColor = Colors.Transparent.ToString();
                    }

                    var color = (Color)ColorConverter.ConvertFromString(hexColor);
                    UpdatePMPageBackground(color);
                    UpdateMainMenuBackground();
                }
                catch (Exception ex)
                {
                    LOG.Error($"Unable to set active content background, {ex.Message}", ex);
                    UpdatePMPageBackground(Colors.Transparent);
                    UpdateMainMenuBackground();
                }
            }
            else
            {
                UpdatePMPageBackground(Colors.Transparent);
                UpdateMainMenuBackground();
            }

            #endregion
        }

        private void HandleSubAndHistoryMenu(AppMenu menuitem)
        {
            SubMenuItems = menuitem.Parent.MenuItems;

            if (!HistoryMenus.Contains(menuitem))
            {
                if (HistoryMenus.Count >= 8)
                    HistoryMenus.RemoveAt(7);
                HistoryMenus.Insert(0, menuitem);
            }
            else
            {
                HistoryMenus.Remove(menuitem);
                HistoryMenus.Insert(0, menuitem);
            }
        }

        public bool SwitchPage(string firstLevelMenuId, string secondLevelMenuId)
        {
            foreach (var menuitem in BaseApp.Instance.MenuManager.MenuItems)
            {
                if (menuitem.MenuID == firstLevelMenuId)
                {
                    foreach (var menu in menuitem.MenuItems)
                    {
                        if (menu.MenuID == secondLevelMenuId)
                        {
                            SwitchMenuItem(menu);
                            return true;
                        }
                    }
                }
            }
            return false;
        }

        #endregion

        #region Refresh Date Time on page

        protected override void InvokeAfterUpdateProperty(Dictionary<string, object> data)
        {
            if (_alarmMenu != null)
                _alarmMenu.IsAlarm = SystemHasAlarm;
        }

        protected override bool OnTimer()
        {
            try
            {
                base.Poll();
                var roles = RoleAccountProvider.Instance.GetRoles();
                if (!Credential.IsEmpty(BaseApp.Instance.UserContext.Credential))
                {
                    var role = roles.Find(x => x.RoleName == BaseApp.Instance.UserContext.Role.RoleName);
                    LogoutTime = role.LogoutTime;
                    IsAutoLogout = role.IsAutoLogout;
                    var idleDuration = GetLastInputTime();
                    if (idleDuration >= LogoutTime * 60 && IsLogin && IsAutoLogout)
                        Logoff();
                    else
                    {
                        // keep credential alive
                        var retKeepAlive = AccountClient.Instance.Service.KeepAlive(BaseApp.Instance.UserContext.Token);
                        switch (retKeepAlive.State)
                        {
                            case CredentialKeepAliveStates.Alive or CredentialKeepAliveStates.NotFound:
                                {
                                    if (_ctsLoginRequestConfirmDialog is { IsCancellationRequested: false })
                                        _ctsLoginRequestConfirmDialog.Cancel();

                                    if (retKeepAlive.State == CredentialKeepAliveStates.NotFound)
                                    {
                                        Logoff();
                                        Execute.OnUIThread(() => { DialogBox.ShowError("You are kicked by RT."); });
                                    }

                                    break;
                                }
                            case CredentialKeepAliveStates.RequestingLogin:
                                {
                                    // 当前用户在其它地方请求登录

                                    if (_ctsLoginRequestConfirmDialog == null ||
                                        _ctsLoginRequestConfirmDialog.IsCancellationRequested)
                                    {
                                        _ctsLoginRequestConfirmDialog = new CancellationTokenSource();
                                        _prgShowLoginRequestConfirmDialog.Report(retKeepAlive.RequestingCredential);
                                    }

                                    /* Execute.OnUIThread(() =>
                                     {
                                         var dlgConfirm = new LoginRequestConfirmationDialog(retKeepAlive.RequestingCredential);
                                         var retDlg = dlgConfirm.ShowDialog();

                                         if (retDlg == true)
                                         {
                                             AccountClient.Instance.Service.ConfirmLoginRequest(retKeepAlive.RequestingCredential.AccountInfo.LoginName);
                                             Logoff();

                                             // 降级为仅查看模式
                                             IsReadOnlyMode = true;
                                             BaseApp.Instance.UserContext.Role = role;
                                             BaseApp.Instance.UserContext.Credential = Credential.ReadOnlyOne;
                                         }
                                         else
                                         {
                                             AccountClient.Instance.Service.RejectLoginRequest(retKeepAlive.RequestingCredential.AccountInfo.LoginName);
                                         }
                                     });*/

                                    // 等待RT处理凭据，否则有可能反复触发请求登录确认窗口.
                                    Thread.Sleep(1000);
                                    break;
                                }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                LOG.Error(ex.Message);
            }

            return true;
        }

        private void StartTimer()
        {
            DispatcherTimer myDispatcherTimer =
                new DispatcherTimer();
            myDispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 1000);
            myDispatcherTimer.Tick += Each_Tick;
            myDispatcherTimer.Start();
        }

        public void Each_Tick(object o, EventArgs sender)
        {
            NowDateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
            NotifyOfPropertyChange(nameof(NowDateTime));
        }

        #endregion

        #endregion

        #endregion

        #region Change PM Background

        private Brush _bgActiveContent;

        public Brush BgActiveContent
        {
            get => _bgActiveContent;
            set
            {
                _bgActiveContent = value;
                NotifyOfPropertyChange();
            }
        }

        public void UpdatePMPageBackground(Color color)
        {
            var DEF_COLOR = Colors.LightBlue;

            if (color == Colors.Transparent)
            {
                try
                {
                    if (Application.Current != null && Application.Current.Resources.Contains("MainArea_BG"))
                    {
                        var res = Application.Current.Resources["MainArea_BG"] as Brush;
                        if (res != null)
                        {
                            // Clone to avoid modifying the shared resource if it's frozen
                            var clone = res.CloneCurrentValue();
                            BgActiveContent = clone;
                            return;
                        }
                    }
                }
                catch
                {

                }

                BgActiveContent = new SolidColorBrush(DEF_COLOR);
                return;
            }

            // Build a vertical gradient based on the provided color
            // Apply alpha into RGB channels, produce an opaque color (alpha applied to channels)
            var alphaByte = (byte)50;
            var factor = alphaByte / 255.0;

            var blendedR = (byte)Math.Round(DEF_COLOR.R * (1.0 - factor) + color.R * factor);
            var blendedG = (byte)Math.Round(DEF_COLOR.G * (1.0 - factor) + color.G * factor);
            var blendedB = (byte)Math.Round(DEF_COLOR.B * (1.0 - factor) + color.B * factor);

            // make opaque baseColor after blending
            var baseColor = Color.FromArgb(255, blendedR, blendedG, blendedB);

            // lighter stop (towards white)
            var lighter = Color.FromArgb(
                255,
                (byte)Math.Min(255, baseColor.R + (255 - baseColor.R) / 4),
                (byte)Math.Min(255, baseColor.G + (255 - baseColor.G) / 4),
                (byte)Math.Min(255, baseColor.B + (255 - baseColor.B) / 4));

            // slightly darker stop
            var darker = Color.FromArgb(
                255,
                (byte)Math.Max(0, (int)(baseColor.R * 0.75)),
                (byte)Math.Max(0, (int)(baseColor.G * 0.75)),
                (byte)Math.Max(0, (int)(baseColor.B * 0.75)));

            var brush = new LinearGradientBrush
            {
                StartPoint = new Point(0, 0),
                EndPoint = new Point(0, 1)
            };
            brush.GradientStops.Add(new GradientStop(lighter, 0.0));
            brush.GradientStops.Add(new GradientStop(baseColor, 0.5));
            brush.GradientStops.Add(new GradientStop(darker, 1.0));

            // Freeze for performance
            if (brush.CanFreeze)
                brush.Freeze();

            BgActiveContent = brush;
        }

        private void UpdateMainMenuBackground()
        {
            if (_view == null || _currentMenuItem == null)
                return;

            // Ensure UI work happens on UI thread
            Application.Current?.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
            {
                // Try direct FindName first (faster when available)
                Menu mainMenu = null;
                try
                {
                    if (_view is FrameworkElement fe)
                        mainMenu = fe.FindName("MainMenu") as Menu;
                }
                catch { /* ignore */ }

                // Fallback to visual search if FindName didn't find it
                if (mainMenu == null)
                    mainMenu = FindVisualChildByName<Menu>(_view, "MainMenu");

                if (mainMenu == null)
                    return;

                // Iterate items and use ItemContainerGenerator to get the MenuItem container
                foreach (var item in mainMenu.Items)
                {
                    var container = mainMenu.ItemContainerGenerator.ContainerFromItem(item) as MenuItem;
                    if (container == null)
                        continue;

                    if (container.DataContext is AppMenu menuInfo)
                    {
                        if (menuInfo.System is "PM1" or "PM2")
                        {
                            var rect = FindVisualChildByName<Rectangle>(container, "InnerBG");
                            if(rect != null)
                            {
                                rect.Fill = menuInfo.Selected
                                ? BgActiveContent
                                : new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FF346294"));
                            }

                            var bgBorder = FindVisualChildByName<Border>(container, "BG");
                            if (bgBorder != null)
                            {
                                bgBorder.Background = menuInfo.Selected
                               ? BgActiveContent
                               : new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FF346294"));
                            }
                        }
                    }
                }
            }));
        }

        private static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
        {
            if (depObj == null) yield break;

            for (var i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
            {
                var child = VisualTreeHelper.GetChild(depObj, i);
                if (child is T t) yield return t;

                foreach (var childOfChild in FindVisualChildren<T>(child))
                    yield return childOfChild;
            }
        }

        private static T FindVisualChildByName<T>(DependencyObject depObj, string name) where T : FrameworkElement
        {
            if (depObj == null) return null;

            for (var i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
            {
                var child = VisualTreeHelper.GetChild(depObj, i);

                if (child is T fe && fe.Name == name)
                    return fe;

                var result = FindVisualChildByName<T>(child, name);
                if (result != null)
                    return result;
            }

            return null;
        }

        #endregion

    }
}
