#include "MainWindow.h"
#include "ui_MainWindow.h"

#include "IpEntriesPanel/IpEntriesPanel.h"
#include "StatisticPanel/StatisticPanel.h"
#include "StatisticPanel/StatisticModel.h"
#include "AddLtrdConnectionDialog/AddLtrdConnectionDialog.h"
#include "LtrdConnectionSettings/LtrdConnectionSettings.h"
#include "Log/LtrdLogPanel.h"

#include <QIcon>
#include <QCloseEvent>
#include <QMenu>
#include <QSettings>
#include <QMessageBox>
#include <QTableView>
#include <QCoreApplication>
#include <QScopedPointer>
#include <QLibraryInfo>

#include "Crates/CEU/CratePluginCEU.h"
#include "Crates/EU/CratePluginEU.h"
#include "Crates/SyncCtl/CratePluginSyncCtl.h"



MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent), m_env(this),
    ui(new Ui::MainWindow) {
    ui->setupUi(this);
    this->setWindowTitle(QCoreApplication::applicationName() + " - " + QCoreApplication::applicationVersion());

    createLanguageMenu();

    ui->m_cratesTree->setContextMenuPolicy(Qt::CustomContextMenu);
    connect(ui->m_cratesTree, SIGNAL(customContextMenuRequested(QPoint)),
            SLOT(showCratesTreeMenu(QPoint)));
#ifndef Q_WS_WIN
    setWindowIcon(QIcon(":/icons/ltr.png"));
#endif

    m_icon_ok = QIcon(":/icons/ltr_green.png");
    m_icon_no_crates = QIcon(":/icons/ltr_yellow.png");
    m_icon_idle = QIcon(":/icons/ltr_red.png");
    m_icon_err = QIcon(":/icons/ltr_red.png");

    /* устанавливаем иконку в трее */
    tray = new QSystemTrayIcon(m_icon_idle);
    tray->setToolTip(PRJ_APP_TITLE);

    QMenu* tray_menu = new QMenu(this);


    tray_menu->addAction(ui->actionShowWnd);
    tray_menu->addAction(ui->actionQuit);

    tray->setContextMenu(tray_menu);

    connect(tray, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
            SLOT(trayActivated(QSystemTrayIcon::ActivationReason)));
    tray->show();


    ui->m_cratesTree->setModel(m_env.cratesModel());
    connect(m_env.cratesModel(), SIGNAL( rowsInserted( const QModelIndex &, int , int )),
            SLOT(addedCrateRow(QModelIndex,int,int)));

    m_statPanel = new StatisticPanel(this);
    this->addDockWidget(Qt::RightDockWidgetArea, m_statPanel);
    connect(ui->actionShowStat, SIGNAL(triggered(bool)), m_statPanel, SLOT(setVisible(bool)));
    connect(m_statPanel, SIGNAL(visibilityChanged(bool)), ui->actionShowStat,
            SLOT(setChecked(bool)));

    m_ipEntriesPanel = new IpEntriesPanel(this);
    this->addDockWidget(Qt::RightDockWidgetArea , m_ipEntriesPanel);
    connect(ui->actionIPEntries, SIGNAL(triggered(bool)), m_ipEntriesPanel, SLOT(setVisible(bool)));
    connect(m_ipEntriesPanel, SIGNAL(visibilityChanged(bool)), ui->actionIPEntries,
            SLOT(setChecked(bool)));    

    m_logPanel = new LtrdLogPanel(this);
    this->addDockWidget(Qt::BottomDockWidgetArea, m_logPanel);
    connect(ui->actionSrvLog, SIGNAL(triggered(bool)), m_logPanel, SLOT(setVisible(bool)));
    connect(m_logPanel, SIGNAL(visibilityChanged(bool)), ui->actionSrvLog,
            SLOT(setChecked(bool)));


    connect(ui->m_cratesTree->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
            SLOT(crateSelChanged(QModelIndex,QModelIndex)));



    QSettings set;
    set.beginGroup("MainWindows");
    restoreState(set.value("state").toByteArray());
    resize(set.value("size", QSize(700, 700)).toSize());
    move(set.value("pos", QPoint(200, 100)).toPoint());

    if (set.value("statisticColumnsState").toByteArray().size()!=0)
        m_statPanel->view()->header()->restoreState(set.value("statisticColumnsState").toByteArray());

    if (set.value("ipAddrsColumnsState").toByteArray().size()!=0)
        m_ipEntriesPanel->view()->horizontalHeader()->restoreState(set.value("ipAddrsColumnsState").toByteArray());

    set.endGroup();

    savePosition();

    setLanguage(set.value("lang").toString());

    retranslateUi();

    int len = set.beginReadArray("LtrdConnections");
    if (len) {
        for (int i=0; i < len; i++) {
            set.setArrayIndex(i);
            LtrdConnection* con =  new LtrdConnection(QHostAddress(set.value("ip-addr").toString()),
                                                          set.value("autocon", "True").toBool());
            addConnection(con, con->autoconnect());
        }
    } else {
        addConnection(new LtrdConnection(), true);
    }
    set.endArray();



    new CratePluginCEU(&m_env);
    new CratePluginEU(&m_env);
    new CratePluginSyncCtl(&m_env);
    QList<QAction *> crateActions = m_env.actions(LtrManagerEnv::ActionGroupCrateInstance);
    foreach (QAction *action, crateActions) {
        ui->menuCrate->addAction(action);
    }

}

