#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "qfiledialog.h"
#include "QFile"
#include "QTextStream"
#include "QRegExp"
#include "QMessageBox"
#include "string"
#include "qstring.h"
#include "math.h"

#include <QMenuBar>
#include <QStatusBar>

QFile fileInput;
QString filenameInput;
QFile fileOutput;

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

    // add drop down menus
    QMenu*  aboutMenu = menuBar()->addMenu("&help");
    aboutMenu->addAction("about...", this, SLOT(showReadme()));
    // add status bar message
    statusBar()->showMessage("open G-Code file");
}


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


void MainWindow::showReadme()
{
    QMessageBox::information(NULL, "readme", "beta version, by felix@softtop.eu", "ok");
}


void MainWindow::on_pushButtonOpen_clicked()
{
    filenameInput = QFileDialog::getOpenFileName(
                this,
                tr("Open File"),
                "/home/felix/Desktop",
                "Gcode Files (*.ngc);; All files (*.*)"
                );

    fileInput.setFileName(filenameInput);
    if(!fileInput.open(QIODevice::ReadWrite | QIODevice::Text))
    {
        QMessageBox::information(NULL, "error", "cannot open file: "+filenameInput, "ok");
    }
    else
    {
        QTextStream inputStream(&fileInput);
        ui->textBrowserInput->setText(inputStream.readAll());
    }

    // add status bar message
    statusBar()->showMessage("set options and save G-Code file");
}


