// bytran -|- spectral calculations for portable devices using the HITRAN database
//
// Developed by Dzianis Pliutau (Denis Plutov) and Kostya Roslyakov
// www.bytran.org | contact@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.



// This class is used to download data from the HITRAN online interface. The data includes the
// molparam.txt file, the files with the total internal partition sums and the HITRAN database file.
// The module also includes a subclass to access HITRAN database in a standard format of a disk.
#include "hitranonlinedownloader.h"
#include "auxiliary.h"
#include <QMessageBox>
#include <QFile>
#include <QTextStream>
#include <QIODevice>
#include <QFileInfo>
#include <QApplication>
#include <QDir>

#include <QDebug>
#include <QDateTime>

HitranOnlineDownloader::HitranOnlineDownloader(QObject *parent) :
    QObject(parent)
{
    // Setting the default internal parameters (should be backed up by reading from files as well in case the format changes)
    // String to contain additional characters for line-by-line query with authentication
    lblSubQueryStrWithAuthen = "/lbl/5?output_format_id=1&iso_ids_list=";
    // String to contain additional characters for line-by-line query without authentication
    lblSubQueryStrWithoutAuthen = "/lbl/api?iso_ids_list=";

    // Initializng an object used to authenticate with the remote server
    login = new ServerLogin();

    // Initializing the object to request data from HITRAN
    requestData = new HitranRequestData();

    // Initializing the download regions finder object
    downloadRegionsFinder = new NormalizedDownloadRegionsFinder();

    // Object for saving of data to the HITRAN file
    hitranio = new HitranInputOutput();

    // File downloader object to download the TIPS files and the file with the molecular information
    fileDownloader = new FileDownloader();

    // Initializing the TIPS downloader
    tipsDownloader = new TipsFilesDownloader();

    // Setting the value of the cancel calculation parameter
    this->cancelCalculation = false;

    // Creating connections
    createActions();
}

// Method is used to set a variable used to cancel the calculation
void HitranOnlineDownloader::setCancelCalculation(bool newCancelCalculation)
{
    this->cancelCalculation = newCancelCalculation;
    QApplication::processEvents();
}

// Method to get the number of records to be read in from the HITRAN file
quint64 HitranOnlineDownloader::getTotalNumRecsToProcess()
{
    int state;  // Boolean variable state


    state = this->getCompleteHitranDbFileExists();
    if (this->hitranFileUsage == 1)
    {
        // If the cumulative HITRAN file exists, use it
        if ((state == 10) || (state == 11) || (state == 12)) {
            return (this->endWnByte - this->startWnByte) / 162;
        }
        // If no cumulative HITRAN file exists, then using the segmented HITRAN files
        else if ((state == 1) || (state == 2)) {
            return this->hitranio->getNumSegmentedRecsToProcess(this->startWnFileIndex, this->endWnFileIndex, this->startWnByte, this->endWnByte);
        }
    }
    else if (this->hitranFileUsage == 0) {
        return this->hitranio->getNumSegmentedRecsToProcess(this->startWnFileIndex, this->endWnFileIndex, this->startWnByte, this->endWnByte);
    }
    // In this case of direct downloads returning the number of records in a sub-segment
    else if (this->hitranFileUsage == 2) {
        return this->hitranio->getReadInData()->count();
    }

    // Returning 0 records if no other option above is valid
    return 0;
}

// Method to set the database folders and files
void HitranOnlineDownloader::setFolders()
{
     hitranio->setHitranDbFilePath(this->cumulativeHitranPath + "/" + this->cumulativeHitranFile);
}

// Method to create actions
void HitranOnlineDownloader::createActions()
{
    // If authentication is requred, then a responce from the server performing authentication needs to be received
    connect(this->login, SIGNAL(authenticationSuccessful()), this, SLOT(authenticationComplete()));

    // After the file has been generated on the remote HITRAN online server, it needs to be downloaded
    // so the path to the newly generated webpage with the downloaded file is passed over to the download routine
    connect(this->requestData, SIGNAL(hitranDataReturned(QByteArray*, bool)), this, SLOT(processServerReturnedData(QByteArray*, bool)));

    // Linking the download object to the corresponding slot
    connect(this->fileDownloader, SIGNAL(dataDownloaded(QByteArray*,bool)), this, SLOT(molparamDownloaded(QByteArray*,bool)));

    // Downloading the TIPS files
    connect(this->tipsDownloader, SIGNAL(notify(QString, double, bool, bool)), this, SIGNAL(notify(QString, double, bool, bool)));
    connect(this->tipsDownloader, SIGNAL(tipsFilesDownloaded(bool)), this, SLOT(tipsFilesDownloaded(bool)));

    // Connection to get the list of download regions
    connect(this->downloadRegionsFinder, SIGNAL(downloadRegionsFormed()), this, SLOT(sendInitialDownloadRequest()));

    // Connection to be able to send notifications from the HITRAN data request object
    connect(this->requestData, SIGNAL(notify(QString,double,bool,bool)), this, SIGNAL(notify(QString,double,bool,bool)));
}