MainWindow::~MainWindow() {
    delete ui;
}

void MainWindow::addConnection(LtrdConnection *con, bool autocon) {
    m_env.cratesModel()->addNewSrvCon(con);
    connect(con, SIGNAL(crateChanged(LtrCrateInfo*,LtrConEvent)),
            SLOT(crateInfoChanged(LtrCrateInfo*,LtrConEvent)));
    connect(con, SIGNAL(statusChanged(LtrdConnection::connStatus)),
            SLOT(ltrdStatusChanged(LtrdConnection::connStatus)));

    if (autocon)
        con->start();
}

void MainWindow::startCurConnection() {
    LtrdConnection* con;
    m_env.cratesModel()->getInfo(ui->m_cratesTree->currentIndex(), &con);
    if (con) {
        con->start();
    }
}

void MainWindow::stopCurConnection() {
    LtrdConnection* con;
    m_env.cratesModel()->getInfo(ui->m_cratesTree->currentIndex(), &con);
    if (con) {
        con->closeConnection();
    }
}

void MainWindow::getCurConInfo(LtrdConnection **con, LtrCrateInfo **crate, int *slot, TLTR_MODULE_STATISTIC *mstat) {
    LtrdConnection *fnd_con;
    LtrCrateInfo *fnd_crate;
    int fnd_slot;

    m_env.cratesModel()->getInfo(ui->m_cratesTree->currentIndex(), &fnd_con, &fnd_crate, &fnd_slot);
    if (mstat && fnd_con && fnd_crate && (fnd_slot!=StatisticModel::invalidSlot)) {
        *mstat =fnd_crate->mstat(fnd_slot);
    }

    if (con)
        *con = fnd_con;
    if (crate)
        *crate = fnd_crate;
    if (slot)
        *slot = fnd_slot;
}

void MainWindow::createLanguageMenu() {
    QActionGroup *langGroup = new QActionGroup(ui->menuLanguage);
    connect(langGroup, SIGNAL(triggered(QAction*)), this,
            SLOT(onLanguageChanged(QAction*)));


    ui->actionLangSystem->setData("");
    langGroup->addAction(ui->actionLangSystem);

    ui->actionLangRussian->setData("ru");
    langGroup->addAction(ui->actionLangRussian);

    ui->actionLangEnglish->setData("en");
    langGroup->addAction(ui->actionLangEnglish);
}

void MainWindow::closeEvent(QCloseEvent *event) {
    saveSettings();
    hideToTray();
}

bool MainWindow::event(QEvent *event) {
    if (event->type()==QEvent::WindowStateChange) {
        if (windowState()==Qt::WindowMinimized) {
        //    savePosition();
        }
    }
    return QMainWindow::event(event);
}

void MainWindow::changeEvent(QEvent *event) {
    switch (event->type()) {
        case QEvent::WindowStateChange:
            savePosition();
            break;
        case QEvent::LanguageChange:
            retranslateUi();
            break;
    }

    QMainWindow::changeEvent(event);
}

