/***************************************************************************************************
 * Copyright 2023 Qsaker(qsaker@foxmail.com). All rights reserved.
 *
 * The file is encoded using "utf8 with bom", it is a part of QtSwissArmyKnife project.
 *
 * QtSwissArmyKnife is licensed according to the terms in the file LICENCE in the root of the source
 * code directory.
 **************************************************************************************************/
#include "saktoolbox.h"

#include "saktoolfactory.h"

SAKToolBox::SAKToolBox(QObject* parent)
    : QObject{parent}
{
    auto createTool = [](int type) -> SAKBaseTool* {
        auto toolFactory = SAKToolFactory::instance();
        return toolFactory->createTool(type);
    };

    SAKBaseTool* tool = createTool(SAKToolFactory::MaskerTool);
    mTxMaskerTool = qobject_cast<SAKMaskerTool*>(tool);
    tool = createTool(SAKToolFactory::MaskerTool);
    mRxMaskerTool = qobject_cast<SAKMaskerTool*>(tool);
    tool = createTool(SAKToolFactory::AnalyzerTool);
    mTxAnalyzerTool = qobject_cast<SAKAnalyzerTool*>(tool);
    tool = createTool(SAKToolFactory::AnalyzerTool);
    mRxAnalyzerTool = qobject_cast<SAKAnalyzerTool*>(tool);
    tool = createTool(SAKToolFactory::EmitterTool);
    mEmitterTool = qobject_cast<SAKEmitterTool*>(tool);
    tool = createTool(SAKToolFactory::ResponserTool);
    mResponserTool = qobject_cast<SAKResponserTool*>(tool);
    tool = createTool(SAKToolFactory::StorerTool);
    mStorerTool = qobject_cast<SAKStorerTool*>(tool);
    tool = createTool(SAKToolFactory::PrestoreTool);
    mPrestorerTool = qobject_cast<SAKPrestorerTool*>(tool);
    tool = createTool(SAKToolFactory::VelometerTool);
    mRxVelometerTool = qobject_cast<SAKVelometerTool*>(tool);
    tool = createTool(SAKToolFactory::VelometerTool);
    mTxVelometerTool = qobject_cast<SAKVelometerTool*>(tool);

    tool = createTool(SAKToolFactory::StatistiticianTool);
    mRxStatisticianTool = qobject_cast<SAKStatisticianTool*>(tool);
    tool = createTool(SAKToolFactory::StatistiticianTool);
    mTxStatisticianTool = qobject_cast<SAKStatisticianTool*>(tool);
    tool = createTool(SAKToolFactory::UdpTransmitterTool);
    mUdpTransmitterTool = qobject_cast<SAKUdpTransmitterTool*>(tool);
    tool = createTool(SAKToolFactory::TcpTransmitterTool);
    mTcpTransmitterTool = qobject_cast<SAKTcpTransmitterTool*>(tool);
    tool = createTool(SAKToolFactory::WebSocketTransmitterTool);
    mWebSocketTransmitterTool = qobject_cast<SAKWebSocketTransmitterTool*>(tool);
    tool = createTool(SAKToolFactory::SerialPortTransmitterTool);
    mSerialPortTransmitterTool = qobject_cast<SAKSerialPortTransmitterTool*>(tool);

    mToolList << mTxMaskerTool << mRxMaskerTool << mTxAnalyzerTool << mRxAnalyzerTool
              << mEmitterTool << mResponserTool << mStorerTool << mPrestorerTool << mRxVelometerTool
              << mTxVelometerTool << mRxStatisticianTool << mTxStatisticianTool
              << mUdpTransmitterTool << mTcpTransmitterTool << mWebSocketTransmitterTool
              << mSerialPortTransmitterTool;

    int flag = Qt::AutoConnection | Qt::UniqueConnection;
    for (auto tool : mToolList) {
        connect(tool,
                &SAKBaseTool::errorOccured,
                this,
                &SAKToolBox::errorOccurred,
                Qt::ConnectionType(flag));
    }

    connect(this, &SAKToolBox::errorOccurred, this, [=]() { this->close(); });
}

SAKToolBox::~SAKToolBox()
{
    if (mComunicationTool) {
        mComunicationTool->exit();
        mComunicationTool->wait();
        mComunicationTool->deleteLater();
    }

    for (auto tool : mToolList) {
        tool->exit();
        tool->wait();
        tool->deleteLater();
    }
}

