#include <QXmlStreamWriter>
#include <QXmlStreamReader>
#include <QXmlStreamAttribute>
#include <QFile>
#include <QMessageBox>
#include <QThread>
#include <QErrorMessage>
#include "serverlmswidget.h"
#include "dialogsettingstray.h"
#include "ui_serverlmswidget.h"
#include "metatypes.h"

const QString ServerLMSWidget::languageENG = "en_EN";
const QString ServerLMSWidget::languageRUS = "ru_RU";

ServerLMSWidget::ServerLMSWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::ServerLMSWidget),
    waitAnimationWidget(nullptr),
    server(nullptr),
    updateThread(nullptr),
    loggerThread(nullptr),
    mutex(nullptr),
    dataParser(nullptr),
    processingSystem(nullptr),
    updateController(nullptr),
    assetsManager(nullptr),
    commonClientHandler(nullptr),
    chatSystem(nullptr),
    docsUpdater(nullptr),
    providerDBLMS(nullptr),
    first (true),
    language(languageENG),
    errorCode(0),
    flStartInitialization(false)
{
    ui->setupUi(this);

    qDebug() << "ServerLMSWidget init thread ID " << QThread::currentThreadId();

    registerMetaType();

    updateMyStyleSheet();

    setLanguageInterfase();

    waitAnimationWidget = new WaitAnimationWidget;
    QMovie *movie = new QMovie(":/resources/icons/762.gif");
    waitAnimationWidget->setParent(this);
    waitAnimationWidget->initialize(movie,this);

    waitAnimationWidget->showWithPlay();
}

ServerLMSWidget::~ServerLMSWidget()
{
    if(flStartInitialization)
    {
        server->stopServer();

        updateThread->quit();
        updateThread->wait();
        delete updateThread;

        delete server;
        delete commonClientHandler;
        delete dataParser;
        delete processingSystem;
        delete updateController;
        delete docsUpdater;
        delete assetsManager;
        delete chatSystem;

        delete mutex;

        loggerThread->quit();
        loggerThread->wait();
        delete loggerThread;

        delete providerDBLMS;
    }

    waitAnimationWidget->hideWithStop();
    delete waitAnimationWidget;

    delete ui;
}

void ServerLMSWidget::changeEvent(QEvent *event)
{
    // В случае получения события изменения языка приложения
    if (event->type() == QEvent::LanguageChange)
    {
        ui->retranslateUi(this);    // переведём окно заново

        updateStateServer();
    }
}

void ServerLMSWidget::resizeEvent(QResizeEvent *event)
{
    QSize size = event->size();
    waitAnimationWidget->resize(size);
}

void ServerLMSWidget::slot_UpdateListClients()
{
    //Очищаем список
    ui->listWidget_Clients->clear();

    for (const ClientHandler *handler : server->getClientsMap()->values())
    {
        QString strClient = handler->getClient()->getFullName();

        ui->listWidget_Clients->addItem(strClient);
        ui->listWidget_Clients->scrollToBottom();
        ui->listWidget_Clients->setCurrentRow(ui->listWidget_Clients->count() - 1);
    }

    int countClients = (*server->getClientsMap()).count();
    Logger::instance().log("SERVER: countClients = " + QString::number(countClients));
}

void ServerLMSWidget::slot_AddMessageToLog(QString message)
{
    ui->loggerTextField->appendPlainText(message);
}

void ServerLMSWidget::slot_ErrorPostgreSQL(QString text)
{
    emit signal_Menu_ShowWindow();
    QMessageBox::critical(this, tr("Error PostgreSQL!"),text);
}

void ServerLMSWidget::slot_UpdateDocs()
{
    QApplication::setOverrideCursor(Qt::WaitCursor);

    if(docsUpdater->updateDocsXML())
        emit signal_DocsChanged();

    QApplication::restoreOverrideCursor();
}

