// bytran -|- spectral calculations for portable devices using the HITRAN database
//
// Developed by Dzianis Pliutau (Denis Plutov) and Kostya Roslyakov
// www.bytran.org | bytran@bytran.org
// Copyright (c) 2016 Dzianis Pliutau
// Distributed under the MIT license
//
// The MIT License (MIT)
// Copyright (c) 2016 Dzianis Pliutau
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.



// Object to save the results of the calculations
#include "resultssaver.h"
#include <QDateTime>
#include <QLocale>
#include <QFile>
#include <QTextStream>
#include <QMessageBox>
#include <QFileInfo>
#include <QFileDialog>
#include <QApplication>
#include "yaxis.h"
#include <QDebug>

ResultsSaver::ResultsSaver(QObject *parent) : QObject(parent)
{
    // So that commas are not inserted into numbers
    loc.setNumberOptions(QLocale::OmitGroupSeparator);

    // Initializing the wavelength object
    wavelength = new Wavelength();

    // Creating the timer objects to monitor file writing to disk
    imageFileTimer = new QTimer(this);
    dataFileTimer = new QTimer(this);

    // Creating connections
    createActions();
}

void ResultsSaver::createActions()
{
    connect(dataFileTimer, SIGNAL(timeout()), this, SLOT(dataTimerUpdate()));
    connect(imageFileTimer, SIGNAL(timeout()), this, SLOT(imageTimerUpdate()));
}

void ResultsSaver::generateFilePathsAndTopString()
{
    // Forming a string to show the pathlength, temperature and the total pressure;
    this->topString = loc.toString(this->pathlength, 'g', 6) + " meters;   ";
    this->topString = this->topString + loc.toString(this->temp, 'g', 6)  + " Kelvin;   ";
    this->topString = this->topString + loc.toString(this->totalPress, 'g', 6) + " Atm;";

    // Forming the file name
    this->generatedFileName = generateFileName();

    // Forming the image save path
    this->imageFilePath = savePath + "/" + generatedFileName + ".png";

    // Forming the text file path
    this->textFilePath = savePath + "/" + generatedFileName + ".txt";

    // Forming the configuration file path
    this->configFilePath = savePath + "/" + generatedFileName + "-config.txt";

    // Waiting for the file to to be saved to disk
    imageFileTimer->start(500);
}

// Method to save the image file to disk once saving the data file has finished
void ResultsSaver::saveTextFileToDisk()
{
    // Waiting for the file to to be saved to disk
    dataFileTimer->start(500);

    if (this->data->getOptDepth()->getY()->count() > 3)
    {
        dataFileTimer->start(500);

        // Saving the configuration file to disk for sharing
        emit saveConfigToDocuments(this->configFilePath);

        // Saving the data file to disk
        savePushedDataToFile(this->textFilePath);
    }
}

// Methods to get and set the file save path
QString ResultsSaver::getSavePath() { return this->savePath; }
void ResultsSaver::setSavePath(QString newSavePath) { this->savePath = newSavePath; }

// Method to form the file name to save the data to
QString ResultsSaver::generateFileName()
{
    QDateTime dateTime;  // Date time object to get the current date and time
    QString dateStr;     // Date string
    QString timeStr;     // String with the current time
    QString dateTimeStr;   // String with the date and time parameters
    QString fileNameStr;   // String containing the complete file name


    dateStr = dateTime.currentDateTime().toString("MM_dd_yyyy");
    timeStr = dateTime.currentDateTime().time().toString("HH_mm_ss");
    dateTimeStr = dateStr + "-" + timeStr;
    fileNameStr = "bytran-" + dateTimeStr;
    return fileNameStr;
}

// Method to pass in an object with the calculation results
// 2 different variables are used, data and pushed data
void ResultsSaver::setData(OneLayerSpectra *slantLayer) { data = slantLayer; }
void ResultsSaver::addPushedData(QVector<double> data, QString title)
{
    this->pushedData.append(data);
    this->titles.append(title);
}
// Method to clear the pushed data
void ResultsSaver::clearPushedData()
{
    this->pushedData.clear();
    this->titles.clear();
}