void MainWindow::retranslateUi() {
    ui->retranslateUi(this);
    ui->menuLtrd->setTitle(LTR_SERVICE_NAME);    

    ui->actionQuit->setToolTip(tr("Quit from %1").arg(PRJ_APP_TITLE));
    ui->actionModuleReset->setToolTip(tr("Reset selected %1 module").arg(LTR_MODULE_NAME));

    ui->actionLtrdCon->setText(tr("Connect to %1").arg(LTR_SERVICE_NAME));
    ui->actionLtrdDisc->setText(tr("Disconnect from %1").arg(LTR_SERVICE_NAME));
    ui->actionLtrdConAdd->setText(tr("Add new %1 connection").arg(LTR_SERVICE_NAME));
    ui->actionLtrdConRem->setText(tr("Delete %1 connection").arg(LTR_SERVICE_NAME));
    ui->actionLtrdSettings->setText(tr("%1 settings...").arg(LTR_SERVICE_NAME));
    ui->actionLtrdSettings->setToolTip(tr("Change %1 settings").arg(LTR_SERVICE_NAME));
    ui->actionSrvLog->setText(tr("%1 log").arg(LTR_SERVICE_NAME));
    ui->actionSrvLog->setToolTip(tr("Show %1 log panel").arg(LTR_SERVICE_NAME));

    m_env.retranslate();
}

void MainWindow::trayActivated(QSystemTrayIcon::ActivationReason reason) {
    if (reason == QSystemTrayIcon::Trigger) {
        showHideMainWindow();
    }
}

void MainWindow::on_actionQuit_triggered() {

    QList<LtrdConnection *> m_cons = m_env.cratesModel()->connectionList();
    Q_FOREACH (LtrdConnection *con, m_cons) {
        con->closeConnection();
    }

    Q_FOREACH (LtrdConnection *con, m_cons) {
        con->wait();
    }

    tray->hide();
    saveSettings();
    qApp->quit();
}

void MainWindow::crateInfoChanged(LtrCrateInfo *info, LtrConEvent event) {
    if ((event == LTR_CONEVENT_ADD) || (event == LTR_CONEVENT_REMOVE))
        setIconStatus();


#ifdef ENABLE_TRAY_MSG
    if (event == LTR_CONEVENT_ADD)
    {
        tray->showMessage(tr("Crate connected"), tr("Connected crate: ") + info->infoString());
    }
    else if (event == LTR_CONEVENT_REMOVE)
    {
        tray->showMessage(tr("Crate disconnected"), tr("Disconnected crate: ") + info->infoString());
    }
#endif

}

void MainWindow::ltrdStatusChanged(LtrdConnection::connStatus status) {
    LtrdConnection* snd_con = qobject_cast<LtrdConnection*>(sender());
    LtrdConnection* con;
    getCurConInfo(&con);

    if (con==snd_con) {
        ui->actionLtrdSettings->setEnabled(con->status()==LtrdConnection::STATUS_ONLINE);
        if ((status == LtrdConnection::STATUS_CONNECTING) ||
                (status == LtrdConnection::STATUS_ONLINE)) {
            ui->actionLtrdCon->setEnabled(false);
            ui->actionLtrdDisc->setEnabled(true);
        } else {
            ui->actionLtrdCon->setEnabled(true);
            ui->actionLtrdDisc->setEnabled(false);
        }
    }

    setIconStatus();

#ifdef ENABLE_TRAY_MSG
    if (status == LtrSrvConnection::STATUS_ONLINE) {
        tray->showMessage("ltrd", tr("New ltrd connection"));
    }
    else if (status == LtrSrvConnection::STATUS_ERROR) {
        tray->showMessage("ltrd", tr("ltrd connection closed by error!"), QSystemTrayIcon::Critical);
    }
#endif
}

void MainWindow::hideToTray() {
    savePosition();
    m_ipEntriesPanel->hide();
    m_logPanel->hide();
    hide();
}

void MainWindow::saveSettings() {
    QSettings set;
    set.beginGroup("MainWindows");
    /* если коно в настоящий момент не свернуто - то сохраняем его текущие
       параметры */
    if (this->isVisible() && !isMinimized()) {
        set.setValue("state", saveState());
        set.setValue("size", size());
        set.setValue("pos", pos());
    } else {
        /* иначе - сохраняем те параметры, которые были до сворачивания */
        set.setValue("state", m_winState);
        set.setValue("size", m_size);
        set.setValue("pos", m_pos);
    }

    set.setValue("statisticColumnsState", m_statPanel->view()->header()->saveState());
    set.setValue("ipAddrsColumnsState", m_ipEntriesPanel->view()->horizontalHeader()->saveState());

    set.endGroup();

    set.beginWriteArray("LtrdConnections");
    QList<LtrdConnection*> conList = m_env.cratesModel()->connectionList();

    for (int i=0; i < conList.size(); i++) {
        set.setArrayIndex(i);
        set.setValue("ip-addr", conList.at(i)->addr().toString());
        set.setValue("autocon", conList.at(i)->autoconnect());
    }
    set.endArray();

    set.setValue("lang", m_curLangName);
}