void ServerLMSWidget::slot_startInitialization_step1()
{
    Logger::instance().log("Update docs.xml...");
    slot_UpdateDocs();
    Logger::instance().log("Update docs.xml completed!");

    ui->btnStopServer->setEnabled(false);
    ui->btnStartServer->setEnabled(true);

    flStartInitialization = true;

    updateStateServer();

    QApplication::restoreOverrideCursor();

    if(hasError() == 100)
        return;

    Logger::instance().log("Try connection to DB...");
    tryConnectionToDB();

    waitAnimationWidget->hideWithStop();
}

void ServerLMSWidget::slot_setVersion(QString versionStr)
{
    ui->lblVersionText->setText(versionStr);
}

void ServerLMSWidget::start()
{
    startInitialization_step0();

}

void ServerLMSWidget::slot_BlockAutorization(bool block)
{
    if(block)
    {
        server->blockAutorization();
        ui->lblBlockAuth->setPixmap(QPixmap(QStringLiteral(":/resources/icons/lock.png")));
        //emit signal_Tray_ShowMessage(tr("Authorization blocked!"));
    }
    else
    {
        server->unBlockAutorization();
        ui->lblBlockAuth->setPixmap(QPixmap(QStringLiteral(":/resources/icons/open.png")));
        //emit signal_Tray_ShowMessage(tr("Authorization unblocked!"));
    }
}

void ServerLMSWidget::slot_LanguageChanged(QString language)
{
    qtLanguageTranslator.load(QString("translations/ServerLMS_") + language, ".");
    qApp->installTranslator(&qtLanguageTranslator);

    emit signal_LanguageChanged(language);
}

void ServerLMSWidget::on_btnStartServer_clicked()
{
    if(server->startServer())
    {
        QApplication::setOverrideCursor(Qt::WaitCursor);
        QApplication::restoreOverrideCursor();

        ui->btnStartServer->setEnabled(false);
        ui->btnStopServer->setEnabled(true);
        slot_BlockAutorization(false);

        emit signal_Tray_ShowMessage(tr("Server is started!"));
    }
    updateStateServer();
}

void ServerLMSWidget::on_btnStopServer_clicked()
{
    if(server->stopServer())
    {
        ui->btnStopServer->setEnabled(false);
        ui->btnStartServer->setEnabled(true);
        slot_BlockAutorization(true);

        emit signal_Tray_ShowMessage(tr("Server is stoped!"));
    }
    updateStateServer();
}

void ServerLMSWidget::on_btnSettings_clicked()
{
    ServerDBSettings settingsTemp;
    if(!DialogSettingsTray::loadSettings(&settingsTemp))
    {
        QMessageBox::critical(this, tr("Error!"), tr("Settings file could not be opened:") + "'config/settings.xml'");
        return;
    }

    DialogSettingsTray dlg(providerDBLMS, this);
    dlg.setWindowFlags(dlg.windowFlags() & ~Qt::WindowContextHelpButtonHint);

    connect(&dlg, &DialogSettingsTray::signal_LanguageChanged, this, &ServerLMSWidget::slot_LanguageChanged);
    //connect(&dlg, &DialogSettingsTray::signal_UpdateStyleSheet, this, &ServerLMSWidget::slot_UpdateStyleSheet);

    connect(&dlg, &DialogSettingsTray::signal_UpdateDocs, this, &ServerLMSWidget::slot_UpdateDocs);


    switch( dlg.exec() )
    {
    case QDialog::Accepted:
    {
        language = dlg.getSettings().Language;

        if(dlg.settingsDBisChanged())
        {
            on_btnStopServer_clicked();

            providerDBLMS->deAuthorizationAll();

            providerDBLMS->DisConnectionFromDB();

            updateStateServer();

            QMessageBox::warning(this, tr("Warning!"), tr("Database settings have been changed.\nThe server will be restarted."));

            tryConnectionToDB();
        }

        break;
    }
    case QDialog::Rejected:
        break;
    default:
        break;
    }
}

