#include "sysmainwindow.h"
#include <QCloseEvent>
#include <QDesktopServices>
#include <QProcess>
#include <QGraphicsDropShadowEffect>
#include <QHBoxLayout>
#include <QMenu>
#include <QDateTime>
#include <QResource>
#include <QTabBar>
#include <QFileDialog>
#include <QFileInfo>
#include <QToolBar>
#include <QApplication>
#include <QVBoxLayout>
#include <QSplitter>
#include <QPainter>
#include <tbaseutil/tdataparse.h>
#include <tbaseutil/tdataresponse.h>
#include <tbaseutil/tfileio.h>
#include <tbaseutil/tlogger.h>
#include <tbaseutil/tresource.h>
#include <tbaseutil/ttheme.h>
#include <tbaseutil/thttputil.h>
#include <tbaseutil/tjsonapi.h>
#include <tdatabaseutil/tsqlqueryv2.h>
#include <tdatabaseutil/tsqlconnectionpoolv2.h>
#include <toputil/topaboutusdialog.h>
#include <topcore/topclasssqlthread.h>
#include <topcore/topcore.h>
#include <toputil/toploginpwdrestdialog.h>
#include <topcore/topmessagecontroller.h>
#include <twidget/tmessagebox.h>
#include <twidget/tpushbutton.h>
#include <twidget/tuiloaderdialog.h>
#include <twidget/tpanelmenu.h>
#include <twidget/twebview.h>
#include <twidget/ttableview.h>
#include <twidget/ttextedit.h>
#include <twidget/ttoolbutton.h>
#include <twidget/tdialog.h>
#include <twidget/tradiobox.h>
#include <twidget/tcheckbox.h>
#include <twidget/tgroupbox.h>
#include <twidget/tdialogbuttonbox.h>
#include "topquickbutton.h"
#include "topquicktoolbar.h"

struct NaviData {
    SysMainWindow::RouteType routeType = SysMainWindow::RouteType::None; // 路由类型
    QVariant menuData; // 当类型为Menu时,缓存导航树的状态
};

SysMainWindow::SysMainWindow(const QString &iModuleNameStr,
                             const QVariantMap &iUrlPars,
                             QWidget *iParent)
    : TopClassAbs(iParent)
{
    initModule(iModuleNameStr, iUrlPars);
    if (this->isHookExists("afterModuleInit")) {
        this->callHooks("afterModuleInit");
    }

    setHasWindowTitleBar(false);

    QStringList rccLst = config("resource.rcc").toStringList();
    for (QString rcc : rccLst) {
        TRES->loadRcc(rcc);
    }

    initNaviToolBar();
    initMainWidget();
    initDefaultRoute();
    initIsPinned();
    initSysTrayIcon();

    mMessageController = new TopMessageController(this);
    mMessageController->setParentWidget(this);

    connect(APP, SIGNAL(notification(QString,QVariant,QString)), this, SLOT(onNotification(QString,QVariant,QString)));

    setMinimumSize(TTHEME_DP(800), TTHEME_DP(600));
    QVariantMap defalutSize = config("default_size").toMap();
    if (!defalutSize.isEmpty() && defalutSize["width"].toInt() > 0
            && defalutSize["height"].toInt() > 0) {
        resize(TTHEME_DP(defalutSize["width"].toInt()),
                TTHEME_DP(defalutSize["height"].toInt()));
    } else {
        showMaximized();
    }

    refreshActionState();
    bool hasOperateTimeOut = config("has_operate_time_out").toBool();
    if (hasOperateTimeOut) {
        mPreOperateTime = new QDateTime(QDateTime::currentDateTime());
        mOperateTimeOutTimer = new QTimer(this);
        mOperateTimeOutTimer->setInterval(1000);
        connect(mOperateTimeOutTimer, SIGNAL(timeout()), this, SLOT(onOperateTimer()));
        mOperateTimeOutTimer->start();
    }
}

SysMainWindow::~SysMainWindow()
{
    if (this->isHookExists("onDestory")) {
        this->callHooks("onDestory");
    }
}

void SysMainWindow::setIsPinned(bool iIsPinned, bool iForce)
{
    if (!iForce) {
        if (mIsPinned == iIsPinned) {
            return;
        }
    }
    mIsPinned = iIsPinned;
    if (mIsPinned) {
        QSize widgetSize = mPopupWidget->size();
        qreal totalWidth = mMainWidget->width();
        mPinButton->setIcon(TRES->icon("pin"));
        // 导航窗体被钉住
        // 将mNaviWidget添加到mCenterWidget
        mPopupWidget->setWindowFlags(Qt::Widget);
        mPopupWidget->setStretchWidgetVisible(false);
        mCenterWidget->insertWidget(0, mPopupWidget);
        mCenterWidget->setSizes(QList<int>()<<widgetSize.width()<<(totalWidth-widgetSize.width()));
    } else {
        mPinButton->setIcon(TRES->icon("pin2"));
        // 导航窗体取消钉住
        // 将mNaviWidget从mCenterWidget中移出
        // 并将mNaviWidget设置成Popup, 设定mNaviWidget的位置
        mPopupWidget->setParent(nullptr);
        mPopupWidget->setWindowFlags(Qt::Popup);
        mPopupWidget->setStretchWidgetVisible(true);
        movePanelMenuWidget();
        mPopupWidget->setVisible(true);
    }
}

bool SysMainWindow::hide2SystemTrayState()
{
    return mHidetoSysTray;
}

void SysMainWindow::saveHide2SystemTrayState(bool iState)
{
    mHidetoSysTray = iState;
    if (mHidetoSysTrayFlag) {
        APP->saveSetting(mHidetoTrayRegName, QVariant::fromValue(mHidetoSysTray));
    }
}

/*!
 * \brief 是否有退出弹框提示
 */
bool SysMainWindow::hide2SystemTrayTipState()
{
    return !mHidetoSysTrayFlag;
}

/*!
 * \brief 设置是否需要退出弹框提示
 */
void SysMainWindow::saveHide2SystemTrayTipState(bool iState)
{
    if (iState) {
        // 需要提示框
        mHidetoSysTrayFlag = false;
        APP->saveSetting(mHidetoTrayRegName, QVariant::fromValue(QString("")));
    } else {
        // 不需要
        mHidetoSysTrayFlag = true;
        APP->saveSetting(mHidetoTrayRegName, QVariant::fromValue(mHidetoSysTray));
    }
}

bool SysMainWindow::isSystemTrayIconShow()
{
    return mSysTrayIconShow;
}

void SysMainWindow::showLicenseInfo()
{
    TUiloaderDialog dialog;
    dialog.setTitle(ttr("License Info"));
    dialog.resize(500, 400);
    dialog.uiLoader()->setScriptEngine(APP->scriptEngine());
    dialog.setIcon("license");
    dialog.setSelf(this);
    dialog.setUiStr(ui("license_info").toString());
    dialog.setButtons(QStringList()<<ttr("For Authorization") + ":Authorization:ResetRole:Warn"<<ttr("Ok") + ":Ok:Ok:Primary");
    dialog.loadValues(getLicenseInfo());
    connect(dialog.buttonBox(),&TDialogButtonBox::clicked,[=](QAbstractButton *iButton){
        QString name = iButton->objectName();
        if (name == "Authorization"){
            this->forAuthorization();
        }
    });
    dialog.run();
}