void HitranOnlineDownloader::downloadMolparamTxt()
{
    QString fileName;  // File name extracted from the download path
    int index;  // Index within the string to start the download at

    index = this->molparamDownloadPath.lastIndexOf("/");
    fileName = this->molparamDownloadPath.mid(index);
    QFileInfo checkFile(this->molparamFolder + fileName);  //  "/" is not added because it is already there

    if (!checkFile.exists())
    {
        fileDownloader->setDownloadPath(this->molparamDownloadPath);
        fileDownloader->setSavePath(this->molparamFolder + fileName);
        fileDownloader->setSaveToHarddrive(true);
        fileDownloader->download();
    }
    else
    {
        // Downloading TIPS files after the molparam files have been downloaded
        emit molparamTxtDownloadComplete();
    }
}

// Methods to get and set the paths to the folders where HITRAN, molparam.txt, and the TIPS files are stored
QString HitranOnlineDownloader::getMolparamFolder() { return this->molparamFolder; }
void HitranOnlineDownloader::setMolparamFolder(QString newMolparamFolder) { this->molparamFolder = newMolparamFolder; }

// Getting and setting the minimum and maximum download wavenumbers
double HitranOnlineDownloader::getStartWn() { return this->startWn; }
void HitranOnlineDownloader::setStartWn(double newStartWn) { this->startWn = newStartWn; }
double HitranOnlineDownloader::getEndWn() { return this->endWn; }
void HitranOnlineDownloader::setEndWn(double newEndWn) { this->endWn = newEndWn; }

// Methods to get and set the HITRAN segmented files download directory
// Getting and setting the HITRAN complete file path (where the complete HITRAN database file is to be stored)
QString HitranOnlineDownloader::getCumulativeHitranPath() { return this->cumulativeHitranPath; }
void HitranOnlineDownloader::setCumulativeHitranPath(QString newCumulativeHitranPath) { this->cumulativeHitranPath = newCumulativeHitranPath; }
// Name of the file to save the segmented downloaded data to (files downloaded in segments)
QString HitranOnlineDownloader::getSegmentedHitranPath() { return this->hitranio->getSegmentedHitranFolder(); }
void HitranOnlineDownloader::setSegmentedHitranPath(QString newSegmentedHitranPath) { this->hitranio->setSegmentedHitranFolder(newSegmentedHitranPath); }
// Name of the file to save the downloaded complete HITRAN file to
QString HitranOnlineDownloader::getCumulativeHitranFile() { return this->cumulativeHitranFile; }
void HitranOnlineDownloader::setCumulativeHitranFile(QString newCumulativeHitranFile) { this->cumulativeHitranFile = newCumulativeHitranFile; }
// Name of the file containing the list of continuous spectral regions which the HITRAN data has already been downloaded for
QString HitranOnlineDownloader::getDownloadHistoryFile() { return this->downloadHistoryFile; }
void HitranOnlineDownloader::setDownloadHistoryFile(QString newDownloadHistoryFile) { this->downloadHistoryFile = newDownloadHistoryFile; }

// Methods to get and set the host name to download the HITRAN database
QString HitranOnlineDownloader::getHitranHostName() { return this->hostName; }
void HitranOnlineDownloader::setHitranHostName(QString newHostName) { this->hostName = newHostName; }

// Method to set the HITRAN hostname, email and password
QString HitranOnlineDownloader::getHitranLoginHost() { return login->getHostName(); }
void HitranOnlineDownloader::setHitranLoginHost(QString hostName) { login->setHostName(hostName); }

QString HitranOnlineDownloader::getHitranEmail() { return login->getEmail(); }
void HitranOnlineDownloader::setHitranEmail(QString newEmail) { login->setEmail(newEmail); }

QString HitranOnlineDownloader::getHitranPassword() { return login->getPassword(); }
void HitranOnlineDownloader::setHitranPassword(QString newPassword) { login->setPassword(newPassword); }

// Setting the variable indicating if authentication is to be used in remote data access
bool HitranOnlineDownloader::getUserAuthen() { return this->userAuthen; }
void HitranOnlineDownloader::setUserAuthen(bool newUserAuthen) { this->userAuthen = newUserAuthen; }

