#include "CratePluginEU.h"
#include "LtrCrateInfo.h"
#include "LtrdConnection.h"
#include <QMessageBox>
#include <QSettings>
#include <QFileDialog>
#include <QProgressDialog>
#include <QThread>
#include "CrateFirmeareUpdateEU.h"
#include "Crates/ConfigDialog/CrateConfigDialog.h"
#include "ltr/include/ltr030api.h"

static INT f_crate_open(LtrManagerEnv *env, TLTR030 *hcrate) {
    LtrdConnection *con = env->currentLtrdCon();
    LtrCrateInfo   *crate = env->currentCrate();
    INT err = LTR_OK;
    Q_ASSERT(con && crate);

    LTR030_Init(hcrate);
    err = LTR030_Open(hcrate, con->addr().toIPv4Address(), con->port(),
                      crate->crateInterface(), crate->serial().toLatin1());

    if (err != LTR_OK) {
        QMessageBox::critical(env->mainWidget(), CratePluginEU::tr("Error"),
                              CratePluginEU::tr("Cannot open crate connection") + ": " +
                              QSTRING_FROM_CSTR(LTR030_GetErrorString(err)));
    }
    return err;
}

CratePluginEU::CratePluginEU(LtrManagerEnv *env) :  ActionPlugin(env),
    m_progressDialog(0), m_fwUpdater(0), m_fwThread(0) {    
    env->registerActionPlugin(LtrManagerEnv::ActionTypeCrateConfig, this);    
    env->registerActionPlugin(LtrManagerEnv::ActionTypeCrateBoot, this);
    env->registerActionPlugin(LtrManagerEnv::ActionTypeCrateReset, this);
}

CratePluginEU::~CratePluginEU() {
    if (m_fwThread) {
        m_fwThread->quit();
        m_fwThread->wait();
    }
    delete m_fwUpdater;
    delete m_fwThread;
    delete m_progressDialog;
}

bool CratePluginEU::actionEnabled(LtrManagerEnv *env, LtrManagerEnv::ActionType type) {
    bool en = false;
    LtrCrateInfo *crate = env->currentCrate();

    if (crate) {
        if (((crate->type()==LTR_CRATE_TYPE_LTR030) || (crate->type()==LTR_CRATE_TYPE_LTR031))
                        && (crate->crateInterface()==LTR_CRATE_IFACE_USB)) {
            en = true;
        }
    }
    return en;
}

void CratePluginEU::actionActivate(LtrManagerEnv *env, LtrManagerEnv::ActionType type) {
    if (type == LtrManagerEnv::ActionTypeCrateBoot) {
        updateFirmware(env);
    } else if (type == LtrManagerEnv::ActionTypeCrateConfig) {
        config(env);
    } else if (type == LtrManagerEnv::ActionTypeCrateReset) {
        reset(env);
    }
}

void CratePluginEU::config(LtrManagerEnv *env) {
    TLTR030 hcrate;
    INT err = f_crate_open(env, &hcrate);
    if (err == LTR_OK) {
        TLTR030_CONFIG ltr030_cfg;
        BYTE factory_mac[CrateConfigDialog::Config::MAC_ADDR_LEN];

        ltr030_cfg.Size = sizeof(ltr030_cfg);
        err = LTR030_GetConfig(&hcrate, &ltr030_cfg);
        if (err == LTR_OK)
            err = LTR030_GetFactoryMac(&hcrate, factory_mac);
        if (err != LTR_OK) {
            QMessageBox::critical(env->mainWidget(), tr("Error"),
                                  tr("Cannot get crate configuration: ") +
                                  LtrdConnection::getErrorString(err));
        } else {
            CrateConfigDialog::Config cfg;
            int flags = CrateConfigDialog::FlagShowFactoryMac;

            cfg.ip_addr = ltr030_cfg.IpAddr;
            cfg.ip_netmask = ltr030_cfg.IpMask;
            cfg.ip_gateway = ltr030_cfg.Gateway;
            cfg.iface = ltr030_cfg.Interface;
            memcpy(cfg.factory_mac, factory_mac, CrateConfigDialog::Config::MAC_ADDR_LEN);
            /* по размеру возвращенной структуры определяем, действителен ли пользовательский
               MAC-адрес */
            if (ltr030_cfg.Size >= (offsetof(TLTR030_CONFIG, UserMac) + sizeof(ltr030_cfg.UserMac))) {
                BYTE disabled_mac[CrateConfigDialog::Config::MAC_ADDR_LEN] = {0,0,0,0,0,0};
                flags |=  CrateConfigDialog::FlagShowUserMac;

                memcpy(cfg.user_mac, ltr030_cfg.UserMac, CrateConfigDialog::Config::MAC_ADDR_LEN);
                if (memcmp(cfg.user_mac, disabled_mac, CrateConfigDialog::Config::MAC_ADDR_LEN))
                    cfg.user_mac_en = 1;

            }

            CrateConfigDialog dlg(cfg, env->mainWidget(), flags);
            if (dlg.exec()==QDialog::Accepted) {
                ltr030_cfg.Interface = dlg.crateInterface();
                ltr030_cfg.IpAddr = dlg.ipAddr();
                ltr030_cfg.IpMask = dlg.ipMask();
                ltr030_cfg.Gateway = dlg.ipGate();
                if (flags & CrateConfigDialog::FlagShowUserMac) {
                    if (dlg.setUserMac()) {
                        memcpy(ltr030_cfg.UserMac, dlg.userMac().data(), CrateConfigDialog::Config::MAC_ADDR_LEN);
                    } else {
                        memset(ltr030_cfg.UserMac, 0, CrateConfigDialog::Config::MAC_ADDR_LEN);
                    }
                }

                err = LTR030_SetConfig(&hcrate, &ltr030_cfg);
                if (err==LTR_OK) {
                    QMessageBox::information(env->mainWidget(), tr("Operation completed"),
                                          tr("Crate settings were successfully written.") + "\n" +
                                          tr("You must turn off and on crate for new settings to take effect!"));
                } else {
                    QMessageBox::critical(env->mainWidget(), tr("Cannot write crate settings!"),
                                           LtrdConnection::getErrorString(err));
                }
            }
        }
        LTR030_Close(&hcrate);
    }
}