void MainWindow::savePosition() {
    if (!isMinimized()) {
        m_winState = saveState();
        m_size = size();
        m_pos = pos();
    }
}

/* Изменение состояния приложения при смене выбранного соединения/крейта/модуля */
void MainWindow::crateSelChanged(QModelIndex current, QModelIndex previous) {
    LtrdConnection* con;
    LtrCrateInfo* crate;
    TLTR_MODULE_STATISTIC mstat;
    int slot;    
    m_env.cratesModel()->getInfo(current, &con, &crate, &slot);
    m_statPanel->setItem(con, crate, slot);
    m_ipEntriesPanel->setConnection(con);
    m_logPanel->setConnection(con);


    if (crate && (slot!=StatisticModel::invalidSlot))
        mstat = crate->mstat(slot);


    ui->actionModuleReset->setEnabled(con && crate && (slot!=StatisticModel::invalidSlot) && (mstat.mid!=LTR_MID_EMPTY));
    ui->actionLtrdConRem->setEnabled(con);


    ui->actionLtrdCon->setEnabled(con && (con->status()!=LtrdConnection::STATUS_CONNECTING)
                                          && (con->status()!=LtrdConnection::STATUS_ONLINE));
    ui->actionLtrdDisc->setEnabled(con && ((con->status()==LtrdConnection::STATUS_CONNECTING)
                                           || (con->status()==LtrdConnection::STATUS_ONLINE)));
    ui->actionLtrdSettings->setEnabled(con && (con->status()==LtrdConnection::STATUS_ONLINE));
    ui->actionLtrdAutoCon->setEnabled(con);

    ui->actionLtrdAutoCon->setChecked(con && con->autoconnect());


    m_env.crateSelectionChanged(current, previous);
}

/* данный слот используется, чтобы развернуть дерево с вновь добавленным крейтом */
void MainWindow::addedCrateRow(const QModelIndex &index, int x, int y) {
    if (index.parent().isValid()) {
        ui->m_cratesTree->expand(index.parent());
        ui->m_cratesTree->expand(index);
    }
}