void SAKToolBox::initialize(int type)
{
    if (mComunicationTool) {
        mComunicationTool->exit();
        mComunicationTool->wait();
        mComunicationTool->deleteLater();
        mComunicationTool = nullptr;
    }

    auto toolFactory = SAKToolFactory::instance();
    auto tool = toolFactory->createTool(type);
    mComunicationTool = qobject_cast<SAKCommunicationTool*>(tool);
    if (!mComunicationTool) {
        qCWarning(mLoggingCategory) << "mComunicationTool is nullptr, type:" << type;
        return;
    }

    // rx->output_masker->output_analyzer->emmitter,responser
    connect(mComunicationTool,
            &SAKBaseTool::bytesOutputted,
            mRxMaskerTool,
            &SAKBaseTool::inputBytes);
    connect(mRxMaskerTool, &SAKBaseTool::bytesOutputted, mRxAnalyzerTool, &SAKBaseTool::inputBytes);
    connect(mRxAnalyzerTool, &SAKBaseTool::bytesOutputted, mEmitterTool, &SAKBaseTool::inputBytes);
    connect(mRxAnalyzerTool, &SAKBaseTool::bytesOutputted, mResponserTool, &SAKBaseTool::inputBytes);

    // emiiter,responser,prestorer->input_analyzer->input_masker->tx
    connect(mEmitterTool, &SAKBaseTool::bytesOutputted, mTxAnalyzerTool, &SAKBaseTool::inputBytes);
    connect(mResponserTool, &SAKBaseTool::bytesOutputted, mTxAnalyzerTool, &SAKBaseTool::inputBytes);
    connect(mPrestorerTool, &SAKBaseTool::bytesOutputted, mTxAnalyzerTool, &SAKBaseTool::inputBytes);
    connect(mTxAnalyzerTool, &SAKBaseTool::bytesOutputted, mTxMaskerTool, &SAKBaseTool::inputBytes);
    connect(mTxMaskerTool,
            &SAKBaseTool::bytesOutputted,
            mComunicationTool,
            &SAKBaseTool::inputBytes);

    // rx->storer; tx->storer
    connect(mRxAnalyzerTool, &SAKBaseTool::bytesOutputted, mStorerTool, &SAKBaseTool::inputBytes);
    connect(mTxMaskerTool, &SAKBaseTool::bytesInputted, mStorerTool, &SAKBaseTool::inputBytes);

    // rx->velometer; tx->velometer
    connect(mComunicationTool,
            &SAKBaseTool::bytesOutputted,
            mRxVelometerTool,
            &SAKBaseTool::inputBytes);
    connect(mComunicationTool,
            &SAKBaseTool::bytesInputted,
            mTxVelometerTool,
            &SAKBaseTool::inputBytes);

    // rx->statistician; tx->statistician
    connect(mRxAnalyzerTool,
            &SAKBaseTool::bytesOutputted,
            mRxStatisticianTool,
            &SAKBaseTool::inputBytes);
    connect(mComunicationTool,
            &SAKBaseTool::bytesInputted,
            mTxStatisticianTool,
            &SAKBaseTool::inputBytes);

    // rx->serialport transmition; serialport transmition->Tx analyzer
    connect(mRxAnalyzerTool,
            &SAKBaseTool::bytesOutputted,
            mSerialPortTransmitterTool,
            &SAKBaseTool::inputBytes);
    connect(mSerialPortTransmitterTool,
            &SAKBaseTool::bytesOutputted,
            mTxAnalyzerTool,
            &SAKBaseTool::inputBytes);

    // rx->udp transmition; udp transmition->Tx analyzer
    connect(mRxAnalyzerTool,
            &SAKBaseTool::bytesOutputted,
            mUdpTransmitterTool,
            &SAKBaseTool::inputBytes);
    connect(mUdpTransmitterTool,
            &SAKBaseTool::bytesOutputted,
            mTxAnalyzerTool,
            &SAKBaseTool::inputBytes);

    connect(mRxAnalyzerTool,
            &SAKBaseTool::bytesOutputted,
            mTcpTransmitterTool,
            &SAKBaseTool::inputBytes);
    connect(mTcpTransmitterTool,
            &SAKBaseTool::bytesOutputted,
            mTxAnalyzerTool,
            &SAKBaseTool::inputBytes);

    connect(mRxAnalyzerTool,
            &SAKBaseTool::bytesOutputted,
            mWebSocketTransmitterTool,
            &SAKBaseTool::inputBytes);
    connect(mWebSocketTransmitterTool,
            &SAKBaseTool::bytesOutputted,
            mTxAnalyzerTool,
            &SAKBaseTool::inputBytes);

    connect(mComunicationTool,
            &SAKCommunicationTool::errorOccured,
            this,
            &SAKToolBox::errorOccurred);

    emit communicatonChanged();
}

void SAKToolBox::open()
{
    if (mComunicationTool) {
        for (auto tool : mToolList) {
            tool->start();
        }

        mIsWorking = true;
        emit isWorkingChanged();
        mComunicationTool->start();
    } else {
        uninitializedTips();
    }
}