void CratePluginEU::updateFirmware(LtrManagerEnv *env) {
    QSettings set;
    QString dir;
    set.beginGroup("LTR-EU");
    /* используем сохранненную начальную директорию для открытия файла */
    dir = set.value("firmwareDir").toString();

    QString filename = QFileDialog::getOpenFileName(env->mainWidget(), tr("Choice of firmware file"),
                                                    dir, tr("Firmware files") + "(*.ldr);;" + tr("All files") +  "(*.*)");
    if (!filename.isEmpty()) {
        /* если файл выбрали, то сохраняем его директорию, чтобы в следующий раз
           открыть диалог на ней */
        set.setValue("firmwareDir", QFileInfo(filename).absolutePath());


        TLTR030 hcrate;
        INT err = f_crate_open(env, &hcrate);
        if (err == LTR_OK) {
            m_env = env;
            if (m_fwUpdater)
                delete m_fwUpdater;
            if (m_fwThread)
                delete m_fwThread;
            if (m_progressDialog)
                delete m_progressDialog;
            m_fwUpdater = new CrateFirmeareUpdateEU(hcrate);
            m_fwThread = new QThread();
            m_fwThread->start();
            m_fwUpdater->moveToThread(m_fwThread);
            connect(this, SIGNAL(startDspUpdate(QString)), m_fwUpdater, SLOT(startDspUpdate(QString)));
            connect(m_fwUpdater, SIGNAL(opStart(unsigned)), SLOT(opStarted(unsigned)));
            connect(m_fwUpdater, SIGNAL(opProgress(QString, unsigned)), SLOT(opProgress(QString, unsigned)));
            connect(m_fwUpdater, SIGNAL(opFinish(int)), SLOT(opFinished(int)));
            m_progressDialog = new QProgressDialog(env->mainWidget(), Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint);
            m_progressDialog->setModal(true);
            m_progressDialog->setMinimumWidth(300);
            m_progressDialog->setAutoClose(false);
            m_progressDialog->setMinimumDuration(300);

            m_progressDialog->setWindowTitle(tr("Firmware Update"));
            m_progressDialog->setValue(0);
            m_progressDialog->setCancelButton(0);

            emit startDspUpdate(filename);
        }
    }
}

void CratePluginEU::reset(LtrManagerEnv *env) {
    TLTR030 hcrate;
    INT err = f_crate_open(env, &hcrate);
    if (err == LTR_OK) {
        err = LTR030_CrateReset(&hcrate);
        if (err != LTR_OK) {
            QMessageBox::critical(env->mainWidget(), tr("Error"), tr("Cannot reset crate") + ": " +
                                  QSTRING_FROM_CSTR(LTR030_GetErrorString(err)));
        }
        LTR030_Close(&hcrate);
    }
}

void CratePluginEU::opStarted(unsigned full_size) {
    if (m_progressDialog) {
        m_progressDialog->setMaximum(full_size);
    }
}

void CratePluginEU::opProgress(QString stage, unsigned done) {
    if (m_progressDialog) {
        m_progressDialog->setLabelText(stage);
        m_progressDialog->setValue(done);
    }
}

void CratePluginEU::opFinished(int err) {
    if (m_progressDialog) {
        m_progressDialog->close();
        m_fwThread->quit();
        m_fwThread->wait();

        if (err == LTR_OK) {
            QMessageBox::information(m_env->mainWidget(), tr("Operation completed"),
                                  tr("Crate firmware was successfully written.") + "\n" +
                                  tr("You must turn off and on crate for run new firmware!"));
        } else {
            QMessageBox::critical(m_env->mainWidget(), tr("Cannot write crate firmware!"),
                                  QSTRING_FROM_CSTR(LTR030_GetErrorString(err)));
        }
    }
}