// Paths to the online download folders with the molparam and the TIPS files
QString HitranOnlineDownloader::getMolparamDownloadPath() { return this->molparamDownloadPath; }
void HitranOnlineDownloader::setMolparamDownloadPath(QString newMolparamDownloadPath) { this->molparamDownloadPath = newMolparamDownloadPath; }

// Getting and setting the variable indicating the type of HITRAN database usage to implement
int HitranOnlineDownloader::getHitranFileUsage() { return this->hitranFileUsage; }
void HitranOnlineDownloader::setHitranFileUsage(int newHitranFileUsage) { this->hitranFileUsage = newHitranFileUsage; setFolders(); }

// Number of the start record in the cumulative HITRAN data file
int HitranOnlineDownloader::getStartWnByte() { return this->startWnByte; }
void HitranOnlineDownloader::setStartWnByte(int newStartWnByte) { this->startWnByte = newStartWnByte; }
// Number of the end record in the cumulative HITRAN data file
int HitranOnlineDownloader::getEndWnByte() { return this->endWnByte; }
void HitranOnlineDownloader::setEndWnByte(int newEndWnByte) { this->endWnByte = newEndWnByte; }
// Number of the starting subrange record
int HitranOnlineDownloader::getStartSubrangeWnByte() { return this->startSubrangeWnByte; }
void HitranOnlineDownloader::setStartSubrangeWnByte(int newStartSubrangeWnByte) { this->startSubrangeWnByte = newStartSubrangeWnByte; }
// Number of the ending subrange record
int HitranOnlineDownloader::getEndSubrangeWnByte() { return this->endSubrangeWnByte; }
void HitranOnlineDownloader::setEndSubrangeWnByte(int newEndSubrangeWnByte) { this->endSubrangeWnByte = newEndSubrangeWnByte; }
// Number of records in the subrange to read in one reading
quint32 HitranOnlineDownloader::getSubrangeRecordsStep() { return this->subrangeRecordsStep; }
void HitranOnlineDownloader::setSubrangeRecordsStep(quint32 newSubrangeRecordsStep) { this->subrangeRecordsStep = newSubrangeRecordsStep; }

// Method to get data to be used in calculations
QVector<HitranOneRec> *HitranOnlineDownloader::getHitranRecs() { return this->hitranio->getReadInData(); }

// Method to get logon to a remote host
ServerLogin *HitranOnlineDownloader::getLoginObj() { return this->login; }

// Method to get the TIPS object
TipsFilesDownloader *HitranOnlineDownloader::getTipsDownloader() { return this->tipsDownloader; }

// Method to set a reference to the molecules array
void HitranOnlineDownloader::setMolArray(QVector<HitranOneMol> *newMol) { this->mol = newMol; }

// Method to determine if the complete HITRAN database file is present on the user's device
int HitranOnlineDownloader::getCompleteHitranDbFileExists()
{
    // Cumulative HITRAN exists but the segmented HITRAN does not
    // 10 -> Cumulative HITRAN exists but the segmented HITRAN does not
    // 11 -> Cumulative HITRAN exists and complete segmented HITRAN exists
    // 12 -> Cumulative HITRAN exists and parts of the segmented HITRAN exist
    //  0 -> Neither the cumulative HITRAN nor the segmented HITRAN exists
    //  1 -> Cumulative HITRAN does not exists, the complete segmented HITRAN exists
    //  2 -> Cumulative HITRAN does not exist, however parts of the segmented HITRAN exist

    QLocale loc;  // Locale object to convert strings to numbers
    bool cumulativeHitranExists;  // Variable is set to true is the cumulative HITRAN file exists
    int segmentedHitranExists;    // Variable is set to true is segmented HITRAN files comering the entire HITRAN spectral range exist (0 - does not exist, 1 - exists and complete, 2 - exists but not all files are present)
    QDir dir(hitranio->getSegmentedHitranFolder());  // Directory object with the HITRAN segmented files
    int index;  // Index variable for the current file
    QStringList files;  // Files found in a directory
    QStringList fileNameSplitList;  // File name split into parts
    int i;  // Loop variable
    int j;  // Loop variable
    QFileInfo checkFile;  // Object to check the existance of a file
    bool foundMatch;      // Variable indicating that a match has been found


    // Checking if the cumulative HITRAN file exists
    if (checkFile.exists(this->cumulativeHitranPath + "/" + this->cumulativeHitranFile)) { cumulativeHitranExists = true;  }
                                                                                    else { cumulativeHitranExists =  false; }
    // Getting the list of files already in the folder
    files = dir.entryList(QDir::NoDotAndDotDot | QDir::Files);

    // Determining if segmented files are in the system
    if (files.count() > 0)
    {
        // Checking if files are sequential and start from 0 index
        segmentedHitranExists =  1;
        for (j = 0; j < files.count(); j++)
        {
            foundMatch = false;
            for (i = 0; i < files.count(); i++)
            {
                fileNameSplitList = files[i].split("-", QString::SkipEmptyParts);
                index = loc.toInt(fileNameSplitList[0]);
                if (j == index) { foundMatch = true; }
            }
            if (foundMatch == false) { segmentedHitranExists = 2; }
        }

        // Making sure all files are present (with the exception of a few trailing files)
        if ((files.count() >= 132) && (segmentedHitranExists ==  1))
        {
            segmentedHitranExists =  1;  // Files are sequential and all files are present
        }
        else
        {
            segmentedHitranExists = 2;   // Files are sequential but not all files are present (trailing files are missing)
        }
    }
    else  // No segmented files exist at all
    {
        segmentedHitranExists =  0;
    }

    // Setting the final variable indicating which files exist
    if (cumulativeHitranExists ==  true)
    {
        if (segmentedHitranExists == 0){
            return 10;  // Cumulative HITRAN exists but the segmented HITRAN does not
        }
        else if (segmentedHitranExists == 1) {
            return 11;  // Cumulative HITRAN exists and complete segmented HITRAN exists
        }
        else if (segmentedHitranExists == 2) {
            return 12;  // Cumulative HITRAN exists and parts of the segmented HITRAN exist
        }
    }
    else
    {
        if (segmentedHitranExists == 0) {
            return 0;  // Neither the cumulative HITRAN nor the segmented HITRAN exists
        }
        else if (segmentedHitranExists == 1) {
            return 1;  // Cumulative HITRAN does not exists, the complete segmented HITRAN exists
        }
        else if (segmentedHitranExists == 2) {
            return 2;  // Cumulative HITRAN does not exist, however parts of the segmented HITRAN exist
        }
    }

    // If no other option is valied then returning 0 (No databases are present)
    return 0;
}

