#include "i2c_keyboard_detection.h"
#include "ui_i2c_keyboard_detection.h"

#include <qfile.h>
#include <qevent.h>

#include <qmessagebox.h>
#include <qfiledialog.h>

#include <qdatetime.h>

i2c_keyboard_detection::i2c_keyboard_detection(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::i2c_keyboard_detection)
{
    ui->setupUi(this);
    QObject::connect(&m_com, SIGNAL(readyRead()), this, SLOT(comDataReceived()));
    QFile fileKbdCodes("hid_codes.txt"/*"hid_keyboard_codes.h"*/);
    if (!fileKbdCodes.open(QFile::ReadOnly))
        return;
    m_keyComments.clear();
    ui->keyList->clear();
    while(1)    {
        QString currentStr = QString(fileKbdCodes.readLine());
        if (currentStr.length() == 0)
            break;
#if 1        
        currentStr = currentStr.left(currentStr.indexOf("//"));
        QStringList currentStrDiv = currentStr.split(QRegExp("\\s"));
        for (int i = (currentStrDiv.length() - 1); i >= 0; i--)
        {
            if(currentStrDiv[i] == "")
                currentStrDiv.removeAt(i);
        }
        if (currentStrDiv.length() != 2)
            continue;
        bool isKbdCode;
        int kbdCode = currentStrDiv[1].toInt(&isKbdCode, 16);
        if (!isKbdCode)
        {
            QMessageBox::warning(this, "UNCORRECT HID CODES FILE", "Code " + currentStrDiv[1] + " is not hex");
            break;
        }
        if (m_kbdCodes.contains(kbdCode))
        {
            QMessageBox::warning(this, "UNCORRECT HID CODES FILE", "One HID code used two times, code " + QString::number(kbdCode, 16));
            break;
        }
        m_kbdCodes.push_back(kbdCode);
        QString addedElement = currentStrDiv[0] + " (" + QString::number(kbdCode,16) + ")";
        ui->keyList->addItem(addedElement);
#else
        QString strComment = currentStr.right(currentStr.length() - currentStr.indexOf("//"));
        currentStr = currentStr.left(currentStr.indexOf("//"));
        QStringList strWords = currentStr.split(QRegExp("\\s"));
        for (int i = (strWords.length() - 1); i >= 0; i--)
        {
            if (strWords[i].length() == 0)
                strWords.removeAt(i);
        }
        if (strWords.length() != 3)
            continue;
        if (strWords[0] != "#define")
            continue;
        if (!strWords[2].startsWith("0x"))
            continue;
        strWords[2] = strWords[2].right(2);
        bool isKbdCode;
        int kbdCode = strWords[2].toInt(&isKbdCode, 16);
        if (!isKbdCode)
            continue;
        if (strWords[1].contains("KEY_"))
            strWords[1] = strWords[1].right(strWords[1].length()-4);
        QString addedElement = strWords[1] + " (" + QString::number(kbdCode,16) + ")";
        ui->keyList->addItem(addedElement);
        m_keyComments.push_back(strComment);
#endif
    }
    ui->label_keyComment->setText(QString::number(m_kbdCodes.length()) + " keys in list");
    fileKbdCodes.close();
    ui->generatedMatrixTable->setShowGrid(true); // Включаем сетку
    // Разрешаем выделение только одного элемента
    ui->generatedMatrixTable->setSelectionMode(QAbstractItemView::SingleSelection);
    //
    ui->generatedMatrixTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
    //int res = system("iceprog hardware.bin");
    //qDebug ("Result: %u", res);
    //ui->keyList->event();
    QObject::connect(ui->normalMatrixMode, SIGNAL(toggled(bool)), this, SLOT(displayMatrix()));
    QObject::connect(ui->fnMatrixMode, SIGNAL(toggled(bool)), this, SLOT(displayMatrix()));

    //m_fpgaFwGeneratorTrd.start();
    //m_fpgaFwGenerator.moveToThread(&m_fpgaFwGeneratorTrd);
    m_fpgaFwGenerator = new QProcess(Q_NULLPTR/*this*/);
    m_fpgaFwGenerator->setProcessChannelMode(QProcess::ProcessChannelMode::MergedChannels);
    QObject::connect(m_fpgaFwGenerator, SIGNAL(readyRead()), this, SLOT(processReadyRead()));
    QObject::connect(m_fpgaFwGenerator, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(processFinished(int,QProcess::ExitStatus)));
    QObject::connect(m_fpgaFwGenerator, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError(QProcess::ProcessError)));

    QObject::connect(&m_uiKbdList, SIGNAL(kbdSelected(int, int)), this, SLOT(qmkKbdSelected(int, int)));
}

i2c_keyboard_detection::~i2c_keyboard_detection()
{
    delete ui;
}

void i2c_keyboard_detection::keyPressEvent(QKeyEvent *e)
{
    qDebug("Key pressed: %u", e->nativeScanCode());
//    Qt::Key_S;
}

void i2c_keyboard_detection::on_keyList_itemSelectionChanged()
{
    qDebug ("Current key item changed! Now %u", ui->keyList->currentRow());
    if (ui->keyList->currentRow() < 0)
        return;
    /*ui->keyList->setToolTip*///ui->label_keyComment->setText(m_keyComments.at(ui->keyList->currentRow()));
    QString currentKey = ui->keyList->currentItem()->text();
    for (int i = 0; i < m_kbdDetectReport.length(); i++)
    {
        if ((m_kbdDetectReport[i].split("\t").at(0) == currentKey) && (ui->enSendKeyToReport->isChecked()))
            QMessageBox::warning(this, "KEY IS ALREADY ADDED", "This key was already added to report. When you press some key, old value will be lost");
    }
}

void i2c_keyboard_detection::on_comRefleshList_clicked()
{
    QList<QSerialPortInfo> comList = QSerialPortInfo::availablePorts();
    ui->comList->clear();
    for (int i = 0; i < comList.length(); i++)
        ui->comList->addItem(comList.at(i).portName());
}

void i2c_keyboard_detection::on_comConnect_clicked()
{
    QWidget* widgets_locked[] = { ui->comList, ui->comRefleshList };
    QWidget* widgets_unlocked[] = {  };
    if (ui->comConnect->text() == "Connect")   {
        m_com.setPortName(ui->comList->currentText());
        if (m_com.open(QSerialPort::ReadWrite))   {
            m_com.setParity(QSerialPort::NoParity);
            m_com.setDataBits(QSerialPort::Data8);
            m_com.setStopBits(QSerialPort::OneStop);
            m_com.setFlowControl(QSerialPort::NoFlowControl);
            m_com.setBaudRate(115200);
            ui->comConnect->setText("Disconnect");
            m_kbdDetectBytes.clear();
            m_kbdDetectData.clear();
            m_isFirstComMsg = true;
        }
        else
            QMessageBox::warning(this, "COM OPEN ERROR", "Com " + ui->comList->currentText() + " cannot open!");
    }
    else    {
        m_com.close();
        ui->comConnect->setText("Connect");
    }
    for (int i = 0; i < sizeof(widgets_locked)/sizeof(QWidget*); i++)
        widgets_locked[i]->setEnabled(!m_com.isOpen());
    for (int i = 0; i < sizeof(widgets_unlocked)/sizeof(QWidget*); i++)
        widgets_unlocked[i]->setEnabled(m_com.isOpen());
}