QVariantMap SysMainWindow::getLicenseInfo()
{
    QVariantMap licenseData;
    licenseData.insert("product", APP->productCategory());
    TJsonApiResponse httpRes = APP->httpUtil().httpRequest("/system/getLicenseInfo").httpGet().toJsonApiResponse();
    if (httpRes.hasError()) {
        TLOG_ERROR("get licenseInfo failed! " + httpRes.firstError().title() + "\n" + httpRes.firstError().detail());
        alertError(httpRes.firstError().detail());
        return QVariantMap();
    }
    QVariantMap licInfo = httpRes.data().toMap();
    licenseData = licenseData.unite(licInfo);
    licenseData.insert("server_ip", licenseData.value("server_ip").toString().remove(QRegExp(":.*$")));
    mLicenseInfo = licenseData;
    return licenseData;
}

void SysMainWindow::forAuthorization()
{
    QString href = QString("mailto:service@topibd.com?subject=%1 %2&body=").arg(APP->productCategory()).arg(ttr("apply for license"));
    href += QString("%1: %2<br>").arg(ttr("Product Name")).arg(mLicenseInfo.value("product").toString());
    href += QString("%1: %2<br>").arg(ttr("Expire Date")).arg(mLicenseInfo.value("expire_date").toString());
    href += QString("%1: %2<br>").arg(ttr("HD Serial")).arg(mLicenseInfo.value("serial").toString());
    href += QString("%1: %2<br>").arg(ttr("Database Name")).arg(mLicenseInfo.value("database_name").toString());
    href += QString("%1: %2").arg(ttr("Server IP")).arg(mLicenseInfo.value("server_ip").toString());

    QDesktopServices::openUrl(QUrl(href, QUrl::TolerantMode));
}

void SysMainWindow::initNaviToolBar()
{
    mNaviToolBar = new TopQuickToolBar(this);
    mNaviToolBar->setToolButtonStyle(Qt::ToolButtonIconOnly);
    mNaviToolBar->setOrientation(Qt::Vertical);
    mNaviToolBar->setMovable(false);

    mNaviToolBar->setStyleSheet(QString("QToolBar{border:solid; border-color:#D5D5D5; border-width:0px; border-right-width:0px; spacing:0px;padding:0px;background-color:%1;}").arg(TTHEME->value("COLOR_PRIMARY_7").toString()));

    // 添加间隔
    QWidget *spacingWget = new QWidget(this);
    spacingWget->setFixedHeight(TTHEME_DP(56));
    mNaviToolBar->addWidget(spacingWget);

    // 添加配置的导航项
    QVariantList naviLst = config("desktop").toMap().value("navi").toList();

    for (QVariant var : naviLst) {
        if (var.type() == QVariant::String) {
            if (var.toString().toUpper() == "STRETCHER") {
                QWidget *stretcher = new QWidget;
                stretcher->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
                mNaviToolBar->addWidget(stretcher);
            }
        } else {
            QVariantMap navi = var.toMap();
            TopQuickButton *btn = mNaviToolBar->addQuickButton(navi);
            if (btn != nullptr) {
                connect(btn, SIGNAL(clicked()), this, SLOT(onQuickBtnClicked()));
            }
        }
    }
}

void SysMainWindow::initMainWidget()
{
    QWidget *centerWidget = new QWidget(this);
    mMainLayout = new QHBoxLayout(centerWidget);
    mMainLayout->setSpacing(0);
    mMainLayout->setMargin(0);

    // 添加左侧导航工具条
    if (mNaviToolBar != nullptr) {
        mMainLayout->addWidget(mNaviToolBar);
    }

    // 添加路由导航菜单
    mPopupWidget = new StretchFrame(this);
    mPopupWidget->setMinimumWidth(TTHEME_DP(config("navi.min_size.width", 200).toInt()));
    mPopupWidget->setMaximumWidth(TTHEME_DP(500));
    mPopupWidget->resize(TTHEME_DP(config("navi.min_size.width", 200).toInt()), mPopupWidget->height());
    mPopupWidget->setVisible(false);
    mPopupWidget->setGraphicsEffect(TTHEME->getShadowEffect(1, "left"));

    QWidget *popupWidget = new QWidget(mPopupWidget);
    QVBoxLayout *panelMenuMainLayout = new QVBoxLayout(popupWidget);
    panelMenuMainLayout->setMargin(0);
    panelMenuMainLayout->setSpacing(0);
    QWidget *panelMenuHeaderWidget = new QWidget;
    panelMenuHeaderWidget->setFixedHeight(TTHEME_DP(40));
    panelMenuHeaderWidget->setStyleSheet("background-color:#E1E1E5;");
    QHBoxLayout *panelMenuHeaderLayout = new QHBoxLayout(panelMenuHeaderWidget);
    mPanelMenuTitleLabel = new QLabel;
    panelMenuHeaderLayout->addWidget(mPanelMenuTitleLabel);
    panelMenuHeaderLayout->addStretch(1);
    mPinButton = new TToolButton(this);
    connect(mPinButton, SIGNAL(clicked(bool)), this, SLOT(onPinButtonClicked()));
    mPinButton->setIcon(TRES->icon(QString("pin")));
    mPinButton->setTuiStyle("size=small");
    panelMenuHeaderLayout->addWidget(mPinButton);
    panelMenuMainLayout->addWidget(panelMenuHeaderWidget);
    mModuleBarStackedWidget = new QStackedWidget(this);
    mPanelMenu = new TPanelMenu(this);
    connect(mPanelMenu, SIGNAL(clicked(QVariant)), this, SLOT(onPanelMenuClicked(QVariant)));
    mModuleBarStackedWidget->addWidget(mPanelMenu);
    panelMenuMainLayout->addWidget(mModuleBarStackedWidget);
    mPopupWidget->setWidget(popupWidget);

    // 添加右侧模块显示区域
    mMainWidget = new QWidget(this);
    QVBoxLayout *centerLayout = new QVBoxLayout(mMainWidget);
    centerLayout->setSpacing(0);
    centerLayout->setMargin(0);

    mWindowTitleBar = new TFramelessWindowBar(this);
    mWindowTitleBar->setHostWindow(this);
    mWindowTitleBar->setAutoHideFlags(TFramelessWindowBar::AutoHideHint_SysButton);
    mWindowTitleBar->setFixedHeight(TTHEME_DP(40));

    QHBoxLayout *winTitleLayout = new QHBoxLayout(mWindowTitleBar);
    winTitleLayout->setSpacing(0);
    winTitleLayout->setMargin(0);
    winTitleLayout->addSpacing(TTHEME_DP(12));
    mTabBar = new QTabBar(this);
    mTabBar->setProperty("SS_TYPE","PRIMARY");
    mTabBar->setDrawBase(false);
    mTabBar->setStyleSheet("QTabBar QToolButton{background-color:white}");
    winTitleLayout->addWidget(mTabBar, 0, Qt::AlignBottom);
    winTitleLayout->addStretch(1);
    winTitleLayout->addSpacing(TTHEME_DP(8));

    QWidget *msgparent = new QWidget(this);
    msgparent->setFixedWidth(TTHEME_DP(200));
    winTitleLayout->addWidget(msgparent, 0);
    mMessageLabel = new QLabel(msgparent);
    mMessageLabel->setFixedWidth(TTHEME_DP(200));
    mMessageLabel->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
    mMessageLabel->setProperty("SS_FONT", "12");

    mMessageAnimation = new QPropertyAnimation(mMessageLabel, "pos", this);
    mMessageAnimation->setEasingCurve(QEasingCurve::Linear);

    winTitleLayout->addSpacing(TTHEME_DP(10));
    QString plantTitle = APP->config("plant_title_" + APP->language()).toString();
    if (plantTitle.isEmpty()) {
        plantTitle = APP->config("plant_title").toString();
    }
    if (!plantTitle.isEmpty()) {
        QLabel *plantLabel = new QLabel(this);
        plantLabel->setStyleSheet("QLabel{font-size: 12pt}");
        plantLabel->setText(plantTitle);
        winTitleLayout->addWidget(plantLabel);
        winTitleLayout->addSpacing(TTHEME_DP(10));
    }
    winTitleLayout->addWidget(mWindowTitleBar->windowMinButton(), 0, Qt::AlignTop);
    winTitleLayout->addWidget(mWindowTitleBar->windowMaxButton(), 0, Qt::AlignTop);
    winTitleLayout->addWidget(mWindowTitleBar->windowNormalButton(), 0, Qt::AlignTop);
    winTitleLayout->addWidget(mWindowTitleBar->windowCloseButton(), 0, Qt::AlignTop);
    connect(mTabBar, SIGNAL(currentChanged(int)), this, SLOT(onTabBarCurrentChanged(int)));
    connect(mTabBar, SIGNAL(tabCloseRequested(int)), this, SLOT(onTabBarCloseRequested(int)));

    centerLayout->addWidget(mWindowTitleBar);

    mStackedWidget = new StackedWidget(this);
    if (config("background_image_path").isValid()) {
        QString fileName = APP->appPlatformPath()
                + "/" + config("background_image_path").toString();
        if (QFile::exists(fileName)) {
            mStackedWidget->setBackground(fileName);
        } else {
            TLOG_ERROR(ttr("Wrong background image path."));
        }
    }
    mStackedWidget->setMouseTracking(true);

    centerLayout->addWidget(mStackedWidget, 1);
    mCenterWidget = new QSplitter(this);
    mCenterWidget->addWidget(mMainWidget);
    mCenterWidget->setChildrenCollapsible(false);
    mMainLayout->addWidget(mCenterWidget);

    setCentralWidget(centerWidget);

    //如果直接加到naviBar上,图标的大小是随toolbar的设定,无法改变图标大小;
    TToolButton *winIconBtn = new TToolButton(this);
    if (!this->iconName().contains('.')){
        winIconBtn->setIcon(TRES->icon(this->iconName() + ".white"));
    } else {
        winIconBtn->setIcon(TRES->icon(this->iconName()));
    }

    mWindowTitleBar->setEditableWidgets(QList<QWidget*>() << mPinButton << winIconBtn << mTabBar);


    winIconBtn->setToolTip(QString("%1\n%2").arg(APP->userLogName()).arg(APP->httpUrl()));

    winIconBtn->setFixedSize(TTHEME_DP(40),TTHEME_DP(41));
    winIconBtn->setStyleSheet(QString("TToolButton{background-color: %1; border-width:0px; border-style:none; border-radius:0px}")
                              .arg(TTHEME->value("COLOR_PRIMARY_8").toString()));
    winIconBtn->setIconSize(QSize(TTHEME_DP(32), TTHEME_DP(32)));
    winIconBtn->move(TTHEME_DP(0), TTHEME_DP(0));
    winIconBtn->raise();
}