// Cumulative HITRAN database download path (for the complete HITRAN database to be downloaded)
QString HitranOnlineDownloader::getCumulativeHitranDownloadPath() { return this->cumulativeHitranDownloadPath; }
void HitranOnlineDownloader::setCumulativeHitranDownloadPath(QString newCumulativeHitranDownloadPath) { this->cumulativeHitranDownloadPath = newCumulativeHitranDownloadPath; }

// Method to initiate the HITRAN download quiery
QString HitranOnlineDownloader::formQuery(double fromWn, double toWn)
{
    QString queryStr;  // String to contain the quiery to be sent to the HITRAN database
    int i;  // Loop variable
    int j;  // Loop variable
    QVector<int> globalIdArray;


    // Setting the beginning structure of the query
    if (this->userAuthen == true) { queryStr = this->hostName + this->lblSubQueryStrWithAuthen; }
                             else { queryStr = this->hostName + this->lblSubQueryStrWithoutAuthen; }

    // Clearing the global id array
    globalIdArray.clear();

    // Indicating the molecules chosen for download (the download will be for all molecules to be included for later offline access)
    for (i = 0; i < mol->count(); i++)
    {
        for (j = 0; j < (*mol)[i].getIso()->count(); j++)
        {
            if ((((*mol)[i]).getIsoAt(j)->getGlobalId() > 0) && (((*mol)[i]).getIsoAt(j)->getGlobalId() != 120))  // For a couple of isotopes the global ID is not available in which case it is set to be -1 in bytran, can't pass -1 to HITRANonline in the request, also skipping CO2 isotope with molecule index 11
            {
                globalIdArray.append(((*mol)[i]).getIsoAt(j)->getGlobalId());
            }
        }
    }

    // Sorting the global id array values in the ascending order
    Auxiliary::selectionSort(&globalIdArray);

    // Forming the isotopes list string with the quiery to send to the remote server
    for (j = 0; j < globalIdArray.count(); j++)
    {
        queryStr = queryStr + QString::number(globalIdArray[j]) + "%2C" ;
    }

    // Setting the query string
    queryStr.remove(queryStr.length() - 3, 3);  // Removing one trailing comma symbol
    // queryStr = queryStr + "&vib_bands=";        // This has something to do with the vibrational bands I believe but the value is not set
    queryStr = queryStr + "&numin=" + QString::number(fromWn);
    queryStr = queryStr + "&numax=" + QString::number(toWn);

    // Maybe need to update this here for the new format search (not par), then the amount of donwloaded data would be smaller (as an option to par).

    // Returning the string with the complete query to be sent to the remote server
    return queryStr;
}