void i2c_keyboard_detection::comDataReceived()
{
    QByteArray dataFromCom = m_com.readAll();
    for (int i = 0; i < dataFromCom.length(); i++)
    {
        if (dataFromCom[i] == 0xFF)
        {
            if (m_kbdDetectBytes.length() != 5)
            {
                //on_comConnect_clicked();
                if (m_isFirstComMsg)
                    m_isFirstComMsg = false;
                else
                    QMessageBox::warning(this, "COM PROTOCOL ERROR", "Received start, but last frame is not complete");
            }
            m_kbdDetectBytes.clear();
        }
        else
        {
            m_kbdDetectBytes.push_back(dataFromCom[i]);
            if (m_kbdDetectBytes.length() > 5)
            {
                if (m_com.isOpen())
                    on_comConnect_clicked();
                QMessageBox::warning(this, "COM PROTOCOL ERROR", "Received extra byte of frame");
            }
            else if (m_kbdDetectBytes.length() == 5)
            {
                int64_t result = ((((int64_t)m_kbdDetectBytes[4]) & 0xFF) << 0) |
                        ((((int64_t)m_kbdDetectBytes[3]) & 0xFF) << 8) | ((((int64_t)m_kbdDetectBytes[2]) & 0xFF) << 16) |
                        ((((int64_t)m_kbdDetectBytes[1]) & 0xFF) << 24) | ((((int64_t)m_kbdDetectBytes[0]) & 0xFF) << 32);
                //m_kbdDetectBytes.clear();
                if (result != 0)
                {
                    for (int k = 0; k < m_kbdDetectData.length(); k++)    {
                        if (m_kbdDetectData[k] == result)
                            return; //  RECEIVING IN PROCESS, WAIT TERMINATION FRAME, THIS FRAME WAS ALREADY ADDED
                    }
                    m_kbdDetectData.push_back(result);
                    return; //  RECEIVING IN PROCESS, WAIT TERMINATION FRAME
                }
                //  TERMINATION FRAME PROCESSING START
                QString currentStatus;
                QVector<int> connectedLines;
                if (m_kbdDetectData.length() == 0)
                    currentStatus = "No keys are pressed";
                else    {
                    currentStatus = "Detected connections: ";
                    for (int k = 0; k < m_kbdDetectData.length(); k++)    {
                        //currentKbdReport = currentKbdReport + QString::number(last_kbd_data[k]) + ", ";
                        for (quint64 n = 0; n < 34; n++)    {
                            quint64 mask = 1;
                            mask = mask << n;
                            if ((m_kbdDetectData[k] & mask) != 0)
                            {
                                currentStatus = currentStatus + QString::number(n) + " ";
                                connectedLines.push_back(n);
                            }
                        }
                        currentStatus = currentStatus + ", ";
                    }
                    currentStatus = currentStatus.left(currentStatus.length() - 2);
                    m_kbdDetectData.clear();
                }
                currentStatus = currentStatus + " (" + QTime::currentTime().toString("HH:mm:ss.zzz") + ")";
                ui->labelKeyboardStatus->setText(currentStatus);
                if ((connectedLines.length() != 0) && (ui->enSendKeyToReport->isChecked()) && (ui->keyList->currentRow() >= 0))
                {
                    QString currentKey = ui->keyList->currentItem()->text();
                    for (int k = (m_kbdDetectReport.length() - 1); k >=0; k--)
                    {
                        if (m_kbdDetectReport[k].split("\t").at(0) == currentKey)
                            m_kbdDetectReport.remove(k);
                    }
                    for (int k = 0; k < connectedLines.length(); k++)
                        currentKey = currentKey + "\t" + QString::number(connectedLines[k]);
                    m_kbdDetectReport.push_front(currentKey);
                    QString tarReport;
                    tarReport = "Now " + QString::number(m_kbdDetectReport.length()) + " keys added\n";
                    for (int k = 0; k < m_kbdDetectReport.length(); k++)
                        tarReport = tarReport + m_kbdDetectReport[k] + "\n";
                    ui->kbdReport->setText(tarReport);
                }
            }
        }
    }
}

void i2c_keyboard_detection::on_enSendKeyToReport_clicked(bool checked)
{
    if (checked)
        on_keyList_itemSelectionChanged();
}

void i2c_keyboard_detection::on_saveKbdReport_clicked()
{
    QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"), "kbd_connections.txt", tr("*.txt"));
    if (fileName == "")
        return;
    QFile savedFile(fileName);
    if (!savedFile.open(QFile::WriteOnly))
    {
        QMessageBox::warning(this, "CANNOT SAVE FILE", "Can't open file for save report");
        return;
    }
    QString tarReport;
    for (int k = 0; k < m_kbdDetectReport.length(); k++)
        tarReport = tarReport + m_kbdDetectReport[k] + "\n";
    if (savedFile.exists())
        savedFile.resize(0);
    savedFile.write(tarReport.toUtf8());
    savedFile.close();
}