void SysMainWindow::initDefaultRoute()
{
    QVariantList naviLst = config("desktop").toMap().value("navi").toList();
    QVariantMap defaultRouteItem = searchDefaultRoute(naviLst);
    if (!defaultRouteItem.isEmpty()) {
        QString routeTypeStr = defaultRouteItem.value("route_type").toString();
        RouteType routeType = str2RouteType(routeTypeStr);
        if (routeType == RouteType::Module) {
            routeModule(defaultRouteItem.value("url_address").toString(), defaultRouteItem);
        } else if (routeType == RouteType::Url) {
            routeUrl(defaultRouteItem.value("url_address").toString(), defaultRouteItem);
        }
    }
}

void SysMainWindow::openModule(const QString &iUrl,
                               const QVariant &iConfig)
{
    bool exists = false;
    TopClassAbs *module = APP->openModule(iUrl, &exists);
    if (module == nullptr) {
        TLOG_ERROR(QString("open module(%1) failed.").arg(iUrl));
        return;
    }
    // 默认将菜单中的标题、图标设置到模块
    QVariantMap paramMap = iConfig.toMap();
    QString title = paramMap.value("title_" + APP->language()).toString();
    if (title.isEmpty()) {
        title = paramMap.value("title").toString();
    }
    module->setTitle(title);
    module->setIconName(paramMap.value("icon").toString());

    const QString url = module->url().toUpper();
    if (!exists) {
        connect(module, SIGNAL(windowTitleChanged(QString)),
                this, SLOT(onModuleWindowTitleChanged()),
                Qt::UniqueConnection);
        connect(module, SIGNAL(windowModifyChanged(bool)),
                this, SLOT(onModuleWindowTitleChanged()),
                Qt::UniqueConnection);
    }

    if (!mUrlAddressWidgetMap.contains(url)) {
        mUrlAddressWidgetMap.insert(url, module);
        mUrlAddressConfigMap.insert(url, iConfig.toMap());
    }
    QVariantMap config;
    config.insert("title", module->title());
    config.insert("icon", module->iconName());
    mStackedWidget->addWidget(module);
    mStackedWidget->setCurrentWidget(module);
    updateTabBar(url, config);
}

void SysMainWindow::updateTabBar(const QString &iUrl, const QVariant &iConfigVar)
{
    disconnect(mTabBar, 0, 0, 0);
    // 遍历选项卡,判断是否有已存在此Tab
    bool exists = false;
    int count = mTabBar->count();
    for (int i = 0; i < count; ++i) {
        if (iUrl == mTabBar->tabData(i).toString()) {
            mTabBar->setCurrentIndex(i);
            exists = true;
            break;
        }
    }
    // 不存在,追加
    if (!exists) {
        QVariantMap configMap = iConfigVar.toMap();
        QString iconStr = configMap.value("icon").toString();

        QString title = configMap.value("title").toString();
        title = title.replace("&", "&&");
        int index = mTabBar->addTab(TRES->icon(iconStr, "TAB", "DARK"),
                                    title);
        mTabBar->setTabData(index, iUrl);
        mTabBar->setCurrentIndex(index);
    }
    connect(mTabBar, SIGNAL(currentChanged(int)), this, SLOT(onTabBarCurrentChanged(int)));
    connect(mTabBar, SIGNAL(tabCloseRequested(int)), this, SLOT(onTabBarCloseRequested(int)));
}

void SysMainWindow::updateTabBarWhenRemoveTab(int index)
{
    if (index == mTabBar->currentIndex()) {
        // 若有前一页,激活前一页
        int previous = index - 1;
        if (previous >= 0) {
            QString previousUrl = mTabBar->tabData(previous).toString();
            QWidget *widget = mUrlAddressWidgetMap.value(previousUrl);
            if (widget != nullptr) {
                mStackedWidget->setCurrentWidget(widget);
            }
        } else {
            // 没有前一页,激活后一页
            int next = index + 1;
            if (next < mTabBar->count()) {
                QString nextUrl = mTabBar->tabData(next).toString();
                QWidget *widget = mUrlAddressWidgetMap.value(nextUrl);
                if (widget != nullptr) {
                    mStackedWidget->setCurrentWidget(widget);
                }
            } else {
                // 没有前一页也没有后一页
            }
        }
    }
    mTabBar->removeTab(index);
    update();
}

