#include "lineringer.h"
#include "ui_lineringer.h"

LineRinger::LineRinger(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::LineRinger)
{
    ui->setupUi(this);

    const auto listPorts = QSerialPortInfo::availablePorts();
    for (const auto& port: listPorts) {
        ui->comBox->addItem(QString(port.portName() + ": " + port.manufacturer()), QVariant(port.portName()));
    }
    {
        ui->parityControlBox->addItem("No", QVariant(QSerialPort::NoParity));
        ui->parityControlBox->addItem("Even", QVariant(QSerialPort::EvenParity));
        ui->parityControlBox->addItem("Odd", QVariant(QSerialPort::OddParity));
        ui->parityControlBox->addItem("Space", QVariant(QSerialPort::SpaceParity));
        ui->parityControlBox->addItem("Mark", QVariant(QSerialPort::MarkParity));
    }
    {
        ui->dataBox->addItem("Data5", QVariant(QSerialPort::Data5));
        ui->dataBox->addItem("Data6", QVariant(QSerialPort::Data6));
        ui->dataBox->addItem("Data7", QVariant(QSerialPort::Data7));
        ui->dataBox->addItem("Data8", QVariant(QSerialPort::Data8));
        ui->dataBox->setCurrentIndex(3);
    }
    {
        ui->stopBox->addItem("One", QVariant(QSerialPort::OneStop));
        ui->stopBox->addItem("OneAndHalf", QVariant(QSerialPort::OneAndHalfStop));
        ui->stopBox->addItem("Two", QVariant(QSerialPort::TwoStop));
    }
    ui->deviceOnlineView->horizontalHeader()->setVisible(true);
    syncColumnHeaders();
    ui->deviceOnlineView->setColumnHidden(1, true);

    ui->ringButton->setEnabled(false);

    modbusDevice = new QModbusRtuSerialMaster(this);
}

LineRinger::~LineRinger()
{
    if (modbusDevice->state() == QModbusDevice::ConnectedState)
        on_connectButton_clicked();
    delete ui;
}

void LineRinger::syncColumnHeaders()
{
    QStringList headers;
    headers << "ID" << "BaudRate" << "Vendor Name" << "Product Code" << "Major Minor Revision" << "Vendor Url" << "Product Name" << "Model Name" << "User Application Name" << "Примечание";
    ui->deviceOnlineView->setHorizontalHeaderLabels(headers);
    ui->deviceOnlineView->resizeColumnsToContents();
}

void LineRinger::on_connectButton_clicked()
{
    if(!modbusDevice)
        return;
    if(modbusDevice->state() != QModbusDevice::ConnectedState) {
        modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter,
                                             ui->comBox->currentData().toString());
#if QT_CONFIG(modbus_serialport)
        modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter,
                                             ui->parityControlBox->currentData().toInt());
        modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter,
                                             ui->baudRateBox->currentText().toInt(nullptr, 10));
        modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter,
                                             ui->dataBox->currentData().toInt());
        modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter,
                                             ui->stopBox->currentData().toInt());
#endif
        modbusDevice->setTimeout(50);
        modbusDevice->setNumberOfRetries(0);
        if(!modbusDevice->connectDevice()) {
            QMessageBox::warning(this, "Ошибка", "Произошла ошибка при попытке подключения.");
        } else {
            ui->connectButton->setText(tr("Отключить"));
            ui->ringButton->setEnabled(true);
            currentBaudRate = ui->baudRateBox->currentText().toUInt(nullptr, 10);
        }
    } else {
        modbusDevice->disconnectDevice();
        ui->connectButton->setText(tr("Подключить"));
        ui->ringButton->setEnabled(false);
    }
}