void i2c_keyboard_detection::on_openConnectionsFile_clicked()
{
    QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"), "kbd_connections.txt", tr("*.txt"));
    if (fileName == "")
        return;
    QFile openedFile(fileName);
    if (!openedFile.open(QFile::ReadOnly))
    {
        QMessageBox::warning(this, "CANNOT OPEN FILE", "Can't open file for save report");
        return;
    }
    QVector<QString> keyNames;
    QVector<int> keyIo;
    while(1)
    {
        QString currentStr = QString(openedFile.readLine());
        if (currentStr.length() == 0)
            break;
        QStringList strWords = currentStr.split("\t");
        if (strWords.length() != 3)
        {
            openedFile.close();
            QMessageBox::warning(this, "UNCORRECT FILE OPENED", "Uncorrect data in file: str without 2 tabs");
            return;
        }
        if ((strWords[0].indexOf("(") == -1) || (strWords[0].indexOf(")") == -1))
        {
            openedFile.close();
            QMessageBox::warning(this, "UNCORRECT FILE OPENED", "Uncorrect data in file: uncorrect key description (no hex key value)");
            return;
        }
        else if ((strWords[0] [strWords[0].length()-1] != ")") || ((strWords[0][strWords[0].length()-3] != "(") && (strWords[0][strWords[0].length()-4] != "(")))
        {
            openedFile.close();
            QMessageBox::warning(this, "UNCORRECT FILE OPENED", "Uncorrect data in file: uncorrect key description (no hex key value)");
            return;
        }
        else if (strWords[0][strWords[0].length()-3] == "(")
            strWords[0].insert(strWords[0].length() - 2, "0");
        keyNames.push_back(strWords[0]);
        int pin1Num, pin2Num;
        bool pinCorr1, pinCorr2;
        pin1Num = strWords[1].toInt(&pinCorr1, 10);
        pin2Num = strWords[2].toInt(&pinCorr2, 10);
        if ((pinCorr1 == false) || (pinCorr2 == false))
        {
            openedFile.close();
            QMessageBox::warning(this, "UNCORRECT FILE OPENED", "Uncorrect data in file: uncorrect pin number");
            return;
        }
        keyIo.push_back(pin1Num);
        keyIo.push_back(pin2Num);
    }
    openedFile.close();
    QVector<int> allIoNumbers;
    for (int i = 0; i < keyIo.length(); i++)
    {
        if (allIoNumbers.indexOf(keyIo[i]) == -1)
            allIoNumbers.push_back(keyIo[i]);
    }
    qSort(allIoNumbers);

    QVector<QVector<int>> ioConnections(allIoNumbers.length());
    for (int i = 0; i < ioConnections.length(); i++)
    {
        ioConnections[i].clear();
        for (int j = 0; j < keyIo.length(); j++)
        {
            if (allIoNumbers[i] == keyIo[j])
            {
                int num2;
                if ((j % 2) == 0)
                    num2 = j + 1;
                else
                    num2 = j - 1;
                if (keyIo[num2] == allIoNumbers[i])
                {
                    QMessageBox::warning(this, "UNCORRECT FILE OPENED", "Uncorrect data in file: pin numbers of one key are the same!");
                    return;
                }
                if (ioConnections[i].indexOf(keyIo[num2]) == -1)
                    ioConnections[i].push_back(keyIo[num2]);
            }
        }
    }

    QVector<QVector<QVector<QString>>> allGeneratedMatrixes;
    QVector<QVector<int>> allGeneratedColumns;
    QVector<QVector<int>> allGeneratedRows;
    for (int i = 0; i < allIoNumbers.length(); i++)
    {
        int currentColumnCnt = 0;
        int currentRowCnt = 0;
        QVector<int> currentColumn(1, allIoNumbers[i]);
        QVector<int> currentRow;
        while(1)
        {
            if (currentColumnCnt == currentColumn.length())
            {
                qDebug ("Warning, not all IO sorted!");
                break;
            }
            for (int j = currentColumnCnt; j < currentColumn.length(); j++)
            {
                int ioIndex = allIoNumbers.indexOf(currentColumn[j]);
                if (ioIndex == -1)
                {
                    QMessageBox::warning(this, "CRITICAL ALGORITM ERROR", "Need more engeneers-programmists!");
                    return;
                }
                for (int k = 0; k < ioConnections[ioIndex].length(); k++)
                {
                    if (currentRow.indexOf(ioConnections[ioIndex][k]) == -1)
                        currentRow.push_back(ioConnections[ioIndex][k]);
                }
            }
            currentColumnCnt = currentColumn.length();

            if ((currentRow.length() + currentColumn.length()) == allIoNumbers.length())
                break;

            if (currentRowCnt == currentRow.length())
            {
                qDebug ("Warning, not all IO sorted!");
                break;
            }
            for (int j = currentRowCnt; j < currentRow.length(); j++)
            {
                int ioIndex = allIoNumbers.indexOf(currentRow[j]);
                if (ioIndex == -1)
                {
                    QMessageBox::warning(this, "CRITICAL ALGORITM ERROR", "Need more engeneers-programmists!");
                    return;
                }
                for (int k = 0; k < ioConnections[ioIndex].length(); k++)
                {
                    if (currentColumn.indexOf(ioConnections[ioIndex][k]) == -1)
                        currentColumn.push_back(ioConnections[ioIndex][k]);
                }
            }
            currentRowCnt = currentRow.length();

            if ((currentRow.length() + currentColumn.length()) == allIoNumbers.length())
                break;
        }
        qSort(currentColumn);
        qSort(currentRow);
        qDebug ("Columns and rows are sorted!");
        if (currentColumn.length() != currentRow.length())
        {
            if (currentColumn.length() > currentRow.length()/*currentRow.indexOf(allIoNumbers[0]) == -1*/)
            {
                QVector<int> temp(currentRow);
                currentRow = currentColumn;
                currentColumn = temp;
            }
        }
        else
        {
            if (currentRow.indexOf(allIoNumbers[0]) == -1)
            {
                QVector<int> temp(currentRow);
                currentRow = currentColumn;
                currentColumn = temp;
            }
        }

        if ((!allGeneratedRows.contains(currentRow)) || (!allGeneratedColumns.contains(currentColumn)))
        {
            allGeneratedRows.push_back(currentRow);
            allGeneratedColumns.push_back(currentColumn);
            QVector<QVector<QString>> currentMatrix(currentRow.length());
            for (int j = 0; j < currentRow.length(); j++)
            {
                currentMatrix[j].resize(currentColumn.length());
                currentMatrix[j].fill("(00)");
                for (int k = 0; k < currentColumn.length(); k++)
                {
                    for (int n = 0; n < (keyIo.length() / 2); n++)
                    {
                        if (((currentColumn[k] == keyIo[2*n]) && (currentRow[j] == keyIo[2*n+1])) ||
                            ((currentColumn[k] == keyIo[2*n+1]) && (currentRow[j] == keyIo[2*n])))
                        {
                            if (currentMatrix[j][k] != "(00)")
                            {
                                QMessageBox::warning(this, "CRITICAL ALGORITM ERROR",
                                                     QString("More than one pin with column %1 and row %2 detected").arg(currentColumn[j]).arg(currentRow[k]));
                                return;
                            }
                            currentMatrix[j][k] = keyNames[n];
                        }
                    }
                }
            }
            allGeneratedMatrixes.push_back(currentMatrix);
        }
    }
    if (allGeneratedMatrixes.length() != 1)
    {
        QMessageBox::warning(this, "ALGORITM WARNING", "More than one matrix is generated, this option is not added yet");
        return;
    }
    QString tarMatrix = "\t";
    for (int i = 0; i < allGeneratedColumns[0].length(); i++)
        tarMatrix = tarMatrix + QString::number(allGeneratedColumns[0][i]) + "\t";
    for (int i = 0; i < allGeneratedRows[0].length(); i++)
    {
        tarMatrix = tarMatrix + "\n" + QString::number(allGeneratedRows[0][i]);
        for (int j = 0; j < allGeneratedColumns[0].length(); j++)
            tarMatrix = tarMatrix + "\t" + allGeneratedMatrixes[0][i][j];
    }
    m_generatedMatrix = allGeneratedMatrixes[0];
    m_generatedFnMatrix = allGeneratedMatrixes[0];

    m_generatedColumns.resize(allGeneratedColumns[0].length());
    for (int i = 0; i < allGeneratedColumns[0].length(); i++)
        m_generatedColumns[i] = QString::number(allGeneratedColumns[0][i]);
    m_generatedRows.resize(allGeneratedRows[0].length());
    for (int i = 0; i < allGeneratedRows[0].length(); i++)
        m_generatedRows[i] = QString::number(allGeneratedRows[0][i]);
    //m_generatedColumns = allGeneratedColumns[0];
    //m_generatedRows = allGeneratedRows[0];

    displayMatrix();
}

void i2c_keyboard_detection::on_clearKbdReport_clicked()
{
    m_kbdDetectReport.clear();
    ui->kbdReport->clear();
}

void i2c_keyboard_detection::resizeEvent(QResizeEvent *event)
{
    static bool isStarted = false;
    if (!isStarted)
    {
        isStarted = true;
        return;
    }
    int dx = event->size().width() - event->oldSize().width();
    int dy = event->size().height() - event->oldSize().height();
    ui->tabWidget->resize(ui->tabWidget->size().width() + dx, ui->tabWidget->size().height() + dy);
    ui->kbdReport->resize(ui->kbdReport->size().width() + dx, ui->kbdReport->size().height() + dy);
    ui->generatedMatrixTable->resize(ui->generatedMatrixTable->size().width() + dx, ui->generatedMatrixTable->size().height() + dy);
    QRect viewRect = ui->generatedMatrixTable->rect();
    if (ui->generatedMatrixTable->columnCount() < 1)
        return;
    int columnWidth = (viewRect.width() - 100) / ui->generatedMatrixTable->columnCount();
    for (int i = 0; i < ui->generatedMatrixTable->columnCount(); i++)
        ui->generatedMatrixTable->setColumnWidth(i, columnWidth);
    //ui->generatedMatrixTable->setFixedWidth(columnWidth*ui->generatedMatrixTable->columnCount());
}