void SysMainWindow::updatePopupWidget(TopQuickButton *iBtn, const QVariantMap &iConfigVar, RouteType type)
{
    if (iBtn == nullptr) {
        return;
    }

    // 更换标题
    const QString lang = APP->language();
    QString title = iConfigVar.value("title_"+lang).toString();
    if (title.isEmpty()) {
        title = iConfigVar.value("title").toString();
    }
    mPanelMenuTitleLabel->setText(title);

    if (type == RouteType::Menu) {
        // 更换PanelMenu
        if (mNaviDataMap[iBtn]->menuData.isNull()) {
            QVariantList data = iConfigVar.value("items").toList();
            QString status = config("navi_status_without_permission").toString();
            if (status == "disable" || status == "hide") {
                //读取并填入状态信息
                data = APP->parseModuleConfPermission(data, moduleName(), config("navi_status_without_permission").toString());
            }
            //组装数据为PanelMenu需要的结构
            buildPanelMenuData(data);
            mNaviDataMap[iBtn]->menuData = data;
            mPanelMenu->setItemList(data);
        } else {
            mPanelMenu->setItemList(mNaviDataMap[iBtn]->menuData);
        }
        mModuleBarStackedWidget->setCurrentWidget(mPanelMenu);
    } else if (type == RouteType::ModuleBar) {
        QString url = iConfigVar.value("url_address").toString().trimmed().toUpper();
        TopClassAbs *module = nullptr;
        if (mModuleBarUrlAddressMap.contains(url)) {
            module = mModuleBarUrlAddressMap[url];
        } else {
            module = APP->openModule(url);
        }
        if (module != nullptr) {
            mModuleBarUrlAddressMap[url] = module;
            mModuleBarStackedWidget->addWidget(module);
            mModuleBarStackedWidget->setCurrentWidget(module);
        }
    }
}

void SysMainWindow::movePanelMenuWidget()
{
    if (mPopupWidget != nullptr && (!mIsPinned)) {
        auto geometry = this->geometry();
        QRect rect(geometry.x() + mNaviToolBar->width(),
                   geometry.y(),
                   mPopupWidget->width(),
                   geometry.height());
        if (!this->isMaximized()) {
            auto frameGeometry = this->frameGeometry();
            rect.setX(frameGeometry.x() + mNaviToolBar->width());
            rect.setY(frameGeometry.y() + 1);
            rect.setHeight(frameGeometry.height() - 3);
        }
        mPopupWidget->setGeometry(rect);
    }
}

void SysMainWindow::initSysTrayIcon()
{
    mHidetoTrayRegName = APP->productCategory();
    if (mHidetoTrayRegName.isEmpty()) {
        mHidetoTrayRegName = "TopLinker/SysMainWindow/HidetoSysTray";
    } else {
        mHidetoTrayRegName = "TopLinker/" + mHidetoTrayRegName + "/HidetoSysTray";
    }
    mSysTrayTitle = config("sys_tray_product_name").toString();
    if (mSysTrayTitle.isEmpty()) {
        mSysTrayTitle = config("sys_title").toString();
    }
    mHidetoSysTrayFlag = true;
    if (APP->getSetting(mHidetoTrayRegName).toString().isEmpty()) {
        mHidetoSysTrayFlag = false;
    }
    mHidetoSysTray = APP->getSetting(mHidetoTrayRegName, false).toBool();
    mSysTrayIconShow = config("sys_tray_icon_show", false).toBool();
    if (mSysTrayIconShow == false) {
        mHidetoSysTrayFlag = false;
    }
    mSysTrayIcon = new QSystemTrayIcon(this);
    QString sysIcon = config("sys_icon").toString();
    if (sysIcon.isEmpty()) {
        sysIcon = "topmes.#0162fe";
    } else if (sysIcon.contains(".")) {
        sysIcon = sysIcon.split(".").first() + ".#0162fe";
    } else {
        sysIcon += ".#0162fe";
    }
    mSysTrayIcon->setIcon(TRES->colorIcon(sysIcon));
    mSysTrayIcon->setToolTip(this->title());
    QMenu *trayMenu = new QMenu;
    trayMenu->setStyleSheet("QMenu::item{padding-top: 0px;}");
    trayMenu->addAction(ttr("Open") + mSysTrayTitle, [this]{
        this->show();
        this->activateWindow();
    });
    trayMenu->addAction(TRES->colorIcon("power-off.#e54545"),ttr("Quit"), [this]{
        mSysTrayIcon->deleteLater();
        QApplication::exit();
    });
    mSysTrayIcon->setContextMenu(trayMenu);
    connect(mSysTrayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
            this, SLOT(onSysTrayIconActived(QSystemTrayIcon::ActivationReason)));
    if (mSysTrayIconShow) {
        mSysTrayIcon->show();
    } else {
        mSysTrayIcon->hide();
    }
}

SysMainWindow::RouteType SysMainWindow::str2RouteType(const QString &iTypeStr)
{
    if (iTypeStr == "module") {
        return RouteType::Module;
    } else if (iTypeStr == "menu") {
        return RouteType::Menu;
    } else if (iTypeStr == "module_bar") {
        return RouteType::ModuleBar;
    } else if (iTypeStr == "action") {
        return RouteType::Action;
    } else if (iTypeStr == "url") {
        return RouteType::Url;
    } else {
        return RouteType::None;
    }
}

void SysMainWindow::routeUrl(const QString &iUrlAddressStr, const QVariantMap &iConfigVar)
{
    if (!iUrlAddressStr.isEmpty()) {
        if (mUrlAddressWidgetMap.contains(iUrlAddressStr)) {
            QWidget *widget = mUrlAddressWidgetMap.value(iUrlAddressStr);
            TWebView *webview = qobject_cast<TWebView *>(widget);
            if (webview != nullptr) {
                mStackedWidget->setCurrentWidget(widget);
                updateTabBar(iUrlAddressStr);
                webview->navigate(iUrlAddressStr);
            }
        } else {
            TWebView *webview = new TWebView(this);
            webview->setAttribute(Qt::WA_DeleteOnClose);
            mUrlAddressWidgetMap.insert(iUrlAddressStr, webview);
            mUrlAddressConfigMap.insert(iUrlAddressStr, iConfigVar);
            QString title = iConfigVar.value("title_"+APP->language()).toString();
            if (title.isEmpty()) {
                title = iConfigVar.value("title").toString();
            }
            QVariantMap tmp;
            tmp.insert("title", title);
            tmp.insert("icon", iConfigVar.value("icon"));
            mStackedWidget->addWidget(webview);
            mStackedWidget->setCurrentWidget(webview);
            updateTabBar(iUrlAddressStr, tmp);
            webview->navigate(iUrlAddressStr);
        }
    }
}

void SysMainWindow::routeModule(const QString &iUrlAddressStr, const QVariantMap &iConfigVar)
{
    if (mIsRoutingModule) {
        return;
    }
    mIsRoutingModule = true;
    if (!iUrlAddressStr.isEmpty()) {
        if (mUrlAddressWidgetMap.contains(iUrlAddressStr)) {
            QWidget *widget = mUrlAddressWidgetMap.value(iUrlAddressStr);
            if (widget != nullptr) {
                mStackedWidget->setCurrentWidget(widget);
                updateTabBar(iUrlAddressStr);
            }
        } else {
            openModule(iUrlAddressStr, iConfigVar);
        }
    }
    mIsRoutingModule = false;
}

void SysMainWindow::routeAction(const QString &iUrlAddressStr)
{
    callAction(iUrlAddressStr);
}

void SysMainWindow::route(TopQuickButton *iBtn, NaviData *iData, const QVariantMap &iConfigVar)
{
    RouteType type = iData->routeType;
    if (type == RouteType::Module) {
        mPopupWidget->setVisible(false);
        QString urlAddress = iConfigVar.value("url_address").toString().trimmed();
        routeModule(urlAddress, iConfigVar);
    } else if (type == RouteType::Menu || type == RouteType::ModuleBar) {
        movePanelMenuWidget();
        if (mCurrentActiveQuickBtn == iBtn) {
            // 若是同一个
            mPopupWidget->setVisible(!mPopupWidget->isVisible());
        } else {
            // 不是同一个
            if (!mPopupWidget->isVisible()) {
                mPopupWidget->setVisible(true);
            }
        }
        updatePopupWidget(iBtn, iConfigVar, type);
    } else if (type == RouteType::Action) {
        QString urlAddress = iConfigVar.value("url_address").toString().trimmed();
        routeAction(urlAddress);
    } else if (type == RouteType::Url) {
        mPopupWidget->setVisible(false);
        QString urlAddress = iConfigVar.value("url_address").toString().trimmed();
        routeUrl(urlAddress, iConfigVar);
    } else {
        return;
    }
    if (mCurrentActiveQuickBtn != nullptr) {
        mCurrentActiveQuickBtn->setActived(false);
    }
    iBtn->setActived(true);
    mCurrentActiveQuickBtn = iBtn;
}