void SAKToolBox::close()
{
    if (mComunicationTool) {
        mComunicationTool->exit();
        mComunicationTool->wait();

        for (auto tool : mToolList) {
            tool->exit();
            tool->wait();
        }

        mIsWorking = false;
        emit isWorkingChanged();
    } else {
        uninitializedTips();
    }
}

void SAKToolBox::send(const QByteArray& bytes, const QVariant& context)
{
    mTxAnalyzerTool->inputBytes(bytes, context);
}

bool SAKToolBox::isWorking()
{
    return mIsWorking;
}

SAKCommunicationTool* SAKToolBox::getCommunicationTool()
{
    return mComunicationTool;
}

SAKMaskerTool* SAKToolBox::getTxMaskerTool()
{
    return mTxMaskerTool;
}

SAKMaskerTool* SAKToolBox::getRxMaskerTool()
{
    return mRxMaskerTool;
}

SAKAnalyzerTool* SAKToolBox::getTxAnalyzerTool()
{
    return mTxAnalyzerTool;
}

SAKAnalyzerTool* SAKToolBox::getRxAnalyzerTool()
{
    return mRxAnalyzerTool;
}

SAKEmitterTool* SAKToolBox::getEmitterTool()
{
    return mEmitterTool;
}

SAKResponserTool* SAKToolBox::getResponserTool()
{
    return mResponserTool;
}

SAKStorerTool* SAKToolBox::getStorerTool()
{
    return mStorerTool;
}

SAKPrestorerTool* SAKToolBox::getPrestorerTool()
{
    return mPrestorerTool;
}

SAKVelometerTool* SAKToolBox::getRxVelometerTool()
{
    return mRxVelometerTool;
}

SAKVelometerTool* SAKToolBox::getTxVelometerTool()
{
    return mTxVelometerTool;
}

SAKStatisticianTool* SAKToolBox::getRxStatisticianTool()
{
    return mRxStatisticianTool;
}

SAKStatisticianTool* SAKToolBox::getTxStatisticianTool()
{
    return mTxStatisticianTool;
}

SAKUdpTransmitterTool* SAKToolBox::getUdpTransmitterTool()
{
    return mUdpTransmitterTool;
}

SAKTcpTransmitterTool* SAKToolBox::getTcpTransmitterTool()
{
    return mTcpTransmitterTool;
}

SAKWebSocketTransmitterTool* SAKToolBox::getWebSocketTransmitterTool()
{
    return mWebSocketTransmitterTool;
}

SAKSerialPortTransmitterTool* SAKToolBox::getSerialPortTransmitterTool()
{
    return mSerialPortTransmitterTool;
}

void SAKToolBox::uninitializedTips()
{
    static QByteArray tips("You must call the interface name initialize()"
                           " before using the object.");
    Q_ASSERT_X(false, __FUNCTION__, tips.constData());

    emit errorOccurred(QString::fromLatin1(tips));
}

QVariant SAKToolBox::communicaton()
{
    return QVariant::fromValue(mComunicationTool);
}

QVariant SAKToolBox::txMasker()
{
    return QVariant::fromValue(mTxMaskerTool);
}

QVariant SAKToolBox::rxMasker()
{
    return QVariant::fromValue(mRxMaskerTool);
}

QVariant SAKToolBox::txAnalyzer()
{
    return QVariant::fromValue(mTxAnalyzerTool);
}

QVariant SAKToolBox::rxAnalyzer()
{
    return QVariant::fromValue(mRxAnalyzerTool);
}

QVariant SAKToolBox::emitter()
{
    return QVariant::fromValue(mEmitterTool);
}

QVariant SAKToolBox::responser()
{
    return QVariant::fromValue(mResponserTool);
}

QVariant SAKToolBox::storer()
{
    return QVariant::fromValue(mStorerTool);
}

QVariant SAKToolBox::prestorer()
{
    return QVariant::fromValue(mPrestorerTool);
}

QVariant SAKToolBox::rxVelometer()
{
    return QVariant::fromValue(mRxVelometerTool);
}

QVariant SAKToolBox::txVelometer()
{
    return QVariant::fromValue(mTxVelometerTool);
}

QVariant SAKToolBox::rxStatistician()
{
    return QVariant::fromValue(mRxStatisticianTool);
}

QVariant SAKToolBox::txStatistician()
{
    return QVariant::fromValue(mTxStatisticianTool);
}

QVariant SAKToolBox::udpTransmitter()
{
    return QVariant::fromValue(mUdpTransmitterTool);
}

QVariant SAKToolBox::tcpTransmitter()
{
    return QVariant::fromValue(mTcpTransmitterTool);
}

QVariant SAKToolBox::webSocketTransmitter()
{
    return QVariant::fromValue(mWebSocketTransmitterTool);
}

QVariant SAKToolBox::serialPortTransmitter()
{
    return QVariant::fromValue(mSerialPortTransmitterTool);
}