void i2c_keyboard_detection::displayMatrix()
{
    if ((m_generatedColumns.length() < 1) || (m_generatedRows.length() < 1) || (m_generatedMatrix.length() < 1))
        return;

    //
    ui->generatedMatrixTable->clear();
    ui->generatedMatrixTable->setRowCount(m_generatedRows.length());
    ui->generatedMatrixTable->setColumnCount(m_generatedColumns.length()); // Указываем число колонок

    QStringList matrixHeaders;
    for (int i = 0; i < m_generatedColumns.length(); i++)
        matrixHeaders.push_back(m_generatedColumns[i]);
    // Устанавливаем заголовки колонок
    ui->generatedMatrixTable->setHorizontalHeaderLabels(matrixHeaders);

    int notEmptyElementsNum = 0;
    for(int i = 0; i < m_generatedRows.length(); i++){
        // Вставляем строку
        //ui->generatedMatrixTable->insertRow(i);
        for (int j = 0; j < m_generatedColumns.length(); j++)
        {
            QTableWidgetItem* newItem;
            if (ui->normalMatrixMode->isChecked())
            {
                newItem = new QTableWidgetItem(m_generatedMatrix[i][j]);
                if (m_generatedMatrix[i][j].contains("Fn"))
                    newItem->setBackground( QBrush ( QColor(170, 200, 255) ) );
            }
            else
            {
                newItem = new QTableWidgetItem(m_generatedFnMatrix[i][j]);
                if (m_generatedFnMatrix[i][j] != m_generatedMatrix[i][j])
                    newItem->setBackground( QBrush ( QColor(170, 200, 255) ) );
                if (m_generatedFnMatrix[i][j].contains("Fn"))
                    newItem->setBackground( QBrush ( QColor(170, 200, 255) ) );
            }
            if (m_generatedMatrix[i][j] == "(00)")
                newItem->setBackground( QBrush ( QColor(180, 180, 180) ) );
            else
                notEmptyElementsNum++;
            ui->generatedMatrixTable->setItem(i,j, newItem);
        }
    }
    matrixHeaders.clear();
    for (int i = 0; i < m_generatedRows.length(); i++)
        matrixHeaders.push_back(m_generatedRows[i]);
    ui->generatedMatrixTable->setVerticalHeaderLabels(matrixHeaders);

    QRect viewRect = ui->generatedMatrixTable->rect();
    int columnWidth = (viewRect.width() - 50) / m_generatedColumns.length();//Calculate first column's width as 80% of QTableView's width.
    for (int i = 0; i < m_generatedColumns.length(); i++)
        ui->generatedMatrixTable->setColumnWidth(i, columnWidth);
    m_generatedNotEmptyElements = notEmptyElementsNum;
    QString matrixInfo = QString("%1 columns, %2 rows, %3 elements").arg(m_generatedColumns.length()).arg(m_generatedRows.length()).arg(notEmptyElementsNum);
    ui->label_matrixInfo->setText(matrixInfo);

    //  CALC ROW MERGING
    QStringList rowMergingVariants;
    for (int i = 0; i < (m_generatedMatrix.length() - 1); i++)
    {
        for (int j = (i + 1); j < m_generatedMatrix.length(); j++)
        {
            bool sameColumnsDetected = false;
            for (int k = 0; k < m_generatedMatrix[i].length(); k++)
            {
                if ((m_generatedMatrix[i][k] != "(00)") && (m_generatedMatrix[j][k] != "(00)"))
                    sameColumnsDetected = true;
            }
            if (!sameColumnsDetected)
                rowMergingVariants.push_back(m_generatedRows[i] + " + " + m_generatedRows[j]);
        }
    }
    ui->mergingVariants->clear();
    ui->mergingVariants->addItems(rowMergingVariants);

    if ((m_diodeDirection >= 0) && (m_diodeDirection < ui->diodeDirection->count()))
        ui->diodeDirection->setCurrentIndex(m_diodeDirection);
}

void i2c_keyboard_detection::on_swapColumnsAndRows_clicked()
{
    QVector<QString> newColumns(m_generatedRows);
    QVector<QString> newRows(m_generatedColumns);
    QVector<QVector<QString>> newMatrix(newRows.length());
    for (int i = 0; i < newMatrix.length(); i++)
    {
        newMatrix[i].resize(newColumns.length());
        for (int j = 0; j < newColumns.length(); j++)
            newMatrix[i][j] = m_generatedMatrix[j][i];
    }
    m_generatedMatrix = newMatrix;
    for (int i = 0; i < newMatrix.length(); i++)
    {
        newMatrix[i].resize(newColumns.length());
        for (int j = 0; j < newColumns.length(); j++)
            newMatrix[i][j] = m_generatedFnMatrix[j][i];
    }
    m_generatedFnMatrix = newMatrix;
    m_generatedRows = newRows;
    m_generatedColumns = newColumns;
    displayMatrix();
}

void i2c_keyboard_detection::on_mergeRows_clicked()
{
    int index1, index2;
    QStringList indexes = ui->mergingVariants->currentText().split(" + ");
    if (indexes.length() != 2)
        return;
    index1 = m_generatedRows.indexOf(indexes[0]);
    index2 = m_generatedRows.indexOf(indexes[1]);
    QVector<QString> newRow(m_generatedColumns.length());
    QVector<QString> newFnRow(m_generatedColumns.length());
    for (int i = 0; i < m_generatedColumns.length(); i++)
    {
        if (m_generatedMatrix[index1][i] == "(00)")
        {
            newRow[i] = m_generatedMatrix[index2][i];
            newFnRow[i] = m_generatedFnMatrix[index2][i];
        }
        else
        {
            newRow[i] = m_generatedMatrix[index1][i];
            newFnRow[i] = m_generatedFnMatrix[index1][i];
        }
    }
    m_generatedRows.remove(index2);
    m_generatedRows[index1] = indexes[0] + "+" + indexes[1];
    m_generatedMatrix.remove(index2);
    m_generatedMatrix[index1] = newRow;
    m_generatedFnMatrix.remove(index2);
    m_generatedFnMatrix[index1] = newFnRow;
    displayMatrix();
}

void i2c_keyboard_detection::on_setFnKey_clicked()
{
    if (ui->generatedMatrixTable->selectedRanges().length() < 1)
        return;
    int row = ui->generatedMatrixTable->selectedRanges()[0].topRow();
    int column = ui->generatedMatrixTable->selectedRanges()[0].leftColumn();
    m_generatedFnMatrix[row][column] = ui->keyList->currentItem()->text();
    displayMatrix();
}