bool SysMainWindow::nativeEvent(const QByteArray &iEventTypeLst,
                                void *iMsgPtr,
                                long *oResultI64Ptr)
{
#ifdef Q_OS_WIN
    if (mWindowTitleBar != nullptr) {
        MSG* msg = static_cast<MSG *>(iMsgPtr);
        if (mWindowTitleBar->winEvent(msg, oResultI64Ptr)) {
            return true;
        }
    }
#endif
    return QMainWindow::nativeEvent(iEventTypeLst, iMsgPtr, oResultI64Ptr);
}

void SysMainWindow::closeEvent(QCloseEvent *iEvent)
{
    if (!mSysTrayIconShow) {
        if (TMessageBox::msgbox(this, ttr("Are you sure to quit?"), "", "Question", ttr("Quit"),
                                QStringList() << ttr("Quit") + ":Yes:Accept:Warn"
                                << ttr("Cancel") + ":Cancel:Reject:Normal",
                                "Cancel") == "Yes") {
            iEvent->accept();
        } else {
            iEvent->ignore();
        }
        return;
    }
    if (mHidetoSysTrayFlag) {
        if (mHidetoSysTray) {
            this->hide();
            iEvent->ignore();
        } else {
            iEvent->accept();
        }
    } else {
        this->showCloseDialog();
        iEvent->ignore();
    }
}

void SysMainWindow::onOperateTimer()
{
    QDateTime *nowTime = new QDateTime(QDateTime::currentDateTime());
    mNowWindowHideState = this->isHidden();
    if (mPreWindowHideState != mNowWindowHideState) {
        mPreOperateTime = nowTime;
        mPreWindowHideState = mNowWindowHideState;
    }
    mNowWindowActiveState = this->isActiveWindow();
    //若窗体为当前窗体,则根据鼠标位置来判断是否操作
    if (mNowWindowActiveState) {
        mNowMousePos = QCursor::pos();
        if (mPreMousePos != mNowMousePos) {
            mPreOperateTime = nowTime;
            mPreMousePos = mNowMousePos;
        }
    }
    if (mPreWindowActiveState != mNowWindowActiveState) {
        mPreOperateTime = nowTime;
        mPreWindowActiveState = mNowWindowActiveState;
    }
    QVariantMap timeOutMap = config("operate_out_time").toMap();
    int hour = timeOutMap.value("hour").toInt();
    int minute = timeOutMap.value("minute").toInt();
    int second = timeOutMap.value("second").toInt();
    int allSec = hour*60*60 + minute*60 + second;
    if (qAbs(nowTime->secsTo(*mPreOperateTime)) >= allSec) {
        mPreOperateTime = nowTime;
        if ("Yes" == TMessageBox::warning(this, ttr("Operate TimeOut!"), ttr("Operate TimeOut!Please restart the application to update!")
                                          ,"",QStringList() << ttr("Confirm")+":Yes:Yes:Primary" << ttr("Cancel")+":Cancel:Cancel:Normal")) {
            this->close();
        };
    }
}

void SysMainWindow::onQuickBtnClicked()
{
    if (TopQuickButton *btn = qobject_cast<TopQuickButton *>(sender())) {
        if (mNaviDataMap.contains(mCurrentActiveQuickBtn) && (mNaviDataMap[mCurrentActiveQuickBtn]->routeType == RouteType::Menu)) {
            mNaviDataMap[mCurrentActiveQuickBtn]->menuData = mPanelMenu->itemList();
        }
        QVariantMap config = btn->data().toMap();
        if (mNaviDataMap.contains(btn)) {
            NaviData *productData = mNaviDataMap.value(btn);
            route(btn, productData, config);
        } else {
            NaviData *productData = new NaviData;
            productData->routeType = str2RouteType(config.value("route_type").toString());
            mNaviDataMap.insert(btn, productData);
            route(btn, productData, config);
        }
    }
}

void SysMainWindow::onTabBarCurrentChanged(int iIndexInt)
{
    QString url = mTabBar->tabData(iIndexInt).toString();
    QWidget *widget = mUrlAddressWidgetMap.value(url);
    if (widget != nullptr) {
        mStackedWidget->setCurrentWidget(widget);
    }
}

void SysMainWindow::onTabBarCloseRequested(int iIndexInt)
{
    QString url = mTabBar->tabData(iIndexInt).toString();
    QWidget *widget = mUrlAddressWidgetMap.value(url);
    if (widget != nullptr) {
        if (widget->close()) {
            mUrlAddressWidgetMap.remove(url);
            mUrlAddressConfigMap.remove(url);

            updateTabBarWhenRemoveTab(iIndexInt);
        }
    }
}

void SysMainWindow::onNaviButtonClicked()
{
    if (TPushButton *btn = qobject_cast<TPushButton *>(sender())) {
        openModule(btn->data().toMap().value("urlAddress").toString(), btn->data());
    }
}

void SysMainWindow::onPinButtonClicked()
{
    setIsPinned(!mIsPinned);
}

void SysMainWindow::onPanelMenuClicked(const QVariant &iDataVar)
{
    QVariantMap dataMap = iDataVar.toMap();
    dataMap.insert("title", dataMap.value("text"));
    QVariantMap userData = dataMap.value("user_data").toMap();
    QString routeTypeStr = userData.value("route_type").toString();
    RouteType routeType = str2RouteType(routeTypeStr);
    QString urlAddress = userData.value("url_address").toString();
    bool routeSuccessBol = false;
    if (routeType == RouteType::Module) {
        routeSuccessBol = true;
        routeModule(urlAddress, dataMap);
    } else if (routeType == RouteType::Action) {
        routeSuccessBol = true;
        routeAction(urlAddress);
    } else if(routeType == RouteType::Url) {
        routeSuccessBol = true;
        routeUrl(urlAddress, dataMap);
    }
    if (!mIsPinned && routeSuccessBol) {
        mPopupWidget->setVisible(false);
    }
}