LineRinger::callStatus LineRinger::lineCall()
{
    QModbusRequest readDeviceBasicIdentification(QModbusRequest::EncapsulatedInterfaceTransport, QByteArray::fromHex("0E0100"));
    QModbusRequest readDeviceRegularIdentification(QModbusRequest::EncapsulatedInterfaceTransport, QByteArray::fromHex("0E0200"));
    bool isRun = false;
    bool *tmp_isRun = &isRun;
    uint tmp_adr = 1;
    auto bar = new QProgressDialog(this);
    connect(bar, &QProgressDialog::canceled, this, [tmp_isRun]() {
        *tmp_isRun = true;
    });
    connect(this, &LineRinger::stopLineCall, this, [tmp_isRun]() {
       *tmp_isRun = true;
    });
    bar->setLabelText(tr("Поиск устройств... Текущий адрес: %1").arg(tmp_adr));
    bar->setCancelButton(nullptr);
    bar->setRange(1, 247);
    bar->setMinimumDuration(100);
    for(tmp_adr = 1; tmp_adr<248; tmp_adr++) {
        bar->setValue(tmp_adr);
        bar->setLabelText(tr("Поиск устройств... Текущий адрес: %1/247").arg(tmp_adr));
        auto *reply = modbusDevice->sendRawRequest(readDeviceBasicIdentification, tmp_adr);
        //Запрос типа устройства.
        if(reply == nullptr) {
            QMessageBox::warning(this, "Ошибка при сканировании.", QString("%1").arg(modbusDevice->errorString()));
            bar->close();
            bar->deleteLater();
            return callStatus::ERROR;
        }
        while(!reply->isFinished()) {
            if(isRun) {
                bar->close();
                bar->deleteLater();
                return callStatus::INTERRUPT;
            }
            QCoreApplication::processEvents();
        }
        if(isRun) {
            bar->close();
            bar->deleteLater();
            return callStatus::INTERRUPT;
        } else if(!isRun) {
            //Нужна проверка типа устройства
            if(reply->error()!=QModbusDevice::TimeoutError) {
                deviceOnLine currentDevice;
                currentDevice.adr = tmp_adr;
                currentDevice.baudRate = currentBaudRate;
                bool regularReplyError = false;
                QString regularReplyErrorString;
                if(reply->error()==QModbusDevice::NoError) {
                    QModbusResponse resp = reply->rawResult();
                    uint8_t numOfObject = resp.data().at(5);
                    QByteArray result = resp.data().remove(0, 6);
                    for(int tmp_obj = 0; tmp_obj < numOfObject; tmp_obj++) {
                        uint8_t objectID = result.at(0);
                        uint8_t lengthOfObject = result.at(1);
                        if(lengthOfObject>0) {
                            currentDevice.fields[objectID].clear();
                        }
                        for(int i = 0; i < lengthOfObject; i++) {
                            currentDevice.fields[objectID] += QString(result.at(2+i));
                        }
                        result.remove(0, lengthOfObject+2);
                    }
                    auto *regularReply = modbusDevice->sendRawRequest(readDeviceRegularIdentification, tmp_adr);
                    if(regularReply == nullptr) {
                        QMessageBox::warning(this, "Ошибка при сканировании.", QString("%1: %2").arg(modbusDevice->error()).arg(modbusDevice->errorString()));
                        bar->close();
                        bar->deleteLater();
                        return callStatus::ERROR;
                    }
                    while(!regularReply->isFinished()) {
                        if(isRun) {
                            bar->close();
                            bar->deleteLater();
                            return callStatus::INTERRUPT;
                        }
                        QCoreApplication::processEvents();
                    }
                    if(isRun) {
                        bar->close();
                        bar->deleteLater();
                        return callStatus::INTERRUPT;
                    } else if(!isRun) {
                        if(regularReply->error()!=QModbusDevice::NoError) {
                            regularReplyError = true;
                            regularReplyErrorString = QString("%1: %2").arg(regularReply->error()).arg(regularReply->errorString());
                        }
                        QModbusResponse regularResp = regularReply->rawResult();
                        uint8_t numOfRegularObject = regularResp.data().at(5);
                        QByteArray regularResult = regularResp.data().remove(0, 6);
                        for(int tmp_obj = 0; tmp_obj < numOfRegularObject; tmp_obj++) {
                            uint8_t objectID = regularResult.at(0);
                            if(objectID > 0x06)
                                continue;
                            uint8_t lengthOfObject = regularResult.at(1);
                            if(lengthOfObject>0) {
                                currentDevice.fields[objectID].clear();
                            }
                            for (int i = 0; i < lengthOfObject; i++) {
                                currentDevice.fields[objectID] += QString(regularResult.at(2+i));
                            }
                            regularResult.remove(0, lengthOfObject+2);
                        }
                    }
                }
                if(!isRun) {
                    devicesList.append(currentDevice);
                    unsigned newRow = ui->deviceOnlineView->rowCount();
                    ui->deviceOnlineView->insertRow(newRow);
                    ui->deviceOnlineView->setItem(newRow, 0, new QTableWidgetItem(QString::number(currentDevice.adr)));
                    ui->deviceOnlineView->setItem(newRow, 1, new QTableWidgetItem(QString::number(currentDevice.baudRate)));
                    for (int i = 0; i < 7; i++) {
                        ui->deviceOnlineView->setItem(newRow, i+2, new QTableWidgetItem(currentDevice.fields[i]));
                    }
                    if(reply->error()!=QModbusDevice::NoError) {
                        ui->deviceOnlineView->setItem(newRow, 9, new QTableWidgetItem(QString("%1: %2").arg(reply->error()).arg(reply->errorString())));
                    } else if(regularReplyError) {
                           ui->deviceOnlineView->setItem(newRow, 9, new QTableWidgetItem(regularReplyErrorString));
                    }
                    ui->deviceOnlineView->resizeColumnsToContents();
                    syncColumnHeaders();
                }
            }
        }
    }
    return callStatus::NOERROR;
}