void i2c_keyboard_detection::on_openMatrixKbdFile_clicked()
{
    if ((m_generatedColumns.length() > 8) || (m_generatedRows.length() > 16) || (m_generatedColumns.length() == 0) || (m_generatedRows.length() == 0))
    {
        QMessageBox::warning(this, "INPUT DATA IS INCORRECT", "Columns num more than 8, or rows num more than 16, or columns are empty, or rows are empty");
        return;
    }
    QString fileName = QFileDialog::getOpenFileName(this, tr("Open File matrix_kbd.v"), "", tr("matrix_kbd.v"));
    if (fileName == "")
        return;
    QFile openedFile(fileName);
    if (!openedFile.open(QFile::ReadWrite))
    {
        QMessageBox::warning(this, "CANNOT OPEN FILE", "Can't open file for save report");
        return;
    }
    QVector<QString> linesFromFile;
    int declarationsCounter = 0;
    int assignKbdRcStart = 0;
    int assignColumnsStart = 0;
    int kbdCodesRamStart = 0;
    int kbdFnCodesRamStart = 0;
    int kbdColumnsSbIoStart = 0;    //  TODO: ADD SEEKING OF THIS
    int kbdRowsSbIoStart = 0;    //  TODO: ADD SEEKING OF THIS
    while(1)    {
        QString currentStr = QString(openedFile.readLine());
        if (currentStr.length() == 0)
            break;
        linesFromFile.push_back(currentStr);
        int len = linesFromFile.length();
        if ((linesFromFile[len - 1].contains("wire ")) && (linesFromFile[len - 1].contains(" KBD_ROWS_EN")))
            declarationsCounter++;
        else if ((linesFromFile[len - 1].contains("wire ")) && (linesFromFile[len - 1].contains(" KBD_ROWS_IN")))
            declarationsCounter++;
        else if ((linesFromFile[len - 1].contains("reg ")) && (linesFromFile[len - 1].contains(" KBD_ROWS_OUT")))
            declarationsCounter++;
        else if ((linesFromFile[len - 1].contains("reg ")) && (linesFromFile[len - 1].contains(" KBD_COLUMNS_EN")))
            declarationsCounter++;
        else if ((linesFromFile[len - 1].contains("wire ")) && (linesFromFile[len - 1].contains(" KBD_COLUMNS_IN")))
            declarationsCounter++;
        else if ((linesFromFile[len - 1].contains("reg ")) && (linesFromFile[len - 1].contains(" KBD_COLUMNS_OUT")))
            declarationsCounter++;
        else if (linesFromFile[len - 1].contains("assign KBD_ROWS_EN["))
        {
            assignKbdRcStart = len - 1;
            linesFromFile.remove(len - 1);
        }
        else if (linesFromFile[len - 1].contains("assign COLUMNS["))
        {
            assignColumnsStart = len - 1;
            linesFromFile.remove(len - 1);
        }
        else if (len > 12)
        {
            if ( (linesFromFile[len - 4].contains(".INIT_7(")) && (linesFromFile[len - 5].contains(".INIT_6(")) &&
                    (linesFromFile[len - 6].contains(".INIT_5(")) && (linesFromFile[len - 7].contains(".INIT_4(")) &&
                    (linesFromFile[len - 8].contains(".INIT_3(")) && (linesFromFile[len - 9].contains(".INIT_2(")) &&
                    (linesFromFile[len - 10].contains(".INIT_1(")) && (linesFromFile[len - 11].contains(".INIT_0(")) &&
                    (linesFromFile[len - 12].contains("SB_RAM40_4K #(")) )
            {
                if ((linesFromFile[len - 1].contains(") kbd_codes (")) && (kbdCodesRamStart == 0))
                    kbdCodesRamStart = len - 11;
                else if ((linesFromFile[len - 1].contains(") kbd_codes_fn (")) && (kbdFnCodesRamStart == 0))
                    kbdFnCodesRamStart = len - 11;
                else
                {
                    QMessageBox::warning(this, "FILE ERROR!", "More than 2 RAM tables or uncorrect RAM tables in file!");
                    openedFile.close();
                    return;
                }
            }
            else if ( (linesFromFile[len - 5].contains("SB_IO #(")) && (linesFromFile[len - 4].contains(".PIN_TYPE(6'b")) &&
                      (linesFromFile[len - 3].contains(".PULLUP(1'b")) && (linesFromFile[len - 1].contains(".PACKAGE_PIN(")) )
            {
                if ((linesFromFile[len - 2].contains("kbd_columns")) && (kbdColumnsSbIoStart == 0))
                    kbdColumnsSbIoStart = len - 1;
                else if ((linesFromFile[len - 2].contains("kbd_rows")) && (kbdRowsSbIoStart == 0))
                    kbdRowsSbIoStart = len - 1;
                else
                {
                    QMessageBox::warning(this, "FILE ERROR!", "More than 2 SB IO or uncorrect SB IO in file!");
                    openedFile.close();
                    return;
                }
            }
        }
    }
    if ( (!((kbdRowsSbIoStart > kbdColumnsSbIoStart) && (kbdColumnsSbIoStart > kbdFnCodesRamStart) &&
            (kbdFnCodesRamStart > kbdCodesRamStart) && (kbdCodesRamStart > assignKbdRcStart) && (assignKbdRcStart > assignColumnsStart)) ) ||
            (kbdFnCodesRamStart == 0) || (kbdCodesRamStart == 0) || (assignColumnsStart == 0) ||
            (assignKbdRcStart == 0) || (kbdColumnsSbIoStart == 0) || (kbdRowsSbIoStart == 0) || (declarationsCounter != 6) )
    {
        QMessageBox::warning(this, "FILE ERROR!", "KBD_RC or COLUMNS assigns, or RAM tables not found, or are in incorrect order");
        openedFile.close();
        return;
    }
    //  TODO: ADD LIST WITH ALL COLUMNS AND ROWS (PHY, NOT LOGICAL)
    QVector<QString> allColumnIo;
    QVector<QString> allRowIo;
    foreach (QString column, m_generatedColumns)
    {
        QStringList columnData = column.split("+");
        foreach (QString oneColumnPin, columnData)
            allColumnIo.push_back(oneColumnPin);
    }
    foreach (QString row, m_generatedRows)
    {
        QStringList rowData = row.split("+");
        foreach (QString oneRowPin, rowData)
            allRowIo.push_back(oneRowPin);
    }
    for (int i = 0; i < linesFromFile.length(); i++)
    {
        //  TODO: CORRECT ALL THIS DECLARATIONS
        if ((linesFromFile[i].contains("wire ")) && (linesFromFile[i].contains(" KBD_ROWS_EN")))
            linesFromFile[i] = QString("  wire [%1:0] KBD_ROWS_EN;\n").arg(allRowIo.size() - 1);
        else if ((linesFromFile[i].contains("wire ")) && (linesFromFile[i].contains(" KBD_ROWS_IN")))
            linesFromFile[i] = QString("  wire [%1:0] KBD_ROWS_IN;\n").arg(allRowIo.size() - 1);
        else if ((linesFromFile[i].contains("reg ")) && (linesFromFile[i].contains(" KBD_ROWS_OUT")))
            linesFromFile[i] = QString("  reg [%1:0] KBD_ROWS_OUT = 0;\n").arg(allRowIo.size() - 1);
        else if ((linesFromFile[i].contains("reg ")) && (linesFromFile[i].contains(" KBD_COLUMNS_EN")))
            linesFromFile[i] = QString("  reg [%1:0] KBD_COLUMNS_EN = 0;\n").arg(allColumnIo.size() - 1);
        else if ((linesFromFile[i].contains("wire ")) && (linesFromFile[i].contains(" KBD_COLUMNS_IN")))
            linesFromFile[i] = QString("  wire [%1:0] KBD_COLUMNS_IN;\n").arg(allColumnIo.size() - 1);
        else if ((linesFromFile[i].contains("reg ")) && (linesFromFile[i].contains(" KBD_COLUMNS_OUT")))
            linesFromFile[i] = QString("  reg [%1:0] KBD_COLUMNS_OUT = 0;\n").arg(allColumnIo.size() - 1);
    }
    //  TODO: ADD CORRECTION OF KBD_COLUMNS SB_IO AND KBD_ROWS SB_IO (LIST OF RC_IO)
    //  TODO: CORRECT ALL ASSIGNS AS IT IS IN NEW FW
    QString ioPackagePin = "\t\t.PACKAGE_PIN( { ";
//    foreach(QString rowIo, allRowIo)
//        ioPackagePin += QString("KBD_RC[%1], ").arg(rowIo);
    for (int i = (allRowIo.size() - 1); i >= 0; i--)
        ioPackagePin += QString("KBD_RC[%1], ").arg(allRowIo[i]);
    ioPackagePin.resize(ioPackagePin.size() - 2);
    ioPackagePin += " } ),\n";
    linesFromFile[kbdRowsSbIoStart] = ioPackagePin;
    linesFromFile[kbdRowsSbIoStart - 1] = QString("  ) kbd_rows [%1:0] (\n").arg(allRowIo.size() - 1);

    ioPackagePin = "\t\t.PACKAGE_PIN( { ";
//    foreach(QString columnIo, allColumnIo)
//        ioPackagePin += QString("KBD_RC[%1], ").arg(columnIo);
    for (int i = (allColumnIo.size() - 1); i >= 0; i--)
        ioPackagePin += QString("KBD_RC[%1], ").arg(allColumnIo[i]);
    ioPackagePin.resize(ioPackagePin.size() - 2);
    ioPackagePin += " } ),\n";
    linesFromFile[kbdColumnsSbIoStart] = ioPackagePin;    
    linesFromFile[kbdColumnsSbIoStart - 1] = QString("  ) kbd_columns [%1:0] (\n").arg(allColumnIo.size() - 1);

    for (int i = 0; i < 8; i++)
    {
        QString ramCodes = QString("\t\t.INIT_%1(256'h").arg(i);
        QString ramCodesFn = ramCodes;
        if (i >= m_generatedColumns.length())
        {
            ramCodes = ramCodes + "0000_0000_0000_0000_0000_0000_0000_0000__0000_0000_0000_0000_0000_0000_0000_0000";
            ramCodesFn = ramCodesFn + "0000_0000_0000_0000_0000_0000_0000_0000__0000_0000_0000_0000_0000_0000_0000_0000";
            continue;
        }
        for (int j = 15; j >= 0; j--)
        {
            if (j < m_generatedRows.length())
            {
                ramCodes = ramCodes + "00" + m_generatedMatrix[j][i].mid(m_generatedMatrix[j][i].length() - 3, 2) + "_";
                ramCodesFn = ramCodesFn + "00" + m_generatedFnMatrix[j][i].mid(m_generatedFnMatrix[j][i].length() - 3, 2) + "_";
            }
            else
            {
                ramCodes = ramCodes + "0000_";
                ramCodesFn = ramCodesFn + "0000_";
            }
            if (j == 8)
            {
                ramCodes = ramCodes + "_";
                ramCodesFn = ramCodesFn + "_";
            }
            else if (j == 0)
            {
                ramCodes.resize(ramCodes.length() - 1);
                ramCodesFn.resize(ramCodesFn.length() - 1);
            }
        }
        linesFromFile[kbdCodesRamStart+i] = ramCodes + "),\n";
        linesFromFile[kbdFnCodesRamStart+i] = ramCodesFn + "),\n";
    }

#if 1
    for (int i = (allRowIo.length() - 1); i >= 0; i--)
    {
        for (int j = 0; j < m_generatedRows.length(); j++)
        {
            QStringList currentRowIo = m_generatedRows[j].split("+");
            if (currentRowIo.contains(allRowIo[i]))
                linesFromFile.insert(assignKbdRcStart, QString("  assign KBD_ROWS_EN[%1] = ROWS_EN[%2];\n").arg(i).arg(j));
        }
    }
    for (int i = 7; i >= 0; i--)
    {
        if (i >= m_generatedColumns.length())
            linesFromFile.insert(assignColumnsStart, QString("  assign COLUMNS[%1] = 0;\n").arg(i));
        else
        {
            QStringList columnsList = m_generatedColumns[i].split("+");
            QString insertedStr = QString("  assign COLUMNS[%1] = ").arg(i);
            foreach (QString columnIo, columnsList)
                insertedStr = insertedStr + QString("KBD_COLUMNS_IN[%1] & ").arg(allColumnIo.indexOf(columnIo));
            insertedStr.resize(insertedStr.length() - 3);
            insertedStr = insertedStr + ";\n";
            linesFromFile.insert(assignColumnsStart, insertedStr);
        }
    }
#else
    for (int i = 7; i >= 0; i--)
    {
        if (i >= m_generatedColumns.length())
            linesFromFile.insert(assignColumnsStart, QString("  assign COLUMNS[%1] = 0;\n").arg(i));
        else
        {
            QStringList columnsList = m_generatedColumns[i].split("+");
            QString insertedStr = QString("  assign COLUMNS[%1] = ").arg(i);
            for (int j = 0; j < columnsList.length(); j++)
                insertedStr = insertedStr + "KBD_RC_IN[" + columnsList[j] + "] & ";
            insertedStr.resize(insertedStr.length() - 3);
            insertedStr = insertedStr + ";\n";
            linesFromFile.insert(assignColumnsStart, insertedStr);
        }
    }
    for (int i = 33; i >=0; i--)
    {
        bool isFound = false;
        for (int j = 0; j < m_generatedRows.length(); j++)
        {
            QStringList rowList = m_generatedRows[j].split("+");
            for (int k = 0; k < rowList.length(); k++)
            {
                int num = rowList[k].toInt();
                if (num == i)
                {
                    linesFromFile.insert(assignKbdRcStart, QString("  assign KBD_RC_EN[%1] = ROWS_EN[%2];\n").arg(i).arg(j));
                    if (isFound)
                    {
                        QMessageBox::warning(this, "ALGORITM ERROR!", "Need more coders");
                        openedFile.close();
                        return;
                    }
                    isFound = true;
                }
            }
        }
        if (!isFound)
           linesFromFile.insert(assignKbdRcStart, QString("  assign KBD_RC_EN[%1] = 0;\n").arg(i));
    }
#endif

    openedFile.close();
    openedFile.open(QFile::WriteOnly);

    QString tarText;
    for (int i = 0; i < linesFromFile.length(); i++)
        tarText = tarText + linesFromFile[i];
    openedFile.write(tarText.toUtf8());
    openedFile.close();
    QWidget* unlockingWidgets[] = { ui->generateFwBinFile, ui->burnFileToFpga, ui->generateBinApio,
                                    ui->generateBinArachne, ui->generateBinNextPnr, ui->fpgaCommandOutput };
    for (int i = 0; i < (sizeof(unlockingWidgets) / sizeof(QWidget*)); i++)
        unlockingWidgets[i]->setEnabled(true);
    QFileInfo currentFile(openedFile.fileName());
    ui->fpgaProjectPath->setText(currentFile.dir().absolutePath());
}