void SysMainWindow::onNotification(const QString &iKeyStr, const QVariant &iDataVar, const QString &iUuid)
{
    Q_UNUSED(iUuid);

    if (qobject_cast<TopCore *>(sender()) != nullptr) {
        if (iKeyStr == A_MESSAGECONTROLLER_SHOWOK
                || iKeyStr == A_MESSAGECONTROLLER_SHOWWARN
                || iKeyStr == A_MESSAGECONTROLLER_SHOWINFO) {
            QString msgText = iDataVar.toMap().value("text").toString();
            msgText = fontMetrics().elidedText(msgText, Qt::ElideMiddle, mMessageLabel->width());
            mMessageLabel->setText(msgText);
            QString color;
            if (iKeyStr == A_MESSAGECONTROLLER_SHOWOK) {
                color = "OK";
            } else if (iKeyStr == A_MESSAGECONTROLLER_SHOWWARN) {
                color = "WARN";
            } else if (iKeyStr == A_MESSAGECONTROLLER_SHOWINFO) {
                color = "INFO";
            }
            mMessageLabel->setProperty("SS_COLOR", color);
            mMessageLabel->setStyleSheet(".Q{}");
            mMessageAnimation->setDuration(3000);
            mMessageAnimation->setStartValue(QPoint(0, TTHEME_DP(40)));
            mMessageAnimation->setEndValue(QPoint(0, -mMessageLabel->height()));
            mMessageAnimation->start();
        } else if (iKeyStr == A_MESSAGECONTROLLER_SHOWERROR
                   || iKeyStr == A_MESSAGECONTROLLER_LOADING
                   || iKeyStr == A_MESSAGECONTROLLER_UNLOADING) {
            if (mMessageController != nullptr) {
                mMessageController->onNotifaction(iKeyStr, iDataVar);
            }
        } else if (iKeyStr == A_CORE_PUSH_MODULE_TO_DESKTOP) {
            if (iDataVar.type() == QVariant::Map) {
                QVariantMap param = iDataVar.toMap();
                TopClassAbs *module = APP->getModule(param.value("moduleName").toString(),
                                                     param.value("uid").toString());
                if (module != nullptr) {
                    const QString url = QString("%1/%2").arg(module->moduleName().toUpper()).arg(module->uid());
                    QVariantMap config;
                    config.insert("title", module->windowTitle());
                    config.insert("icon", module->iconName());
                    config.insert("url_address", url);
                    if (!mUrlAddressWidgetMap.contains(url)) {
                        mUrlAddressWidgetMap.insert(url, module);
                        mUrlAddressConfigMap.insert(url, config);
                    }
                    mStackedWidget->addWidget(module);
                    mStackedWidget->setCurrentWidget(module);
                    updateTabBar(url, config);
                }
            }
        } else if (iKeyStr == A_CORE_OPEN_MODULE_TO_DESKTOP) {
            TopClassAbs *module = nullptr;
            if (iDataVar.type() == QVariant::Map) {
                QVariantMap param = iDataVar.toMap();
                module = APP->openModule(QString("%1/%2")
                                         .arg(param.value("moduleName").toString())
                                         .arg(param.value("uid").toString()));
            } else if (iDataVar.type() == QVariant::String) {
                module = APP->openModule(iDataVar.toString());
            }
            if (module != nullptr) {
                connect(module, SIGNAL(windowTitleChanged(QString)), this, SLOT(onModuleWindowTitleChanged()), Qt::UniqueConnection);
                connect(module, SIGNAL(windowModifyChanged(bool)), this, SLOT(onModuleWindowTitleChanged()), Qt::UniqueConnection);

                const QString url = QString("%1%2")
                        .arg(module->moduleName())
                        .arg(module->uid().isEmpty() ? "" : ("/"+module->uid()));
                QVariantMap config;
                if (module->uid().isEmpty()) {
                    config.insert("title", module->title());
                } else {
                    config.insert("title", module->windowTitle());
                }
                config.insert("icon", module->iconName());
                config.insert("url_address", url);
                if (!mUrlAddressWidgetMap.contains(url)) {
                    mUrlAddressWidgetMap.insert(url, module);
                    mUrlAddressConfigMap.insert(url, config);
                    mStackedWidget->addWidget(module);
                }
                mStackedWidget->setCurrentWidget(module);
                updateTabBar(url, config);
            }
        } else if (iKeyStr == "mainwindow_show_other") {
            QString modulePath = iDataVar.toString().trimmed();
            QRegularExpression re("^(?<path>[^/]+/?[^,]*).*$");
            QRegularExpressionMatch match = re.match(modulePath);
            if (match.hasMatch()) {
                modulePath = match.captured("path");
            }
            for (int i = 0; i < mTabBar->count(); ++i) {
                if (mTabBar->tabData(i).toString().toUpper() == modulePath.toUpper()) {
                    mTabBar->setCurrentIndex(i);
                    break;
                }
            }
        } else if (iKeyStr == "mainwindow_close_tab") {
            QString tmpUrl;
            QString tmpUid;
            if (iDataVar.type() == QVariant::Map) {
                QVariantMap param = iDataVar.toMap();
                tmpUrl = param.value("moduleName").toString();
                tmpUid = param.value("uid").toString();
                tmpUrl = QString("%1%2").arg(tmpUrl).arg(tmpUid.isEmpty() ? "" : ("/"+tmpUid));
            } else if (iDataVar.type() == QVariant::String) {
                tmpUrl = iDataVar.toString();
            }
            if (!tmpUrl.isEmpty()) {
                int tmpIndex = -1;
                for (int i = 0; i < mTabBar->count(); ++i) {
                    if (mTabBar->tabData(i).toString().toUpper() == tmpUrl.toUpper()) {
                        tmpIndex = i;
                        break;
                    }
                }
                QWidget *widget = mUrlAddressWidgetMap.value(tmpUrl);
                if (widget == nullptr) {
                    tmpUrl = tmpUrl.toUpper();
                    widget = mUrlAddressWidgetMap.value(tmpUrl);
                }
                if (widget != nullptr) {
                    if (widget->close()) {
                        mUrlAddressWidgetMap.remove(tmpUrl);
                        mUrlAddressConfigMap.remove(tmpUrl);
                        updateTabBarWhenRemoveTab(tmpIndex);
                    }
                }
            }
        }
    }
}

void SysMainWindow::onModuleWindowTitleChanged()
{
    if (TopClassAbs *module = qobject_cast<TopClassAbs*>(sender())) {
        QString url = QString("%1/%2").arg(module->moduleName().toUpper()).arg(module->uid());
        int count = mTabBar->count();
        for (int i = 0; i < count; ++i) {
            if (url == mTabBar->tabData(i).toString()) {
                mTabBar->setTabText(i, module->title() + (module->isDataModified() ? "*" : ""));
            }
        }
    }
}

void SysMainWindow::onSysTrayIconActived(QSystemTrayIcon::ActivationReason reason)
{
    if (QSystemTrayIcon::Trigger == reason) {
        this->show();
        this->activateWindow();
    }
}

void SysMainWindow::initIsPinned()
{
    setIsPinned(false, true);
}

void SysMainWindow::buildPanelMenuData(QVariantList &oMenuDataLst)
{
    const QString lang = APP->language();

    for (QVariant & i : oMenuDataLst) {
        QVariantMap imap = i.toMap();
        QVariantMap tmp;
        QString title = imap.value("title_"+lang).toString();
        if (title.isEmpty()) {
            title = imap.value("title").toString();
        }
        bool isExpanded = false;
        if (imap.contains("is_expanded")) {
            isExpanded = imap.value("is_expanded").toBool();
        }
        tmp.insert("text", title);
        tmp.insert("icon", imap.value("icon"));
        tmp.insert("status", imap.value("status"));
        tmp.insert("unfold", isExpanded);
        QVariantMap userData;
        userData.insert("url_address", imap.value("url_address"));
        userData.insert("route_type", imap.value("route_type"));
        tmp.insert("user_data", userData);
        if (imap.contains("items")) {
            QVariantList items = imap.value("items").toList();
            buildPanelMenuData(items);
            tmp.insert("child_list", items);
        }
        i = tmp;
    }
}

QVariantMap SysMainWindow::searchDefaultRoute(const QVariantList &iNaviLst)
{
    for (QVariant i : iNaviLst) {
        QVariantMap imap = i.toMap();
        if (imap.contains("default_route")) {
            QVariant defaultRouteVar = imap.value("default_route");
            if ((defaultRouteVar.type() == QVariant::Bool) && (defaultRouteVar.toBool())) {
                return imap;
            }
        }
        if (imap.contains("items")) {
            QVariantMap ret = searchDefaultRoute(imap.value("items").toList());
            if (!ret.isEmpty()) {
                return ret;
            }
        }
    }
    return QVariantMap();
}