void ServerLMSWidget::slot_trySetServerState(PacketType packetType)
{
    if (packetType == PacketType::BUSY)
    {
        server->blockAutorization();
        updateStateServer();
    }
    else if (packetType == PacketType::FREE)
    {
        server->unBlockAutorization();
        updateStateServer();
    }
}

void ServerLMSWidget::setLanguageInterfase()
{
    ServerDBSettings settings;
    DialogSettingsTray::loadSettings(&settings);

    if(settings.Language == "ENG")
    {
        qtLanguageTranslator.load(QString("translations/ServerLMS_") + languageENG, ".");
        language = languageENG;
    }
    else
    {
        qtLanguageTranslator.load(QString("translations/ServerLMS_") + languageRUS, ".");
        language = languageRUS;
    }

    qApp->installTranslator(&qtLanguageTranslator);

    emit signal_LanguageChanged(language);
}

void ServerLMSWidget::updateMyStyleSheet()
{
    QString styleSheet = loadStyleSheet();
    styleSheet = styleSheet.replace("\n", "");

    this->setStyleSheet(styleSheet);
}

QString ServerLMSWidget::loadStyleSheet()
{
    QString fileName = "./resources/css/styleSheetMain.css";
    QFile styleSheetFile(fileName);
    if (!styleSheetFile.open(QFile::ReadOnly | QFile::Text))
    {
        QMessageBox::warning(this, tr("Warning!"), tr("The file could not be opened ") + fileName);
        return QStringLiteral("");
    }
    else
    {
        QByteArray byteArray = styleSheetFile.readAll();
        styleSheetFile.close();

        QString style = byteArray;
        return style;
    }
}

void ServerLMSWidget::startInitialization_step0()
{
    QApplication::setOverrideCursor(Qt::WaitCursor);

    providerDBLMS = new ProviderDBLMS(this);
    connect(providerDBLMS, &ProviderDBLMS::signal_ErrorPostgreSQL, this, &ServerLMSWidget::slot_ErrorPostgreSQL);
    connect(providerDBLMS, &ProviderDBLMS::signal_BlockAutorization, this, &ServerLMSWidget::slot_BlockAutorization);

    mutex = new QMutex;

    updateThread = new QThread;
    loggerThread = new QThread;

    chatSystem = new ChatSystem();

    assetsManager = new AssetsManager;
    assetsManager->moveToThread(updateThread);

    updateController = new UpdateController;
    updateController->moveToThread(updateThread);

    docsUpdater = new DocsUpdater(updateController/*, this*/);
    docsUpdater->moveToThread(updateThread);

    processingSystem = new ProcessingSystem(providerDBLMS, updateController, docsUpdater);

    dataParser = new DataParser(assetsManager,processingSystem);

    commonClientHandler = new CommonClientHandler;
    connect(this,&ServerLMSWidget::signal_DocsChanged,commonClientHandler, &CommonClientHandler::slot_DocsChanged);
    connect(commonClientHandler,&CommonClientHandler::sigSetServerState,this,&ServerLMSWidget::slot_trySetServerState);

    server = new MultiThreadServer(this,updateController,processingSystem,dataParser,6000);

    loggerThread->start();
    updateThread->start();

    commonClientHandler->initialize(server->getClientsMap(),processingSystem,dataParser);
    processingSystem->initialize(server,dataParser,commonClientHandler,updateController,chatSystem);
    chatSystem->initialize(commonClientHandler,dataParser,server->getClientsMap());

    Logger::instance().moveToThread(loggerThread);
    Logger::instance().setLoggingType(LoggingType::WIDGET);
    Logger::instance().setLogToFile(true);

    connect(this,&ServerLMSWidget::sigUpdateControllerInitialize,updateController,&UpdateController::initialize/*,Qt::DirectConnection*/);
    connect(updateController,&UpdateController::sigInitializeFinished, this,&ServerLMSWidget::slot_startInitialization_step1/*,Qt::DirectConnection*/);
    connect(this,&ServerLMSWidget::sigCalculateFullHash,updateController,&UpdateController::calculateFullHash,Qt::AutoConnection);
    connect(updateController,&UpdateController::sigErrorRequired,this,&ServerLMSWidget::setError);
    connect(updateController,&UpdateController::sigUpdateDocs,this,&ServerLMSWidget::slot_UpdateDocs,Qt::AutoConnection);
    connect(&Logger::instance(),&Logger::sigLogToWidget,this, &ServerLMSWidget::slot_AddMessageToLog,Qt::QueuedConnection);

    connect(assetsManager,&AssetsManager::signal_setVersion, this, &ServerLMSWidget::slot_setVersion);

    emit sigUpdateControllerInitialize(commonClientHandler,dataParser,assetsManager);
}