// Method to get the wavelengths parameters
Wavelength *ResultsSaver::getWavelength() { return this->wavelength; }
void ResultsSaver::setWavelength(Wavelength *newWavelength) { *wavelength = *newWavelength; }

// Methods to get the total pressure, temperature, pathlength and partial pressures (to be saved in the output file)
double ResultsSaver::getTotalPress() { return this->totalPress; }
void ResultsSaver::setTotalPress(double newTotalPress) { this->totalPress = newTotalPress; }
double ResultsSaver::getTemperature() { return this->temp; }
void ResultsSaver::setTemperature(double newTemp) { this->temp = newTemp; }
double ResultsSaver::getPathlength() { return this->pathlength; }
void ResultsSaver::setPathlength(double newPathlength) { this->pathlength = newPathlength; }
double ResultsSaver::getPartPressAt(int index) { return this->partPress.at(index); }
void ResultsSaver::setPartPress(QVector<double> *newPartPress)
{
    int i;  // Loop variable

    this->partPress.resize(newPartPress->count());
    for (i = 0; i < partPress.count(); i++)
    {
        partPress[i] = (*newPartPress)[i];
    }
}
void ResultsSaver::setSelectedAndNames(QVector<HitranOneMol> *mol)
{
    int i;  // Loop variable

    this->selected.resize(mol->count());
    this->names.resize(mol->count());
    for (i = 0; i < mol->count(); i++)
    {
        selected[i] = (*mol)[i].getSelected();
        names[i] = (*mol)[i].getChemForm();
    }
}

// Method to form the data string to save to a text file
// This method seems to be saving what was passed in after the calculations (this is not from the plotting function)
// Maybe make changes to be able to use this later instead of the displayed data
void ResultsSaver::saveDataToFile(QString filePath)
{
    int i = 0;  // Loop variable
    int j = 0;  // Loop variable
    QString auxStr;  // Ausiliary string used for conversions
    QString totalStr;  // Total string containing all data to be output to an external file
    QFile dataFile;    // Qfile object


    // Saving the file to the harddrive
    dataFile.setFileName(filePath);
    if (dataFile.open(QIODevice::ReadWrite))
    {
        QTextStream ioStream(&dataFile);

        // Need to form the titles string to write out first
        totalStr = "Wavenumber, cm-1";
        totalStr = totalStr + "\t" + wavelength->getAxisTitle();
        for (i = 0; i < names.count(); i++)
        {
            totalStr = totalStr + "\t" + names[i];
        }
        totalStr = totalStr + "\n";
        ioStream << totalStr;
        for (j = 0; j < this->wavelength->getWns()->count(); j++)
        {
            totalStr = "";

            // Writing out wavenumber in cm-1
            auxStr = loc.toString(this->wavelength->getWnsAt(j), 'g', 8);
            totalStr = totalStr + auxStr;

            // Writing out wavelength in micron
            auxStr = loc.toString(this->wavelength->fromDefaultToCurrent(this->wavelength->getWnsAt(j)), 'g', 8);
            totalStr = totalStr + + "\t" + auxStr;

            for (i = 0; i < this->data->getMol()->count(); i++)
            {
                if (selected[i] == true)
                {
                    // Writing out the molecular component value for a given wavenumber
                    // Here need to save in current units
                    auxStr = loc.toString(this->data->getMolAt(i)->getOptDepth()->getYAt(j), 'g', 8);
                    totalStr = totalStr + "\t" + auxStr;
                }
            }

            // Forming the total string for a single line
            totalStr = totalStr + "\n";

            ioStream << totalStr;
            dataFile.flush();
        }

        dataFile.close();
    }
}