// Slot is called after the molparam.txt file has been downloaded
void HitranOnlineDownloader::molparamDownloaded(QByteArray *molparamData, bool success)
{
    if (success == false)
    {
        emit openOkDialog("Error downloading the molparam.txt\nfile from HITRAN online (hitran.org)");
    }

    // Emiting a signal indicating that the download of the molparam.txt file is complete
    emit molparamTxtDownloadComplete();
}

// Method to download files with the total internal partition sums
void HitranOnlineDownloader::downloadTipsFiles()
{
    tipsDownloader->downloadTipsFiles();
}
// Slot is called once the TIPS files have finished downloading
void HitranOnlineDownloader::tipsFilesDownloaded(bool success)
{
    if (success == false)
    {
        emit openOkDialog("Error downloading TIPS files\nfrom HITRANonline (hitran.org)");
    }
    else
    {
        // Emitting a signal indicating that auxiliary data download is complete
        emit tipsDownloadComplete();
    }
}

void HitranOnlineDownloader::getHitranData(bool newUseAuthen)
{
    // Setting up the parameter indicating that authentication needs to be used
    this->userAuthen = newUseAuthen;    

    // Downloading the missing HITRAN data
    getHitranData();
}
// Method to download HITRAN
void HitranOnlineDownloader::getHitranData()
{
    getHitranData(this->userAuthen, this->startWn, this->endWn);
}

// Method to download HITRAN data with the specified start and end wavenumbers
void HitranOnlineDownloader::getHitranData(bool newUseAuthen, double fromWn, double toWn)
{
    int state;  // State of the boolean variable indicating if the segmented or complete HITRAN exists


    // Setting up the parameter indicating that authentication needs to be used
    this->userAuthen = newUseAuthen;

    // Getting the presence of the HITRAN database on the target device
    // 10 -> Cumulative HITRAN exists but the segmented HITRAN does not
    // 11 -> Cumulative HITRAN exists and complete segmented HITRAN exists
    // 12 -> Cumulative HITRAN exists and parts of the segmented HITRAN exist
    // 00 -> Neither the cumulative HITRAN nor the segmented HITRAN exists
    // 01 -> Cumulative HITRAN does not exists, the complete segmented HITRAN exists
    // 02 -> Cumulative HITRAN does not exist, however parts of the segmented HITRAN exist
    state = this->getCompleteHitranDbFileExists();

    // Gets stuck here someties under iOS when compiled with Qt later than 5.6.1-1 -> need to find out why and recompile with the image save inabled, also check the dialogs on newer devices (need to make sure the dialogs are native, though not necessary)

    if ( (this->hitranFileUsage == 1) && ((state == 1) || (state == 10) || (state == 11) || (state == 12)) )
    {
        // Emitting a signal indicating that the download of the HITRAN database is complete
        emit hitranDownloadComplete();
    }
    else if (this->hitranFileUsage == 2)
    {
        // Emitting a signal indicating that the download of the HITRAN database is complete
        emit hitranDownloadComplete();
    }
    else
    {
        // Checking data offline and online for HITRAN
        emit notify("Searching HITRAN parts\navailable on your device.", 0, true, true);

        // Determining the spectral regions for the data to be downloaded to fill in the missing information not available on the local harddrive
        downloadRegionsFinder->setSegmentedHitranPath(this->hitranio->getSegmentedHitranFolder());
        downloadRegionsFinder->setStartWn(fromWn);
        downloadRegionsFinder->setEndWn(toWn);
        downloadRegionsFinder->findDownloadRegions();

        // The "find download regions finder" sends a signal to the "send initial download request"
        // Had to be implemented like that because the speed of file access and forming a list to download is slow
        // and the program switches over to the next task without waiting for completion.
    }
}

// Method to send an initial download request
void HitranOnlineDownloader::sendInitialDownloadRequest()
{
    QString auxStr;  // Auxiliary string


    // Variable used to indicate if the calculation is to be canceled (setting to false by default)
    this->cancelCalculation = false;

    // Setting the initial number of the downloaded HITRAN regions
    numHitranRegionsDownloaded = 0;

    // Only carry out further calculations if data download is required
    if (downloadRegionsFinder->getDownloadEndWn()->count() > 0)
    {
        // Authenticating with the remote server if the authentication is required (the download sequence is different in this case)
        if (this->userAuthen == true)
        {
            // Authenticating with a remote server if required
            login->authenticate();
        }
        else
        {
            // Displaying a notification to the user
            auxStr = "Downloading HITRAN, file " + QString::number(1) + " of " + QString::number(downloadRegionsFinder->getDownloadEndWn()->count()) + "\nFile spectral range: " + QString::number(downloadRegionsFinder->getDownloadStartWnValueAtIndex(numHitranRegionsDownloaded)) + " - " + QString::number(downloadRegionsFinder->getDownloadEndWnValueAtIndex(numHitranRegionsDownloaded)) + " cm-1";
            emit notify(auxStr, 0, true, true);

            // Downloads are carried out itteratively with the client waiting for each request to be completed (othewise mmultiple requests overlap)
            sendDataRequest(downloadRegionsFinder->getDownloadStartWnValueAtIndex(numHitranRegionsDownloaded), downloadRegionsFinder->getDownloadEndWnValueAtIndex(numHitranRegionsDownloaded));
        }
    }
    else
    {
        // Emitting a signal indicating that the download of the HITRAN database is complete
        emit hitranDownloadComplete();
    }
}