void i2c_keyboard_detection::on_generateFwBinFile_clicked()
{
    ui->fpgaCommandOutput->clear();
    QString program = ui->fpgaProjectPath->text();
    QStringList args;
#if 0
    QStringList sysVar;
    QFile sysVarFile("sys_var.txt");
    if (!sysVarFile.open(QFile::ReadOnly))
    {
        QMessageBox::warning(this, "PROCESS ERROR!", "CANT OPEN FILE SYS_VAR.TXT!");
        return;
    }
    while(1)
    {
        QString line = sysVarFile.readLine();
        if (line.length() == 0)
            break;
        sysVar.push_back(line);
    }
    sysVarFile.close();
    m_fpgaFwGenerator.setEnvironment(sysVar);
#endif
    QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
    //env.insert("TMPDIR", "C:\\MyApp\\temp"); // Добавляет переменную окружения
    //env.insert("PATH", env.value("Path") + ";C:\\Bin");
    //process.setProcessEnvironment(env);
    //process.start("myapp");
    //m_fpgaFwGenerator.setProcessEnvironment(env);
#if 1
    if (ui->generateBinApio->isChecked())
    {
        program = "apio";//program + "/apio build";
        //args << "build";
    }
    else if (ui->generateBinArachne)
        program = "make";//program + "make";
    else if (ui->generateBinNextPnr)
    {
        program = "make nextpnr";//program + "make";
        args << "nextpnr";
    }
    args.clear();
    //args.push_back("make");
    //args.push_back("nextpnr");

//    QString cmd = "yosys";
//    args << "-q" << "-p" << "synth_ice40" << "top.v" << "i2c_slave.v" << "matrix_kbd.v" << "ram.v" << "simple_filter.v" << "uart.v" << "descriptors.v";

//    QString cmd = "arachne-pnr";
//    args << "-d" << "1k" << "-P" << "tq144" << "-p" << "inouts.pcf" << "i2c_kbd_alt.blif" << "-o" << "i2c_kbd_alt.asc";

//    QString cmd = "yosys";
//    args << "-q" << "-p" << "synth_ice40 -json i2_kbd_alt.json" << "top.v" << "i2c_slave.v" << "matrix_kbd.v" << "ram.v" << "simple_filter.v" << "uart.v" << "descriptors.v";

//    QString cmd = "nextpnr-ice40";
//    args << "--hx1k" << "--json" << "i2_kbd_alt.json" << "--pcf" << "inouts.pcf" << "--asc" << "i2c_kbd_alt.asc";

    QString cmd = "icebox_explain";
    args << "i2c_kbd_alt.asc > i2c_kbd_alt.ex";

    m_fpgaFwGenerator->setWorkingDirectory("/home/t-bird/i2c_keyboard/i2c_keyboard_ice40"/*ui->fpgaProjectPath->text()*/);
    m_fpgaFwGenerator->start(cmd, args);
    //QThread::msleep(500);
    //m_fpgaFwGenerator->write("ls");
    //m_fpgaFwGenerator->write(program.toLatin1()/*, args*/);
#endif
    //system("apio");
}

