#include "src/ATBAPP/ATBDeviceControllerPlugin.h"
#include "src/ATBAPP/ATBHealthEvent.h"

#include <QTimer>
#include <QTextCodec>


ATBDeviceControllerPlugin::ATBDeviceControllerPlugin(QObject *parent) : QObject(parent),
    pluginState(PLUGIN_STATE::NOT_INITIALIZED)
{
    this->pluginInfo = QString::fromUtf8(pluginInfoString.c_str());

    this->hw = new hwapi();

    connect(dynamic_cast<QObject*>(hw), SIGNAL(hwapi_templatePrintFinished_OK()),  this, SLOT(onPrintFinishedOK()));
    connect(dynamic_cast<QObject*>(hw), SIGNAL(hwapi_templatePrintFinished_Err()), this, SLOT(onPrintFinishedERR()));
    connect(dynamic_cast<QObject*>(hw), SIGNAL(hwapi_gotNewCoin()),    this, SLOT(onCashGotCoin()));
    connect(dynamic_cast<QObject*>(hw), SIGNAL(hwapi_vendStopByMax()), this, SLOT(onCashVendStopByMax()));
}

ATBDeviceControllerPlugin::~ATBDeviceControllerPlugin() {}

PLUGIN_STATE ATBDeviceControllerPlugin::initDCPlugin(QObject *healthEventReceiver, const QSettings & settings)
{
    this->healthEventReceiver = healthEventReceiver;

    // read variables from setting
    QString serialPort = settings.value("DEVICE_CONTROLLER/serialPort", "ttymxc2").toString();
    QByteArray printerEncoding = settings.value("DEVICE_CONTROLLER/printerEnconding", "ISO 8859-2").toString().toLatin1();



    // open serial port
    hw->dc_openSerial(5, "115200", serialPort, 1);


    // text encoding for printer
    this->codec = QTextCodec::codecForName(printerEncoding);



    this->pluginState = PLUGIN_STATE::INITIALIZED;

    return pluginState;
}


// TASKS: Cash handling -------------------------------------------------------
void ATBDeviceControllerPlugin::requestStartCashInput(const QString & amount)
{
    qCritical() << "Start Cash vending with amount = " << amount;

    uint32_t amountInt = static_cast<uint32_t>(amount.toUInt());
    hw->cash_startPayment(amountInt);
}

void ATBDeviceControllerPlugin::requestStopCashInput()
{
    hw->cash_stopPayment();
}

void ATBDeviceControllerPlugin::cashCollect()
{
    hw->vend_success();
}

void ATBDeviceControllerPlugin::cashAbort()
{
    hw->vend_failed();
}


// TASKS: printing ------------------------------------------------------------
void ATBDeviceControllerPlugin::requestPrintTicket(const QHash<QString, QVariant> & printingData)
{
    struct T_dynDat *dynTicketData = new T_dynDat;
    memset(dynTicketData, 0, sizeof(*dynTicketData));

    qCritical() << "ATBDeviceControllerPlugin::requestPrintTicket( "        << endl
                << "     licenseplate = " << printingData["licenseplate"]   << endl
                << "           amount = " << printingData["amount"]         << endl
                << "       parkingEnd = " << printingData["parkingEnd"]     << endl
                << "      currentTime = " << printingData["currentTime"]    << endl
                << "      currentDate = " << printingData["currentDate"]    << endl;


    // set dynamic printer data:
    memcpy((char*)dynTicketData->licensePlate, codec->fromUnicode(printingData["licenseplate"].toString()).data(), 8);
    memcpy((char*)dynTicketData->vendingPrice, codec->fromUnicode(printingData["amount"].toString()).data(), 8);

    QDateTime parkingEndQDateTime = QDateTime::fromString(printingData["parkingEnd"].toString(), Qt::ISODate);
    QDateTime currentDateTime     = QDateTime::fromString(printingData["currentTime"].toString(), Qt::ISODate);

    memcpy((char*)dynTicketData->parkingEnd,   codec->fromUnicode(parkingEndQDateTime.toString("hh:mm")).data(), 8);
    memcpy((char*)dynTicketData->currentTime,  codec->fromUnicode(currentDateTime.toString("hh:mm")).data(), 8);
    memcpy((char*)dynTicketData->currentDate,  codec->fromUnicode(currentDateTime.toString("dd.MM.yy")).data(), 8);

    // DEBUG
    qCritical() << "ATBDeviceControllerPlugin::requestPrintTicket()";

    if (!this->hw->dc_isPortOpen()) {
        qCritical() << "    ... serial port is not open!";
        this->onPrintFinishedERR();
        return;
    }


    // TODO: wird hier nur 'licensePlate' gedruckt?
    if (!this->hw->prn_sendDynamicPrnValues(dynTicketData->licensePlate)) {
        this->errorCode = "hwapi::prn_sendDynamicPrnValues";
        this->errorDescription = "hwapi method 'hwapi::prn_sendDynamicPrnValues' result is false";

        qCritical() << "ERROR:";
        qCritical() << "ATBDeviceControllerPlugin::requestPrintTicket( "        << endl
                    << "     licenseplate = " << printingData["licenseplate"]   << endl
                    << "           amount = " << printingData["amount"]         << endl
                    << "       parkingEnd = " << printingData["parkingEnd"]     << endl
                    << "      currentTime = " << printingData["currentTime"]    << endl
                    << "      currentDate = " << printingData["currentDate"]    << endl;


        this->onPrintFinishedERR();
        return;
    }

    QTimer::singleShot(1000, this, SLOT(onPrinterDataPrepared()));
}