// Slot indicating that authentication has been completed
void HitranOnlineDownloader::authenticationComplete()
{
    // Sending data request for a single spectal region
    sendDataRequest(downloadRegionsFinder->getDownloadStartWnValueAtIndex(numHitranRegionsDownloaded), downloadRegionsFinder->getDownloadEndWnValueAtIndex(numHitranRegionsDownloaded));
}

// Slot is called to send a data request to the remote server
void HitranOnlineDownloader::sendDataRequest(double fromWn, double toWn)
{
    QString query;  // String query to be sent to a remote server

    // Forming a query
    query = formQuery(fromWn, toWn);

    // Sending the query to the remote server
    requestData->setRequestStr(query);
    requestData->requestDataFromRemoteHost();
}
// Method to send a download every time data request
void HitranOnlineDownloader::sendDownloadEveryTimeDataRequest()
{
    QString auxStr;  // Auxiliary string to display messages to the user


    if (numHitranRegionsDownloaded < downloadRegionsFinder->getDownloadEndWn()->count())
    {
        // Displaying a notification to the user
        auxStr = "Downloading HITRAN data " + QString::number(numHitranRegionsDownloaded + 1) + " of " + QString::number(downloadRegionsFinder->getDownloadEndWn()->count()) + "\nRegion spectral range: " + QString::number(downloadRegionsFinder->getDownloadStartWnValueAtIndex(numHitranRegionsDownloaded)) + " - " + QString::number(downloadRegionsFinder->getDownloadEndWnValueAtIndex(numHitranRegionsDownloaded)) + " cm-1";
        emit notify(auxStr, 0, true, true);

        // Sending a data request to the HITRAN server
        sendDataRequest(downloadRegionsFinder->getDownloadStartWnValueAtIndex(numHitranRegionsDownloaded), downloadRegionsFinder->getDownloadEndWnValueAtIndex(numHitranRegionsDownloaded));
    }
    else
    {
        // Emitting a signal notifying that no more data is avialable
        emit noMoreDataAvailable();
    }
}