void i2c_keyboard_detection::processReadyRead()
{
    QString output = m_fpgaFwGenerator->readAll();
    ui->fpgaCommandOutput->append(output);
}

void i2c_keyboard_detection::processFinished(int result, QProcess::ExitStatus exitStatus)
{
    QMessageBox::about(this, "PROCESS FINISHED", QString("Result %1, exit status %2").arg(result).arg(exitStatus));
}

void i2c_keyboard_detection::processError(QProcess::ProcessError error)
{
    QMessageBox::about(this, "PROCESS ERROR", QString("Error %1").arg(error));
}

void i2c_keyboard_detection::on_saveMatrixFile_clicked()
{
    QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"),
                                                    QString("matrix_%1x%2_%3.txt").arg(m_generatedColumns.length()).arg(m_generatedRows.length()).arg(m_generatedNotEmptyElements), tr("*.txt"));
    if (fileName == "")
        return;
    QFile savedFile(fileName);
    if (!savedFile.open(QFile::WriteOnly))
    {
        QMessageBox::warning(this, "CANNOT SAVE FILE", "Can't save file with matrixes");
        return;
    }
    QString matrixText = "Normal matrix:\n\n";
    for (int i = 0; i < m_generatedColumns.length(); i++)
        matrixText = matrixText + "\t" + m_generatedColumns[i];
    for (int j = 0; j < m_generatedRows.length(); j++)
    {
        matrixText = matrixText + "\n";
        matrixText = matrixText + m_generatedRows[j];
        for (int i = 0; i < m_generatedColumns.length(); i++)
            matrixText = matrixText + "\t" + m_generatedMatrix[j][i];
    }
    matrixText = matrixText + "\n\n\nFn matrix:\n\n";
    for (int i = 0; i < m_generatedColumns.length(); i++)
        matrixText = matrixText + "\t" + m_generatedColumns[i];
    for (int j = 0; j < m_generatedRows.length(); j++)
    {
        matrixText = matrixText + "\n";
        matrixText = matrixText + m_generatedRows[j];
        for (int i = 0; i < m_generatedColumns.length(); i++)
            matrixText = matrixText + "\t" + m_generatedFnMatrix[j][i];
    }
    savedFile.write(matrixText.toUtf8());
    savedFile.close();
}

void i2c_keyboard_detection::on_openMatrixFile_clicked()
{
    QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"), "matrix.txt", tr("*.txt"));
    if (fileName == "")
        return;
    QFile openedFile(fileName);
    if (!openedFile.open(QFile::ReadOnly))
    {
        QMessageBox::warning(this, "CANNOT OPEN FILE", "Can't open matrix file");
        return;
    }
    QVector<QString> textLines;
    QVector<QStringList> textElements;
    while(1)
    {
        QString currentStr = QString(openedFile.readLine());
        if (currentStr.length() == 0)
            break;
        if (currentStr[currentStr.length() - 1] == "\n")
            currentStr.resize(currentStr.length() - 1);
        QStringList currentStrList = currentStr.split("\t");
        textLines.push_back(currentStr);
        textElements.push_back(currentStrList);
    }
    openedFile.close();
    int rowNum = (textLines.length() - 8) / 2;
    if (textLines.length() < 10)
    {
        QMessageBox::warning(this, "UNCORRECT FILE OPENED", "This file not contain correct matrixes!");
        return;
    }
    QStringList columns = textLines[2].split("\t");
    if ((textLines[0] != "Normal matrix:") || (textLines[5 + rowNum] != "Fn matrix:") || (textLines[2] != textLines[7 + rowNum]))
    {
        QMessageBox::warning(this, "UNCORRECT FILE OPENED", "This file not contain correct matrixes!");
        return;
    }
    for (int i = 3; i < textElements.length(); i++)
    {
        if ((i > (2+rowNum)) && (i < (7+rowNum)))
            continue;
        if (textElements[i].length() != textElements[3].length())
        {
            QMessageBox::warning(this, "UNCORRECT FILE OPENED", "This file not contain correct matrixes!");
            return;
        }
    }
    m_generatedRows.resize(rowNum);
    m_generatedColumns.resize(columns.length() - 1);
    m_generatedMatrix.resize(rowNum);
    m_generatedFnMatrix.resize(rowNum);
    for (int i = 0; i < (columns.length() - 1); i++)
        m_generatedColumns[i] = columns[i + 1];
    for (int i = 0; i < rowNum; i++)
    {
        m_generatedRows[i] = textElements[i + 3][0];
        QVector<QString> matrixRow(m_generatedColumns.length());
        for (int j = 0; j < m_generatedColumns.length(); j++)
            matrixRow[j] = textElements[i + 3][j + 1];
        m_generatedMatrix[i] = matrixRow;
        for (int j = 0; j < m_generatedColumns.length(); j++)
            matrixRow[j] = textElements[i + rowNum + 8][j + 1];
        m_generatedFnMatrix[i] = matrixRow;
    }
    displayMatrix();
}

void i2c_keyboard_detection::on_addNewKey_clicked()
{
    QString addedElement = ui->newKeyName->text() + " (" + QString::number(ui->newKeyCode->value(),16) + ")";
    ui->keyList->addItem(addedElement);
}