void ATBDeviceControllerPlugin::onPrinterDataPrepared()
{
    this->currentTemplate = 1;
    this->onPrinterPrintNextTemplate();
}


void ATBDeviceControllerPlugin::onPrinterPrintNextTemplate()
{
    qCritical() << "     ... print template " << this->currentTemplate;

    if (!this->hw->prn_printTemplate(this->currentTemplate)) {
        this->errorCode = "hwapi::prn_printTemplate";
        this->errorDescription = QString("hwapi method 'hwapi::onPrinterPrintNextTemplate(%1)' result is false").arg(this->currentTemplate);
        this->onPrintFinishedERR();
        return;
    }

    if (this->currentTemplate >= 3) {
        // all templates are printed
        this->currentTemplate = 0;

        // FAKE SIGNAL:
        QTimer::singleShot(1000, this, SLOT(onPrintFinishedOK()));
    }
    else {
        // print next template
        this->currentTemplate++;
        QTimer::singleShot(3000, this, SLOT(onPrinterPrintNextTemplate()));
    }

}


/************************************************************************************************
 * private slots, interface to low level hwapi
 *
 */
void ATBDeviceControllerPlugin::onPrintFinishedOK()
{
    // DEBUG
    qCritical() << "ATBDeviceControllerPlugin::onPrintFinishedOK()";

    emit this->printTicketFinished(nsDeviceControllerInterface::RESULT_STATE::SUCCESS,
                                   "",
                                   "");
}
void ATBDeviceControllerPlugin::onPrintFinishedERR()
{
    // DEBUG
    qCritical() << "ATBDeviceControllerPlugin::onPrintFinishedERR()";


    this->errorCode = "PRINTER";               // TODO: get more detailed error code from low level API
    this->errorDescription = "Printer error";  // TODO: get more detailed error description from low level API

    emit this->printTicketFinished(nsDeviceControllerInterface::RESULT_STATE::ERROR_BACKEND,
                                   this->errorCode,
                                   this->errorDescription);
}



/************************************************************************************************
 * cash payment
 */
void ATBDeviceControllerPlugin::onCashGotCoin()
{
    // DEBUG
    qCritical() << "ATBDeviceControllerPlugin::onGotCoin()";

    uint32_t amountInt = this->hw->getInsertedAmount();

    QString amountString = QString::number(amountInt);


    emit this->cashInputEvent(nsDeviceControllerInterface::RESULT_STATE::SUCCESS,
                              nsDeviceControllerInterface::CASH_STATE::CACHE_INPUT,
                              amountString,
                              "",
                              "");
}


void ATBDeviceControllerPlugin::onCashVendStopByMax()
{
    // DEBUG
    qCritical() << "ATBDeviceControllerPlugin::onCashVendStopByMax()";

    uint32_t amountInt = this->hw->getInsertedAmount();

    QString amountString = QString::number(amountInt);

    emit this->cashInputFinished(nsDeviceControllerInterface::RESULT_STATE::SUCCESS,
                              amountString,
                              "",
                              "");

}

/************************************************************************************************
 * Mandatory plugin methods
 *
 */

PLUGIN_STATE ATBDeviceControllerPlugin::getState()
{
    return this->pluginState;
}

QString & ATBDeviceControllerPlugin::getLastError()
{
    return this->errorCode;
}

const QString & ATBDeviceControllerPlugin::getLastErrorDescription()
{
    return this->errorDescription;
}

const QString & ATBDeviceControllerPlugin::getPluginInfo()
{
    return this->pluginInfo;
}


const QString ATBDeviceControllerPlugin::getString(nsDeviceControllerInterface::RESULT_STATE resultState)
{
    QString str;

    switch (resultState) {
    case nsDeviceControllerInterface::RESULT_STATE::SUCCESS:
        str = QString("RESULT_STATE::SUCCESS");
        break;
    case nsDeviceControllerInterface::RESULT_STATE::ERROR_BACKEND:
        str = QString("RESULT_STATE::ERROR_BACKEND");
        break;
    case nsDeviceControllerInterface::RESULT_STATE::ERROR_TIMEOUT:
        str = QString("RESULT_STATE::ERROR_TIMEOUT");
        break;
    case nsDeviceControllerInterface::RESULT_STATE::ERROR_PROCESS:
        str = QString("RESULT_STATE::ERROR_PROCESS");
        break;
    case nsDeviceControllerInterface::RESULT_STATE::ERROR_RETRY:
        str = QString("RESULT_STATE::ERROR_RETRY");
        break;
    case nsDeviceControllerInterface::RESULT_STATE::INFO:
        str = QString("RESULT_STATE::INFO");
        break;
    }
    return str;

}



/************************************************************************************************
 * ... end
 */

#if QT_VERSION < 0x050000
Q_EXPORT_PLUGIN2( ATBDeviceControllerPlugin, ATBDeviceControllerPlugin )
#endif