void SysMainWindow::showCloseDialog()
{
    TDialog *closeDlg = new TDialog(this);
    TFramelessWindowBar *winTitleBar = closeDlg->windowTitleBar();
    winTitleBar->setAutoHideFlags(TFramelessWindowBar::AutoHideHint_All);
    winTitleBar->setFixedHeight(TTHEME_DP(28));
    QHBoxLayout *titleVLayout = new QHBoxLayout(winTitleBar);
    titleVLayout->setSpacing(0);
    titleVLayout->setMargin(0);
    titleVLayout->addSpacing(TTHEME_DP(8));
    titleVLayout->addWidget(winTitleBar->windowTitleLabel());
    titleVLayout->addStretch(1);
    titleVLayout->addWidget(winTitleBar->windowCloseButton());
    closeDlg->setWindowTitle(ttr("Quit"));
    QVBoxLayout *vlayout = new QVBoxLayout(closeDlg);
    vlayout->setContentsMargins(10, 22, 10, 18);
    vlayout->setSpacing(12);
    QLabel *textLabel = new QLabel;
    textLabel->setProperty("SS_FONT", "16");
    textLabel->setProperty("SS_COLOR", QVariant("WARN"));
    textLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
    textLabel->setWordWrap(true);
    textLabel->setText(ttr("Are you sure to quit?"));
    vlayout->addWidget(textLabel);
    vlayout->addStretch();
    TRadioBox *radioBox = new TRadioBox;
    QVariantList itemList;
    QVariantMap itemHide;
    itemHide.insert("name", "hide");
    itemHide.insert("text", ttr("Hide to system tray"));
    QVariantMap itemQuit;
    itemQuit.insert("name", "quit");
    itemQuit.insert("text", ttr("Quit"));
    itemList.append(itemHide);
    itemList.append(itemQuit);
    radioBox->setItemList(itemList);
    if (mHidetoSysTray) {
        radioBox->setCurrentName("hide");
    } else {
        radioBox->setCurrentName("quit");
    }
    TCheckBox *checkbox = new TCheckBox;
    checkbox->setText(ttr("No longer remind"));
    QHBoxLayout *hlayout = new QHBoxLayout;
    hlayout->addStretch();
    hlayout->addWidget(radioBox);
    hlayout->addStretch();
    hlayout->addWidget(checkbox);
    hlayout->addStretch();
    vlayout->addLayout(hlayout);
    vlayout->addStretch();
    QWidget *line = new QWidget;
    line->setFixedHeight(1);
    line->setStyleSheet(QString("background-color:%1;").arg(TTHEME->value("COLOR_LIGHT_BORDER").toString()));
    vlayout->addWidget(line);
    TDialogButtonBox *buttonBox = new TDialogButtonBox;
    buttonBox->setContentsMargins(1, 1, 1, 1);
    buttonBox->findChild<QHBoxLayout*>()->setSpacing(16);
    buttonBox->setCenterButtons(true);
    buttonBox->setButtons(QStringList() << ttr("Ok")+":Ok:Accept:Warn"<<ttr("Cancel")+":Cancel:Reject:Normal");
    vlayout->addWidget(buttonBox);
    connect(buttonBox, SIGNAL(rejected()), closeDlg, SLOT(reject()));
    connect(buttonBox, SIGNAL(accepted()), closeDlg, SLOT(accept()));
    closeDlg->setFixedSize(420, 210);
    int ret = closeDlg->exec();
    if (radioBox->currentName() == "hide") {
        mHidetoSysTray = true;
    } else {
        mHidetoSysTray = false;
    }
    if (checkbox->isChecked()) {
        mHidetoSysTrayFlag = true;
        APP->saveSetting(mHidetoTrayRegName, QVariant::fromValue(mHidetoSysTray));
    }
    if (ret == 1) {
        if (radioBox->currentName() == "hide") {
            this->hide();
        } else {
            mSysTrayIcon->deleteLater();
            QApplication::exit();
        }
    }
}

/*!
 * \brief 修改密码对话框
 * \param iPwdStrengthCheck 是否进行强度检查
 * \param iPwdLengthLimit 密码最小长度限制
 * \param iPwdTypeCount 密码最少种类限制
 */
void SysMainWindow::showPasswordModificationDialog(bool iPwdStrengthCheck, int iPwdLengthLimit, int iPwdTypeCount, const QStringList &iNonPasswordLst)
{
    TopLoginPwdRestDialog dialog;
    dialog.setStrengthCheck(iPwdStrengthCheck);
    dialog.setPwdLengthLimit(iPwdLengthLimit);
    dialog.setPwdTypeCountLimit(iPwdTypeCount);
    dialog.setNonPasswordList(iNonPasswordLst);
    dialog.exec();
}

void SysMainWindow::showAboutDialog()
{
    TopAboutUsDialog dialog;
    const QString lang = APP->language();
    QVariantMap aboutCfg = config("about_us").toMap();
    QString title = aboutCfg.value("title_"+lang).toString();
    if (title.isEmpty()) title = aboutCfg.value("title").toString();
    if (title.isEmpty()) {
        title = this->windowTitle();
        if (!config("product_version").toString().isEmpty()) {
            title += config("product_version").toString();
        }
    }
    dialog.setWindowTitle(title);

    QString copyright = aboutCfg.value("copyright_" + lang).toString();
    if (copyright.isEmpty()) {
        copyright = aboutCfg.value("copyright").toString();
    }

    QString lawdesc = aboutCfg.value("lawdesc_" + lang).toString();
    if (lawdesc.isEmpty()) {
        lawdesc = aboutCfg.value("lawdesc").toString();
    }

    dialog.exec();
}

void SysMainWindow::showModuleVersionDialog()
{
    if (mModuleVersionDialogFinished == false) {
        return;
    }
    mModuleVersionDialogFinished = false;
    TSqlSelectorV2 selector;
    selector.setTable("sys_conf");
    selector.setField(QStringList()<<"id"<<"version"<<"path"<<"modify_time"<<"data"<<"attr_data");
    selector.setFieldFormat("path", "array");
    selector.setFieldFormat("attr_data", "json");
    selector.setWhere("path @> '{sys,module_version}'");
    selector.setOrder("path", Qt::AscendingOrder);
    QVariant data = doThreadWork(new TopClassSqlThread(this), TOPSQLTHREAD_SELECT_ARRAYMAP, QVariant::fromValue(selector));
    TDataResponse dataRes(data.toMap());
    if (dataRes.hasError()) {
        alertError(ttr("Load data failed!"), dataRes.errText());
        return;
    }
    QVariantList tmpList = dataRes.data().toList();
    QVariantList dataList;
    QVariantMap detailMap;
    for (QVariant &var: tmpList) {
        QVariantMap varMap = var.toMap();
        QVariantMap attrData = varMap.value("attr_data").toMap();
        QString title = attrData.value("module_name_" + APP->language()).toString();
        if (title.isEmpty()) {
            title = attrData.value("module_name").toString();
        }
        QVariantMap rowData;
        rowData.insert("id", varMap.value("id"));
        rowData.insert("title", title);
        rowData.insert("name", varMap.value("path").toStringList().last());
        rowData.insert("version", varMap.value("version"));
        rowData.insert("data", varMap.value("data"));
        rowData.insert("time", varMap.value("modify_time"));
        dataList.append(rowData);
        detailMap.insert(varMap.value("id").toString(), rowData);
    }

    TDialog *dialog = new TDialog(this);
    dialog->setWindowModality(Qt::ApplicationModal);
    QVBoxLayout *vlayout = new QVBoxLayout(dialog);
    vlayout->setMargin(0);
    vlayout->setSpacing(0);
    TTableView *tableview = new TTableView(this);
    QVariantList headerItems;
    headerItems << QVariantMap();
    headerItems << QVariantMap{{"name","name"},{"display",ttr("Name")},{"displayRole","$name"},{"size","200"}};
    headerItems << QVariantMap{{"name","title"},{"display",ttr("Title")},{"displayRole","$title"},{"size","200"}};
    headerItems << QVariantMap{{"name","version"},{"display",ttr("Version")},{"displayRole","$version"},{"size","120"}};
    headerItems << QVariantMap{{"name","time"},{"display",ttr("Time")},{"displayRole","$time"}};
    tableview->setHeaderItem(headerItems);
    tableview->setPrimaryKey("id");
    tableview->setDataKeyList(QStringList()<<"id"<<"name"<<"title"<<"version"<<"time");
    connect(tableview, &TTableView::doubleClicked, [this, dialog, tableview, detailMap]() {
        if (tableview->selectedPrimaryKeys().size() > 0) {
            QString sid = tableview->selectedPrimaryKeys().first().toString();
            this->showVersionDetailDialog(dialog, detailMap.value(sid));
        }
    });
    QMenu *menu = new QMenu(tableview);
    QAction *actDetail = menu->addAction(TRES->icon("info-circle"), ttr("Version Log"));
    connect(actDetail, &QAction::triggered, [this, dialog, tableview, detailMap]() {
        if (tableview->selectedPrimaryKeys().size() > 0) {
            QString sid = tableview->selectedPrimaryKeys().first().toString();
            this->showVersionDetailDialog(dialog, detailMap.value(sid));
        }
    });
    tableview->setContextMenu(menu);
    tableview->loadData(dataList);
    vlayout->addWidget(tableview);
    vlayout->addSpacing(6);
    dialog->setWindowIcon(TRES->icon("module_version"));
    dialog->setWindowTitle(ttr("Module Version"));
    dialog->resize(750, 700);
    connect(dialog, &TDialog::finished, [=](){
        mModuleVersionDialogFinished = true;
    });
    dialog->show();
}