// Slot to process the server downloaded data
void HitranOnlineDownloader::processServerReturnedData(QByteArray *hitranData, bool success)
{
    QString auxStr;  // String to display progress information to the user


    // Handling the case when the user presses the cancel button
    if (this->cancelCalculation == true)
    {
        if (this->hitranFileUsage != 2)
        {
            // Saving data to a segmented HITRAN file
            QFile outFile(hitranio->getSegmentedHitranFolder() + "/" + downloadRegionsFinder->getFileNameAtIndex(numHitranRegionsDownloaded));  // Name of the HITRAN file in a text form with segmented information
            // Opening the HITRAN database text file for reading
            outFile.open(QIODevice::ReadWrite);
            QTextStream out(&outFile);
            out << *hitranData;
            outFile.close();

            // Clearing the notification
            emit notify("", 0, false, true);

            // Emitting a signal indicating that the download of the HITRAN database is complete
            emit hitranDownloadComplete();

            // Processing the events
            QApplication::processEvents();
        }
        else
        {
            // Emitting a signal indicating that the download of the HITRAN database is complete
            numHitranRegionsDownloaded++;

            // This will convert the byte data to the actual HITRAN formated variables and make that data available to the program
            this->hitranio->setByteArrayData(hitranData);

            // Indicating that the data is available
            emit dataAvailable();
        }
    }
    else  // If the cancel button has not been pressed, then processing the download etc.
    {
        if (success)
        {
            if (this->hitranFileUsage != 2)
            {
                // Saving data to a segmented HITRAN file
                QFile outFile(hitranio->getSegmentedHitranFolder() + "/" + downloadRegionsFinder->getFileNameAtIndex(numHitranRegionsDownloaded));  // Name of the HITRAN file in a text form with segmented information
                // Opening the HITRAN database text file for reading
                outFile.open(QIODevice::ReadWrite);
                QTextStream out(&outFile);
                out << *hitranData;
                outFile.close();

                // Updating the download history file with the newly downloaded spectral region
                // This should either be done once only or separately for each data request for sub-regions (think about how to better implement this)
                // This operation should only be done once, after all of the HITRAN data has been downloaded from a remote server
                numHitranRegionsDownloaded++;
                if (numHitranRegionsDownloaded < downloadRegionsFinder->getDownloadEndWn()->count())
                {
                    // Displaying a notification to the user
                    auxStr = "Downloading HITRAN, file " + QString::number(numHitranRegionsDownloaded + 1) + " of " + QString::number(downloadRegionsFinder->getDownloadEndWn()->count()) + "\nFile spectral range: " + QString::number(downloadRegionsFinder->getDownloadStartWnValueAtIndex(numHitranRegionsDownloaded)) + " - " + QString::number(downloadRegionsFinder->getDownloadEndWnValueAtIndex(numHitranRegionsDownloaded)) + " cm-1";
                    emit notify(auxStr, 0, true, true);

                    // Downloading the next file
                    // Downloads are carried out itteratively with the client waiting for each request to be completed (otherwise mmultiple requests overlap)
                    sendDataRequest(downloadRegionsFinder->getDownloadStartWnValueAtIndex(numHitranRegionsDownloaded), downloadRegionsFinder->getDownloadEndWnValueAtIndex(numHitranRegionsDownloaded));
                }
                else
                {
                    // Clearing the notification
                    emit notify("", 0, false, true);

                    // Emitting a signal indicating that the download of the HITRAN database is complete
                    emit hitranDownloadComplete();
                }

                // Processing the events
                QApplication::processEvents();
            }
            else  // This is when the data is directly downloaded every timme (no saving of data)
            {
                // Emitting a signal indicating that the download of the HITRAN database is complete
                numHitranRegionsDownloaded++;

                // This will convert the byte data to the actual HITRAN formated variables and make that data available to the program
                this->hitranio->setByteArrayData(hitranData);

                // Indicating that the data is available
                emit dataAvailable();
            }
        }
        else
        {
            // Sending a notification
            emit notify("", 0, false, true);

            emit openOkDialog("Error downloading HITRAN data.");

            // Emiting a signal indicating that the HITRAN download is unsuccessful
            emit hitranDownloadUnsuccessful();
        }
    }
}

// Method to determine the HITRAN database record boundaries for the entire spectral region based on the start and end wavenumbers
void HitranOnlineDownloader::getHitranRecordBoundaries()
{
    int state;  // State variable


    state = this->getCompleteHitranDbFileExists();  // Getting the information of whether the complete HITRAN database exists and in what form
    if (this->hitranFileUsage == 1)  // Usage of the locally placed cumulative HITRAN file (cumulative or segmented)
    {
        // If the cumulative HITRAN file exists, use it
        if ((state == 10) || (state == 11) || (state == 12))
        {
            // Determining the starting and ending record numbers for the entire range
            hitranio->getCumulativeStartEndRecNum(this->startWn, this->endWn, &(this->startWnByte), &(this->endWnByte));

            // Initializing the subrange
            this->startSubrangeWnByte = this->startWnByte;
            this->endSubrangeWnByte = this->startWnByte + this->subrangeRecordsStep * 162;  // This multiplication factor is to take into account the number of bytes in a single line of the HITRAN database
            if (endSubrangeWnByte > this->endWnByte) { this->endSubrangeWnByte = this->endWnByte; }
        }
        // If no cumulative HITRAN file exists, then using the segmented HITRAN files
        else if ((state == 1) || (state == 2))
        {
            // Determining the starting and ending record numbers for the entire range
            hitranio->getSegmentedStartEndRecNum(this->startWn, this->endWn, &(this->startWnFileIndex), &(this->endWnFileIndex), &(this->startWnByte), &(this->endWnByte));

             // Initializing the subrange values
            this->startSubrangeWnByte = this->startWnByte;
            this->startSubrangeWnFileIndex = this->startWnFileIndex;
            // Determine how to handle the switching to the next file once the capacity of a file is decreased
        }
    }
    // Download on a need-to basis
    else if (this->hitranFileUsage == 0)
    {
        // Determining the starting and ending record numbers for the entire range
        hitranio->getSegmentedStartEndRecNum(this->startWn, this->endWn, &(this->startWnFileIndex), &(this->endWnFileIndex), &(this->startWnByte), &(this->endWnByte));

        // Initializing the subrange values
        this->startSubrangeWnByte = this->startWnByte;
        this->startSubrangeWnFileIndex = this->startWnFileIndex;
        // Determine how to handle the switching to the next file once the capacity of a file is decreased
    }
    // Download without saving
    else if (this->hitranFileUsage == 2)
    {
        // Instead finding the download regions
        downloadRegionsFinder->setStartWn(this->startWn);
        downloadRegionsFinder->setEndWn(this->endWn);
        downloadRegionsFinder->findDownloadEveryTmeRegions();
        numHitranRegionsDownloaded = 0;  // Setting to the beginnig segment
    }
}