void MainWindow::on_pushButtonOptimize_clicked()
{
    double range = 360;

    filenameInput.replace(".ngc","");

    QString filenameOutput= QFileDialog::getSaveFileName(
                this,
                tr("SaveFile"),
                filenameInput+"_optimized_"+(QString("%1").arg(range, 0, 'g', 4))+".ngc",
                "Gcode Files (*.ngc)"
                );

    fileOutput.setFileName(filenameOutput);
    if(!fileOutput.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text))
    {
        QMessageBox::information(NULL, "error", "cannot open file: "+filenameOutput, "ok");
    }
    else
    {
        QTextStream outputStream(&fileOutput);
        QTextStream inputStream(&fileInput);
        inputStream.device()->reset();

        QString lineLookahead;
        QString line;
        QString capturedLookahead;
        QString captured;
        double newValue = 0;
        double newValueLookahead= 0;
        double previousValue = 0;
        double angleDifference = 0;
        double RotationMax = 0;
        double RotationMin = 0;
        bool insertLift = false;

        lineLookahead = "";
        QRegExp rxA ("A+ ?-?[0-9]+(.[0-9]*)?"); /*find A with number*/
        QRegExp rxN ("-?[0-9]+(.[0-9]*)?"); /*find number*/

        while (!inputStream.atEnd())
        {
            /* read line */
            line=inputStream.readLine();

            if (line.contains(rxA))
            {
                /* read lineLookahead*/
                lineLookahead=inputStream.readLine();

                if ((lineLookahead.contains(rxA)) && (lineLookahead.contains("I")))
                {
                    /* CASE 1: 'line IS 'A'-line AND lineLookahead IS 'I-line': process line AND lineLookahead */
                    capturedLookahead = rxA.cap();
                    capturedLookahead.contains(rxN);
                    QString capturedInCapturedLookahead = rxN.cap();
                    double valueLookahead = capturedInCapturedLookahead.toDouble();

                    line.contains(rxA);
                    captured = rxA.cap();
                    captured.contains(rxN);
                    QString capturedInCaptured = rxN.cap();
                    double value = capturedInCaptured.toDouble();

                    /* calculate difference */
                    angleDifference = valueLookahead - value;

                    /*adjust valueLookahead if > 360 degrees of lineLookahead*/
                    int turns = abs(valueLookahead/180);
                    double remainder = fabs(valueLookahead)-turns*180;
                    if (turns == 0)
                    {
                        newValueLookahead = valueLookahead;
                    }
                    else
                    {
                        if(turns&1) // ungerade 360, 1080, usw
                        {
                            if (value <= 0)
                            {
                                newValueLookahead = (180-remainder);
                            }
                            else
                            {
                                newValueLookahead = -(180-remainder);
                            }
                        }
                        else // gerade 720, 1440 usw.
                        {
                            if (value <0 )
                            {
                                newValueLookahead = -remainder;
                            }
                            else
                            {
                                newValueLookahead = remainder;
                            }
                        }
                    }
                    /* calculate newValue of line */
                    newValue =  newValueLookahead - angleDifference;

                    /* remember min/max values of rotation */
                    if (newValue > RotationMax) {RotationMax = newValue;}
                    if (newValue < RotationMin) {RotationMin = newValue;}
                    if (newValueLookahead > RotationMax) {RotationMax = newValueLookahead;}
                    if (newValueLookahead < RotationMin) {RotationMin = newValueLookahead;}

                    /* replace Value with new value in string */
                    QString newValueString = "A " + QString("%1").arg(newValue, 0, 'g', 13);
                    QString newValueStringLookahead = "A " + QString("%1").arg(newValueLookahead, 0, 'g', 13);
                    line.replace(captured, newValueString);
                    lineLookahead.replace(capturedLookahead, newValueStringLookahead);

                    /* Replace G01 by G00 */
                    QRegExp rx2 ("G01 A+ ?-?[0-9]+(.[0-9]*)?"); /*find G01 A with number line*/
                    if(line.contains(rx2)) /*rotation only*/
                    {
                        if (ui->checkBoxSetRotationSpeed->isChecked())
                        {
                            line.replace("G01", "G00");
                        }
                        /* Lift up before rotation*/
                        angleDifference = fabs(newValue - previousValue);
                        previousValue = newValueLookahead;

                        if ((ui->checkBoxInsertLift->isChecked()) && (angleDifference >= ui->spinBoxLiftAngle->value()))
                        {
                            insertLift = true;
                            QString lineLift= "G00 Z " + QString::number(ui->spinBoxLift->value());
                            outputStream << lineLift+"\n";
                        }
                    }
                    /* write prozessed line to file*/
                    outputStream << line+"\n";

                    /* Lift down after rotaion */
                    if (insertLift)
                    {
                        QString lineLift= "G00 Z " + QString("%1").arg(ui->doubleSpinBoxLift->value(), 0, 'g', 13);
                        outputStream << lineLift+"\n";
                        insertLift = false;
                    }
                    /* write prozessed lookaheadLine to file*/
                    outputStream << lineLookahead+"\n";
                }
                else
                {
                    /* CASE 2: line is 'A'-line but lineLookahead is NOT 'I-line': process line only */
                    /* adjust value if > 360 degrees of line */
                    line.contains(rxA);
                    captured = rxA.cap();
                    captured.contains(rxN);
                    QString capturedInCaptured = rxN.cap();
                    double value = capturedInCaptured.toDouble();
                    int turns = abs(value/180);
                    double remainder = fabs(value)-turns*180;
                    if (turns == 0)
                    {
                        newValue = value;
                    }
                    else
                    {
                        if(turns&1) // ungerade 360, 1080, usw
                        {
                            if (value <= 0)
                            {
                                newValue = (180-remainder);
                            }
                            else
                            {
                                newValue = -(180-remainder);
                            }
                        }
                        else // gerade 720, 1440 usw.
                        {
                            if (value <0 )
                            {
                                newValue = -remainder;
                            }
                            else
                            {
                                newValue = remainder;
                            }
                        }
                    }
                    /* replace Value with new value in string */
                    QString newValueString = "A " + QString("%1").arg(newValue, 0, 'g', 13);
                    line.replace(captured, newValueString);

                    /* remember min/max values of rotation */
                    if (newValue > RotationMax) {RotationMax = newValue;}
                    if (newValue < RotationMin) {RotationMin = newValue;}

                    /* Replace G01 by G00 */
                    QRegExp rx2 ("G01 A+ ?-?[0-9]+(.[0-9]*)?"); /*find G01 A with number line*/
                    if(line.contains(rx2)) /*rotation only*/
                    {
                        if (ui->checkBoxSetRotationSpeed->isChecked())
                        {
                            line.replace("G01", "G00");
                        }

                        /* Lift up before rotation*/
                        angleDifference = fabs(newValue - previousValue);
                        previousValue = newValue;
                        if ((ui->checkBoxInsertLift->isChecked()) && (angleDifference >= ui->spinBoxLiftAngle->value()))
                        {
                            insertLift = true;
                            QString lineLift= "G00 Z " + QString::number(ui->spinBoxLift->value());
                            outputStream << lineLift+"\n";
                        }
                    }
                    /*write prozessed line to file*/
                    outputStream << line+"\n";

                    /* Lift down after rotaion */
                    if (insertLift)
                    {
                        QString lineLift= "G00 Z " + QString("%1").arg(ui->doubleSpinBoxLift->value(), 0, 'g', 13);
                        outputStream << lineLift+"\n";
                        insertLift = false;
                    }
                    /* CASE 2a: */
                    /* lineLookahead IS 'A'-line: write prozessed lookaheadLine to file */
                    if ((lineLookahead.contains(rxA)))
                    {
                        capturedLookahead = rxA.cap();
                        capturedLookahead.contains(rxN);
                        QString capturedInCaptured = rxN.cap();
                        double valueLookahead = capturedInCaptured.toDouble();
                        int turnsLookahead = abs(valueLookahead/180);
                        double remainderLookahead = fabs(valueLookahead)-turnsLookahead*180;
                        if (turnsLookahead== 0)
                        {
                            newValueLookahead = valueLookahead;
                        }
                        else
                        {
                            if(turnsLookahead&1) // ungerade 360, 1080, usw
                            {
                                if (valueLookahead <= 0)
                                {
                                    newValueLookahead = (180-remainderLookahead);
                                }
                                else
                                {
                                    newValueLookahead = -(180-remainderLookahead);
                                }
                            }
                            else // gerade 720, 1440 usw.
                            {
                                if (valueLookahead <0 )
                                {
                                    newValueLookahead= -remainderLookahead;
                                }
                                else
                                {
                                    newValueLookahead = remainderLookahead;
                                }
                            }
                        }
                        /* replace Value with new value in string */
                        QString newValueStringLookahead = "A " + QString("%1").arg(newValueLookahead, 0, 'g', 13);
                        lineLookahead.replace(capturedLookahead, newValueStringLookahead);

                        /* remember min/max values of rotation */
                        if (newValueLookahead > RotationMax) {RotationMax = newValueLookahead;}
                        if (newValueLookahead < RotationMin) {RotationMin = newValueLookahead;}

                        /* Replace G01 by G00 */
                        QRegExp rx2 ("G01 A+ ?-?[0-9]+(.[0-9]*)?"); /*find G01 A with number line*/
                        if(lineLookahead.contains(rx2)) /*rotation only*/
                        {
                            if (ui->checkBoxSetRotationSpeed->isChecked())
                            {
                                lineLookahead.replace("G01", "G00");
                            }

                            /* Lift up before rotation*/
                            angleDifference = fabs(newValueLookahead - previousValue);
                            previousValue = newValueLookahead;
                            if ((ui->checkBoxInsertLift->isChecked()) && (angleDifference >= ui->spinBoxLiftAngle->value()))
                            {
                                insertLift = true;
                                QString lineLift= "G00 Z " + QString::number(ui->spinBoxLift->value());
                                outputStream << lineLift+"\n";
                            }
                        }
                        /*write prozessed line to file*/
                        outputStream << lineLookahead+"\n";

                        /* Lift down after rotaion */
                        if (insertLift)
                        {
                            QString lineLift= "G00 Z " + QString("%1").arg(ui->doubleSpinBoxLift->value(), 0, 'g', 13);
                            outputStream << lineLift+"\n";
                            insertLift = false;
                        }
                    }
                    else
                    {
                        /* CASE 2b: */
                        /* lineLookahead is NO 'A'-line: write NOT prozessed lookaheadLine to file */
                        outputStream << lineLookahead+"\n";
                    }
                }
            }
            else
            {
                /* CASE 3: line is NO 'A'-line */
                /* write NOT prozessed line to file */
                outputStream << line+"\n";
            }
        }
        /* close files */
        fileInput.close();
        fileOutput.close();

        /*  read and show content of optimized File */
        QFile fileShow;
        fileShow.setFileName(filenameOutput);
        fileShow.open(QIODevice::ReadOnly | QIODevice::Text);
        QTextStream showStream(&fileShow);
        ui->textBrowserOutput->setText(showStream.readAll());
        fileShow.close();

        /* display min/max values */
        ui->labelMinMax->setText("A-Axis Rotation: " + QString("%1").arg(RotationMin, 0, 'g', 4) + " to " + QString("%1").arg(RotationMax, 0, 'g', 4) + " degrees.");
    }
}