void i2c_keyboard_detection::on_openQmkKbd_clicked()
{
    QString dirName = QFileDialog::getExistingDirectory(this, "Open QMK keyboard directory");
    if (dirName.size() == 0)
        return;
    QDir qmkDir(dirName);
    QDir keymapsDir(dirName + "/keymaps");

    if (!keymapsDir.exists())
    {
        QMessageBox::warning(this, "UNCORRECT DIR OPENED", "This dir not contains keymap folder!");
        return;
    }

    QFile layoutFile(dirName + "/" + qmkDir.dirName() + ".h");
    if (!layoutFile.exists())
    {
        QMessageBox::warning(this, "UNCORRECT DIR OPENED", "This dir not contains layout file (" + keymapsDir.dirName() + ".h)!");
        return;
    }

    QFile configFile(dirName + "/config.h");
    if (!configFile.exists())
    {
        QMessageBox::warning(this, "UNCORRECT DIR OPENED", "This dir not contains config file (" + keymapsDir.dirName() + ".h)!");
        return;
    }

    QFileInfoList qmkDirList = qmkDir.entryInfoList();
    QFileInfoList qmkKbdList = keymapsDir.entryInfoList();

    m_qmkRevisions.clear();
    m_qmkKeymaps.clear();

    m_qmkRevisions.push_back(qmkDir.absolutePath());

    foreach (QFileInfo info, qmkDirList)
    {
        if ((info.isDir()) && (info.baseName() != "keymaps") && (info.fileName() != ".") && (info.fileName() != ".."))
            m_qmkRevisions.push_back(info);
    }
    foreach (QFileInfo info, qmkKbdList) {
        if ((info.isDir()) && (info.fileName() != ".") && (info.fileName() != ".."))
            m_qmkKeymaps.push_back(info);
    }
    m_uiKbdList.setInfo(m_qmkRevisions, m_qmkKeymaps);
    m_uiKbdList.show();
    this->setEnabled(false);
}

void i2c_keyboard_detection::qmkKbdSelected(int rev, int keymap)
{
    this->setEnabled(true);
    if ((rev < 0) || (keymap < 0) || (rev >= m_qmkRevisions.size()) || (keymap >= m_qmkKeymaps.size()))
        return;

    QString mainLayoutFileName = m_qmkRevisions[0].absoluteFilePath() + "/" + m_qmkRevisions[0].fileName() + ".h";
    QFile mainLayoutFile(mainLayoutFileName);
    if (!mainLayoutFile.open(QFile::ReadOnly))
    {
        QMessageBox::warning(this, "UNCORRECT DIR OPENED", "This dir not contains layout file (" + mainLayoutFileName + ")!");
        return;
    }
    QString mainLayout = QString(mainLayoutFile.readAll());
    mainLayoutFile.close();

    QString revLayout = "";
    if (rev != 0)
    {
        QString revLayoutFileName = m_qmkRevisions[rev].absoluteFilePath() + "/" + m_qmkRevisions[rev].fileName() + ".h";
        QFile revLayoutFile(revLayoutFileName);
        if (revLayoutFile.open(QFile::ReadOnly))
        {
            revLayout = QString(revLayoutFile.readAll());
            revLayoutFile.close();
        }
        else
            QMessageBox::warning(this, "WARNING", "THIS REVISION NOT HAS OWN LAYOUT FILE");
    }

    QString mainConfigFileName = m_qmkRevisions[0].absoluteFilePath() + "/config.h";
    QFile mainConfigFile(mainConfigFileName);
    if (!mainConfigFile.open(QFile::ReadOnly))
    {
        QMessageBox::warning(this, "UNCORRECT DIR OPENED", "This dir not contains config file (" + mainConfigFileName + ")!");
        return;
    }
    QString mainConfig = QString(mainConfigFile.readAll());
    mainConfigFile.close();

    QString revConfig = "";
    if (rev != 0)
    {
        QString revConfigFileName = m_qmkRevisions[rev].absoluteFilePath() + "/config.h";
        QFile revConfigFile(revConfigFileName);
        if (revConfigFile.open(QFile::ReadOnly))
        {
            revConfig = QString(revConfigFile.readAll());
            revConfigFile.close();
        }
        else
            QMessageBox::warning(this, "WARNING", "THIS REVISION NOT HAS OWN CONFIG FILE");
    }

    QString keymapConfig = "";
    QString keymapConfigFileName = m_qmkKeymaps[keymap].absoluteFilePath() + "/config.h";
    QFile keymapConfigFile(keymapConfigFileName);
    if (keymapConfigFile.open(QFile::ReadOnly))
    {
        keymapConfig = QString(keymapConfigFile.readAll());
        keymapConfigFile.close();
    }
    else
        QMessageBox::warning(this, "WARNING", "THIS KEYMAP NOT HAS OWN CONFIG FILE");

    QString keymapFileName = m_qmkKeymaps[keymap].absoluteFilePath() + "/keymap.c";
    QFile keymapFile(keymapFileName);
    if (!keymapFile.open(QFile::ReadOnly))
    {
        QMessageBox::warning(this, "UNCORRECT DIR OPENED", "This dir not contains keymap file (" + keymapFileName + ")!");
        return;
    }
    QString keymapSrc = QString(keymapFile.readAll());
    keymapFile.close();

    QString keycodeFileNameEnd = "/tmk_core/common/keycode.h";
    QString keycodeFileNameBegin = m_qmkRevisions[0].absoluteFilePath() + "/../..";
    for (int i = 0; i < 3; i++)
    {
        if (QFile(keycodeFileNameBegin + keycodeFileNameEnd).exists())
            break;
        keycodeFileNameBegin += "/..";
    }

    QFile keycodeFile(keycodeFileNameBegin + keycodeFileNameEnd);
    if (!keycodeFile.open(QFile::ReadOnly))
    {
        QMessageBox::warning(this, "UNCORRECT DIR OPENED", "This dir not contains keycodes file!");
        return;
    }
    QString keyCodes = QString(keycodeFile.readAll());
    keycodeFile.close();

    QString error = m_qmkParser.parseFiles(mainLayout, revLayout, mainConfig, revConfig, keymapConfig, keymapSrc, keyCodes, true);

    if (!error.isEmpty())
    {
        QMessageBox::warning(this, "PARSING ERROR", error);
        return;
    }

    m_generatedColumns = m_qmkParser.getColumns();
    m_generatedRows = m_qmkParser.getRows();
    m_diodeDirection = m_qmkParser.getDiodeDirection();
    QVector<QVector<int>> matrix = m_qmkParser.getMatrix();
    QVector<QVector<int>> fnMatrix = m_qmkParser.getFnMatrix();

    QVector<QString> uiKeyNames;
    QVector<int> uiKeyCodes;

    for (int i = 0; i < ui->keyList->count(); i++)
    {
        QString keyDesc = ui->keyList->item(i)->text();
        uiKeyNames.push_back(keyDesc);
        keyDesc = keyDesc.mid(keyDesc.lastIndexOf("(") + 1, keyDesc.lastIndexOf(")") - keyDesc.lastIndexOf("(") - 1);
        uiKeyCodes.push_back(keyDesc.toInt(Q_NULLPTR, 16));
    }

    m_generatedMatrix.clear();
    m_generatedFnMatrix.clear();
    m_generatedMatrix.resize(m_generatedRows.size());
    m_generatedFnMatrix.resize(m_generatedRows.size());
    for (int i = 0; i < m_generatedRows.size(); i++)
    {
        m_generatedMatrix[i].resize(m_generatedColumns.size());
        m_generatedFnMatrix[i].resize(m_generatedColumns.size());
        for (int j = 0; j < m_generatedColumns.size(); j++)
        {
            for (int k = 0; k < uiKeyCodes.size(); k++)
            {
                if (matrix[i][j] == uiKeyCodes[k])
                    m_generatedMatrix[i][j] = uiKeyNames[k];
                if (fnMatrix[i][j] == uiKeyCodes[k])
                    m_generatedFnMatrix[i][j] = uiKeyNames[k];
            }
            if ((m_generatedMatrix[i][j].isEmpty()) && ((matrix[i][j] == 0) || (matrix[i][j] == 1)))
                m_generatedMatrix[i][j] = "(00)";
            if ((m_generatedFnMatrix[i][j].isEmpty()) && ((fnMatrix[i][j] == 0) || (fnMatrix[i][j] == 1)))
                m_generatedFnMatrix[i][j] = "(00)";
        }
    }

    displayMatrix();

    qDebug("222");
}