void MainWindow::resetCurModule() {
    LtrdConnection* con;
    LtrCrateInfo* crate;
    TLTR_MODULE_STATISTIC mstat;
    int slot;
    getCurConInfo(&con, &crate, &slot, &mstat);


    if (con && crate && (slot!=StatisticModel::invalidSlot)) {
        /* если с клиентом установлены соединения, то перед сбросом уточняем
         * дополнительно, действительно ли этого хочет клиент */
        if ((mstat.client_cnt==0) ||
               (QMessageBox::warning(this, tr("Warning"), tr("There is opened client connection with this module.") +
                                     "\n" + tr("If you reset module this connection will be closed!") +
                                     "\n" + tr("You still want to reset this module?"),
                                    QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
                == QMessageBox::Yes)) {
            con->moduleReset(crate, slot);
        }
    }
}

/* отображение контекстного меню для дерева активных соединений/крейтов */
void MainWindow::showCratesTreeMenu(QPoint point) {
    LtrdConnection* con;
    LtrCrateInfo* crate;
    TLTR_MODULE_STATISTIC mstat;
    int slot;
    getCurConInfo(&con, &crate, &slot, &mstat);

    /* показываем контекстное меню в зависимости от того, что выбрано */
    m_crateContextMenu.clear();

    if (!crate) {
        /* выбрано соединение */
        m_crateContextMenu.addAction(ui->actionLtrdCon);
        m_crateContextMenu.addAction(ui->actionLtrdDisc);
        m_crateContextMenu.addAction(ui->actionLtrdSettings);
        m_crateContextMenu.addAction(ui->actionLtrdAutoCon);
        m_crateContextMenu.addAction(ui->actionLtrdConAdd);
        m_crateContextMenu.addAction(ui->actionLtrdConRem);
    } else if (con && crate && (slot==StatisticModel::invalidSlot)) {
        /* выбран крейт */
        QList<QAction *> actions = m_env.actions(LtrManagerEnv::ActionGroupCrateInstance);
        foreach (QAction *action, actions) {
            m_crateContextMenu.addAction(action);
        }
    } else if (con && crate && (slot!=StatisticModel::invalidSlot)) {
        /* выбран модуль */
        m_crateContextMenu.addAction(ui->actionModuleReset);
    }
    m_crateContextMenu.popup(ui->m_cratesTree->mapToGlobal(point));

}

void MainWindow::addLtrdConnectionDialog() {
    AddLtrdConnectionDialog dialog(this);
    if (dialog.exec()==QDialog::Accepted) {
        addConnection(new LtrdConnection(dialog.addr(), dialog.autocon()), dialog.needConnect());
        setIconStatus();
    }
}

void MainWindow::remLtrdConnection() {
    LtrdConnection* con;
    getCurConInfo(&con);
    if (con) {
        con->closeConnection();
        con->remove();
        m_env.cratesModel()->removeSrvCon(con);
        setIconStatus();
    }
}

void MainWindow::ltrdConnectionSettingsDialog() {
    LtrdConnection* con;
    getCurConInfo(&con);
    if (con) {
        LtrdConnectionSettings dialog(con, m_logPanel->logLevelNames(), this);
        dialog.exec();
    }
}

void MainWindow::LtrdAutoconChanged() {
    LtrdConnection* con;
    getCurConInfo(&con);
    if (con) {
        con->setAutoconnect(ui->actionLtrdAutoCon->isChecked());
    }
}

/* Изменение цвета иконки в трее в зависимости от текущего состояния подключений */
void MainWindow::setIconStatus() {
    int cratesCnt = m_env.cratesModel()->cratesCnt();
    if (cratesCnt) {
        tray->setIcon(m_icon_ok);
    } else {
        QList<LtrdConnection*> conlist = m_env.cratesModel()->connectionList();
        int err_cnt=0, online_cnt=0;
        for (int i=0; i < conlist.size(); i++) {
            if (conlist.at(i)->status()==LtrdConnection::STATUS_ERROR) {
                err_cnt++;
            } else if (conlist.at(i)->status()==LtrdConnection::STATUS_ONLINE) {
                online_cnt++;
            }
        }

        if (online_cnt!=0) {
            tray->setIcon(m_icon_no_crates);
        } else if (err_cnt!=0) {
            tray->setIcon(m_icon_err);
        } else {
            tray->setIcon(m_icon_idle);
        }
    }

}

void MainWindow::showHideMainWindow(bool hide_enable) {
    /* восстановление из свернутого состояния */
    if (!this->isVisible()) {
        restoreState(m_winState);
        resize(m_size);
        move(m_pos);
        showNormal();
        activateWindow();
    } else if (!this->isActiveWindow() &&
             !m_ipEntriesPanel->isActiveWindow() &&
             !m_statPanel->isActiveWindow() &&
             !m_logPanel->isActiveWindow()) {
        /* просто отображение в случае, если окно закрыто другими */
        if (isMinimized())
            restoreState(m_winState);
        showNormal();
        activateWindow();
    } else if (hide_enable) {
        /* если окно было активно - то прячем его */
        hideToTray();
    }
}

void MainWindow::anotherAppMsgRecvd(const QString &string) {
    if (string == "activate") {
        showHideMainWindow(false);
    }
}

void MainWindow::onLanguageChanged(QAction *act) {
    QString locName = act->data().toString();
    if (locName!=m_curLangName)
        setLanguage(locName);
}

void MainWindow::setLanguage(const QString &name) {
    m_curLangName = name;
    QLocale loc;
    if (name.isEmpty()) {
        loc = QLocale::system();
    } else {
        loc = QLocale(name);
    }

    foreach (QAction *act, ui->menuLanguage->actions()) {
        if (act->data().toString()==name) {
            act->setChecked(true);
        }
    }

    switchTranslator(m_translator, loc, "ltrmanager", PRJ_TRANSLATIONS_DIR);
    switchTranslator(m_translatorLboot, loc, "qlbootdialog", PRJ_TRANSLATIONS_DIR);
    switchTranslator(m_translatorQt, loc, "qt",
                 #ifdef _WIN32
                     PRJ_TRANSLATIONS_DIR
                 #else
                     QLibraryInfo::location(QLibraryInfo::TranslationsPath)
                 #endif
                     );
}

void MainWindow::switchTranslator(QTranslator &translator, QLocale &locale,
                                  const QString &name, const QString &dir) {
    qApp->removeTranslator(&translator);

#if (QT_VERSION >= QT_VERSION_CHECK(4, 8, 0))
    translator.load(locale, name, "_", dir);
#else
    translator.load(name + "_" + locale.name(), dir);
#endif
    qApp->installTranslator(&translator);
}