void SysMainWindow::showVersionDetailDialog(QWidget *iParent, const QVariant &iDetail)
{
    TDialog *dialog = new TDialog(iParent);
    dialog->setWindowModality(Qt::ApplicationModal);
    QVBoxLayout *vlayout = new QVBoxLayout(dialog);
    vlayout->setMargin(0);
    vlayout->setSpacing(0);
    TTextEdit *detail = new TTextEdit(this);
    detail->setReadOnly(true);
    detail->setText(iDetail.toMap().value("data").toString());
    vlayout->addWidget(detail);
    dialog->setWindowIcon(TRES->icon("info-circle"));
    dialog->setWindowTitle(ttr("Version Log") + QString("[%1]").arg(iDetail.toMap().value("title").toString()));
    dialog->resize(600, 650);
    dialog->show();
}

void SysMainWindow::openModuleUpgrade()
{
    if (!APP->hasRight("sys-module-upgrade-edit")) {
        TMessageBox::warning(this, ttr("No permission to open module upgrade exe!"));
        return;
    }
#ifdef Q_OS_WIN
    const QString exeFileStr = APP->appBinPath() + "/TopModuleUpgrade.exe";
    const QString exeFileStrNew = APP->appBinPath() + "/PackageUploaderPlus.exe";
#else
    const QString exeFileStr = APP->appBinPath() + "/TopModuleUpgrade";
    const QString exeFileStrNew = APP->appBinPath() + "/PackageUploaderPlus";
#endif
    QFileInfo finfo(exeFileStrNew);
    if (finfo.exists()) {
        QProcess::startDetached(exeFileStrNew, QStringList()<<"-l"<<APP->language(), APP->appBinPath());
    } else {
        QDesktopServices::openUrl(QUrl::fromLocalFile(exeFileStr));
    }
}

StretchFrame::StretchFrame(QWidget *iParent)
    :QFrame(iParent)
{
    setAttribute(Qt::WA_Hover, true);
    setMouseTracking(true);
    mStretchWidgetWidth = TTHEME_DP(5);
    mLayout = new QHBoxLayout(this);
    mLayout->setMargin(0);
    mLayout->setSpacing(0);
    mStretchWidget = new QFrame(this);
    mStretchWidget->setFixedWidth(mStretchWidgetWidth);
    mStretchWidget->setStyleSheet("background-color: rgba(0,0,0,0)");
}

void StretchFrame::setWidget(QWidget *iWidget)
{
    if (mWidget != nullptr) {
        delete mWidget;
    }
    mWidget = iWidget;
    mLayout->insertWidget(0, mWidget);
}

void StretchFrame::setStretchWidgetVisible(bool iVisible)
{
    mStretchWidget->setVisible(iVisible);
}

void StretchFrame::mousePressEvent(QMouseEvent *event)
{
    if (this->isTopLevel() && event->button() == Qt::LeftButton){
        mDragPosition = event->globalPos() - this->frameGeometry().topLeft();
        mLeftButtonPressed = true;
    }
    QFrame::mousePressEvent(event);
}

void StretchFrame::mouseReleaseEvent(QMouseEvent *)
{
    mLeftButtonPressed = false;
}

bool StretchFrame::event(QEvent *event)
{
    if (event->type() == QEvent::HoverMove) {
        QHoverEvent *hoverEvent = static_cast<QHoverEvent *>(event);
        QPoint pos = hoverEvent->pos();
        QPoint gp = mapToGlobal(pos);
        if (hoverEvent && !mLeftButtonPressed && (windowFlags() & Qt::Popup)) {
            QPoint br = QPoint(this->width(), this->height());
            int x = pos.x();
            bool widthReizable = (this->minimumWidth() != this->maximumWidth());
            if (x < br.x() && x >= br.x() - mStretchWidgetWidth && widthReizable) {
                if (cursor().shape() != Qt::SizeHorCursor) {
                    QApplication::setOverrideCursor(QCursor(Qt::SizeHorCursor));
                    setCursor(QCursor(Qt::SizeHorCursor));
                }
            } else if (cursor().shape() != Qt::ArrowCursor) {
                QApplication::restoreOverrideCursor();
                setCursor(QCursor(Qt::ArrowCursor));
            }
        } else if (mLeftButtonPressed && (cursor().shape() == Qt::SizeHorCursor)) {
            QRect rect = this->rect();
            QPoint tl = mapToGlobal(rect.topLeft()); //top left
            QPoint br = mapToGlobal(rect.bottomRight()); //bottom right
            QRect rectMove(tl, br);
            if (gp.x() - tl.x() > minimumWidth()){
                rectMove.setWidth(gp.x() - tl.x());
            }
            this->setGeometry(rectMove);
        }
    } else if (event->type() == QEvent::Resize) {
        if (mWidget != nullptr) {
            qreal width = size().width();
            qreal height = size().height();
            mWidget->resize(width, height);
        }
        const QRect rect = geometry();
        mStretchWidget->setGeometry((rect.width() - mStretchWidgetWidth),
                                    0, mStretchWidgetWidth, rect.height());
        mStretchWidget->raise();
    } else if (event->type() == QEvent::Hide) {
        mLeftButtonPressed = false;
        QApplication::restoreOverrideCursor();
    } else if (event->type() == QEvent::Show) {
        setFocus();
    }
    return QFrame::event(event);
}

void StretchFrame::showEvent(QShowEvent *event)
{
    QFrame::showEvent(event);
    activateWindow();
}

StackedWidget::StackedWidget(QWidget *iParent)
    : QStackedWidget(iParent)
{

}

void StackedWidget::setBackground(const QString &iBackground)
{
    mBackground = iBackground;
}

void StackedWidget::paintEvent(QPaintEvent *iEvent)
{
    Q_UNUSED(iEvent)

    if (!mBackground.isEmpty()) {
        QPainter p(this);
        p.drawPixmap(this->rect(), QPixmap(mBackground));
    }
}