void ServerLMSWidget::tryConnectionToDB()
{
    if(! providerDBLMS->ConnectionToDB())
    {
        emit signal_Tray_ShowMessage(tr("Database connection error!"), QSystemTrayIcon::Critical);

        emit signal_Menu_ShowWindow();

        QMessageBox::critical(this, tr("Error!"), tr("Database connection error!"));

        on_btnSettings_clicked();
    }
    else
    {
        providerDBLMS->deAuthorizationAll();

        //Настройки БД
        DataBaseSettings dbSettings = providerDBLMS->getDBSettings();
        QString strDBsettings = QString("%1 (%2) %3 : %4").arg(dbSettings.dbName,
                                                               dbSettings.dbType,
                                                               dbSettings.dbHostName,
                                                               QString::number(dbSettings.dbPort));

        Logger::instance().log("Connection to DB completed!");

        emit signal_Tray_ShowMessage(tr("Database connection OK!") + "\n" + strDBsettings);

        on_btnStartServer_clicked();
    }

    updateStateServer();
}

void ServerLMSWidget::updateStateServer()
{
    if(server)
    {
        if(server->getStateServer() == EStateServer::started)
        {
            ui->lblOnOffText->setText(tr("started"));
            ui->lblOnOff->setPixmap(QPixmap(QStringLiteral(":/resources/icons/circleGreen.png")));
        }
        else
        {
            ui->lblOnOffText->setText(tr("stoped"));
            ui->lblOnOff->setPixmap(QPixmap(QStringLiteral(":/resources/icons/circleGray.png")));
        }

        if(server->getStateBlockAutorization() == EStateBlockAutorization::unblocked)
        {
            ui->lblBlockAuth->setPixmap(QPixmap(QStringLiteral(":/resources/icons/open.png")));
        }
        else
        {
            ui->lblBlockAuth->setPixmap(QPixmap(QStringLiteral(":/resources/icons/lock.png")));
        }
    }

    if(providerDBLMS)
    {
        if(providerDBLMS->DBisConnected())
        {
            //Настройки БД
            DataBaseSettings dbSettings = providerDBLMS->getDBSettings();
            QString strDBsettings = tr("connected") + QString("   [%1 (%2) %3 : %4]").arg(dbSettings.dbName,
                                                                   dbSettings.dbType,
                                                                   dbSettings.dbHostName,
                                                                   QString::number(dbSettings.dbPort));
            ui->lblDBsettings->setText(strDBsettings);
            ui->lblDBisConnected->setPixmap(QPixmap(QStringLiteral(":/resources/icons/circleGreen.png")));
        }
        else
        {
            ui->lblDBsettings->setText(tr("not connected"));
            ui->lblDBisConnected->setPixmap(QPixmap(QStringLiteral(":/resources/icons/circleGray.png")));

            ui->btnStopServer->setEnabled(false);
            ui->btnStartServer->setEnabled(false);
        }
    }
}