// Method to get the next records subrange
void HitranOnlineDownloader::getNextSubrange()
{
    int state;  // Getting the information about the HITRAN databases available
    int step;   // Records step calculated depending on the location of the spectral region


    state = this->getCompleteHitranDbFileExists();  // Getting the information on whether the complete HITRAN database exists and in what form
    // Use complete HITRAN locally stored
    if (this->hitranFileUsage == 1)
    {
        // If the cumulative HITRAN file exists, use it
        if ((state == 10) || (state == 11) || (state == 12))
        {
            // If the start subrange is beyond the calculation region, then no read is necessary and returning false
            if (this->startSubrangeWnByte >= this->endWnByte)
            {
                // Sending a notification
                emit notify("", 0, false, true);

                // Emitting a signal notifying that no more data is available
                emit noMoreDataAvailable();
            }
            else
            {
                // Reading the subrange of data values and passing to the internal data variable
                hitranio->readCumulativeHitranData(this->startSubrangeWnByte, this->endSubrangeWnByte);

                // Incrementing the subrange values
                this->startSubrangeWnByte = this->endSubrangeWnByte;
                this->endSubrangeWnByte = this->startSubrangeWnByte + this->subrangeRecordsStep * 162;  // This multiplication factor is to take into account the number of bytes in a single line of the HITRAN database
                if (endSubrangeWnByte > this->endWnByte) { this->endSubrangeWnByte = this->endWnByte; }

                // Returning true indicating that some data has been read in
                emit dataAvailable();
            }
        }
        else if ((state == 1) || (state == 2))  // If no cumulative HITRAN file exists, then using the segmented HITRAN files
        {
            if ((this->startSubrangeWnFileIndex > this->endWnFileIndex) || ((this->startSubrangeWnByte >= this->endWnByte) && (this->startSubrangeWnFileIndex == this->endWnFileIndex)))
            {
                // Sending a notification
                emit notify("", 0, false, true);

                // Emitting a signal notifying that no more data is available
                emit noMoreDataAvailable();
            }
            else
            {
                // Calculating the records step
                step = this->subrangeRecordsStep * 162;
                if ((this->startSubrangeWnFileIndex == this->endWnFileIndex) && ((this->endWnByte - this->startSubrangeWnByte) < (this->subrangeRecordsStep * 162)))
                {
                    step = this->endWnByte - this->startSubrangeWnByte;
                }

                // Reading data and incrementing to the next file
                hitranio->readSegmentedHitran(&(this->startSubrangeWnByte), &(this->startSubrangeWnFileIndex), step);

                // Returning true indicating that some data has been read in
                emit dataAvailable();
            }
        }
    }
    // Download on a need-to basis, store and reuse
    else if (this->hitranFileUsage == 0)
    {
        if ((this->startSubrangeWnFileIndex > this->endWnFileIndex) || ((this->startSubrangeWnByte >= this->endWnByte) && (this->startSubrangeWnFileIndex == this->endWnFileIndex)))
        {
            // Sending a notification
            emit notify("", 0, false, true);

            // Emitting a signal notifying that no more data is available
            emit noMoreDataAvailable();
        }
        else
        {
            // Calculating the records step
            step = this->subrangeRecordsStep * 162;
            if ((this->startSubrangeWnFileIndex == this->endWnFileIndex) && ((this->endWnByte - this->startSubrangeWnByte) < (this->subrangeRecordsStep * 162)))
            {
                step = this->endWnByte - this->startSubrangeWnByte;
            }

            // Reading data and incrementing to the next file
            hitranio->readSegmentedHitran(&(this->startSubrangeWnByte), &(this->startSubrangeWnFileIndex), step);

            // Returning true indicating that some data has been read in
            emit dataAvailable();
        }
    }
    // Download every time
    // This is the case when each spectal segment is requested directly from the HITRAN server
    else if (this->hitranFileUsage == 2)
    {
        // Requesting a download of a spectral segment        
        sendDownloadEveryTimeDataRequest();
    }
}

// Method is to carry out complete HITRAN online database download (segmented files which will be stored separately)
void HitranOnlineDownloader::downloadCompleteSegmentedHitran()
{
    // To send a request for a complete HITRAN download
    getHitranData(this->userAuthen, 0, 50000);
}