void LineRinger::on_ringButton_clicked()
{
    ui->deviceOnlineView->clear();
    devicesList.clear();
    syncColumnHeaders();
    while(ui->deviceOnlineView->rowCount()!=0)
        ui->deviceOnlineView->removeRow(ui->deviceOnlineView->rowCount()-1);
    if(isAutoBaud) {
        auto bar = new QProgressDialog(this);
        bar->setLabelText(tr("Поиск устройств... Текущая скорость: %1").arg(currentBaudRate));
        bar->setRange(0, ui->baudRateBox->count());
        bar->setAutoClose(true);
        bar->setMinimumDuration(0);
        bar->setCancelButton(nullptr);
        connect(bar, &QProgressDialog::canceled, this, [this]() {
           emit stopLineCall();
        });
        bar->setValue(0);
        ui->deviceOnlineView->setColumnHidden(1, false);
        modbusDevice->disconnectDevice();
        for (int i = 0; i < ui->baudRateBox->count(); i++) {
            bar->setValue(i+1);
            modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter,
                                                 ui->baudRateBox->itemText(i).toInt(nullptr, 10));
            if (!modbusDevice->connectDevice()) {
                QMessageBox::warning(this, "Ошибка", "Произошла ошибка при попытке подключения.");
                on_connectButton_clicked();
                break;
            }
            currentBaudRate = ui->baudRateBox->itemText(i).toUInt(nullptr, 10);
            bar->setLabelText(tr("Поиск устройств... Текущая скорость: %1").arg(currentBaudRate));
            if(lineCall() == callStatus::INTERRUPT) {
                QMessageBox::warning(this, "Уведомление", QString("Досрочное завершение опроса. Найдено %1 устройств.").arg(devicesList.count()));
                modbusDevice->disconnectDevice();
                on_connectButton_clicked();
                bar->close();
                bar->deleteLater();
                ui->timer->setTime(QTime::currentTime());
                ui->timer->setDate(QDate::currentDate());
                return;
            }
            modbusDevice->disconnectDevice();
        }
        on_connectButton_clicked();
    } else {
        ui->deviceOnlineView->setColumnHidden(1, true);
        if(lineCall() == callStatus::INTERRUPT) {
            QMessageBox::warning(this, "Уведомление", QString("Досрочное завершение опроса. Найдено %1 устройств.").arg(devicesList.count()));
        }
    }
    ui->timer->setTime(QTime::currentTime());
    ui->timer->setDate(QDate::currentDate());
}

void LineRinger::on_checkAutoBaud_stateChanged(int arg1)
{
    isAutoBaud = (bool)arg1;
}