// Method to form the data string to save to a text file
void ResultsSaver::savePushedDataToFile(QString filePath)
{
    int i = 0;  // Loop variable
    int j = 0;  // Loop variable
    QString auxStr;   // Ausiliary string used for conversions
    QString totalStr; // Total string containing all data to be output to an external file
    QFile dataFile;   // QFile object


    // Saving the file to the harddrive
    dataFile.setFileName(filePath);
    if (dataFile.open(QIODevice::ReadWrite))
    {
        QTextStream ioStream(&dataFile);

        // Saving the titles string
        totalStr = titles[0];
        for (i = 1; i < titles.count(); i++)
        {
            totalStr = totalStr + "\t" + titles[i];
        }
        totalStr = totalStr + "\n";
        ioStream << totalStr;

        // Iterating through all wavenumbers to save the data
        for (j = 0; j < pushedData[0].count(); j++)
        {
            totalStr = loc.toString(pushedData[0][j], 'g', 8);
            for (i = 1; i < pushedData.count(); i++)
            {
                auxStr = loc.toString(pushedData[i][j], 'g', 8);
                totalStr = totalStr + "\t" + auxStr;
            }

            // Forming the total string for a single line
            totalStr = totalStr + "\n";

            ioStream << totalStr;
            dataFile.flush();
        }

        // Closing the file
        dataFile.close();
    }
}

// Method to get the delimited wavenumber grid (as a String)
QString ResultsSaver::getDelimitedWnGridString(QString delimiter, char format, int precision)
{
    int i = 0;  // Loop variable
    QString auxStr;    // Auxiliary string
    QString totalStr;  // Total string


    totalStr = loc.toString(this->wavelength->getWnsAt(i), format, precision);
    for (i = 1; i < wavelength->getWns()->count(); i++)
    {
        // Writing out wavenumber in cm-1
        auxStr = loc.toString(this->wavelength->getWnsAt(i), format, precision);
        totalStr = totalStr + delimiter + auxStr;
    }

    return totalStr;
}

// Method to get the delimited spectral data (as a String) based on the partial column title name
QString ResultsSaver::getDelimitedSpectrumString(QString colSubStr, QString delimiter, char format, int precision)
{
    int i;  // Loop variable
    QString auxStr;   // Auxiliary string
    int specIndex;    // Index of the spectrum to return
    QString totalStr = "";  // Total string


    qDebug() << "to search: " << colSubStr;

    specIndex = -1;
    for (i = 0; i < titles.count(); i++)
    {
        qDebug() << "Titles: " << titles[i];
        if ((titles[i]).startsWith(colSubStr, Qt::CaseInsensitive)) { specIndex = i; }
    }

    qDebug() << "Spec index: " << specIndex;

    if (specIndex != -1)
    {
        totalStr = loc.toString(pushedData[specIndex][0], format, precision);
        for (i = 1; i < (pushedData[specIndex]).count(); i++)
        {
            auxStr = loc.toString(pushedData[specIndex][i], format, precision);
            totalStr = totalStr + delimiter + auxStr;
        }
    }

    return totalStr;
}

// Method to get the generated filename
QString ResultsSaver::getTextFilePath() { return this->textFilePath; }
QString ResultsSaver::getImageFilePath() { return this->imageFilePath; }
QString ResultsSaver::getConfigFilePath() { return this->configFilePath; }
QString ResultsSaver::getGenearatedFileName() { return this->generatedFileName; }

// Method to get the top string
QString ResultsSaver::getTopString() { return this->topString; }

// Slot is called every time the timer time reaches its maximum
void ResultsSaver::dataTimerUpdate()
{
    QFileInfo info;
    QFileInfo configInfo;  // Information about the configuration file

    QApplication::processEvents();
    info.setFile(this->textFilePath);
    configInfo.setFile(this->configFilePath);
    if (info.exists() && configInfo.exists())
    {
        dataFileTimer->stop();
        emit savingFilesToDiskComplete();
    }
}

// Slot is called every time the timer time reaches its maximum
void ResultsSaver::imageTimerUpdate()
{
    QFileInfo info;

    QApplication::processEvents();
    info.setFile(this->imageFilePath);
    if (info.exists())
    {
        imageFileTimer->stop();
        saveTextFileToDisk();
    }
}
