// 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.



// Object to read/write HITRAN lines within a specified spectral range
#include "hitraninputoutput.h"
#include <QLocale>
#include <QFile>
#include <QTextStream>
#include <QApplication>
#include <math.h>
#include <QDir>
#include <QMessageBox>

HitranInputOutput::HitranInputOutput()
{
    ;
}

// Methods to get and set the path to the HITRAN database file
QString HitranInputOutput::getHitranDbFilePath() { return this->hitranDbFilePath; }
void HitranInputOutput::setHitranDbFilePath(QString newHitranDbFilePath) { this->hitranDbFilePath = newHitranDbFilePath; }

// Method to get and set the segmented HITRAN folder
QString HitranInputOutput::getSegmentedHitranFolder() { return this->segmentedHitranFolder; }
void HitranInputOutput::setSegmentedHitranFolder(QString newSegmentedHitranFolder) { this->segmentedHitranFolder = newSegmentedHitranFolder; }

// Method to get the number of the start and end record corresponding to a given start and end wavenumbers
void HitranInputOutput::getCumulativeStartEndRecNum(double fromWn, double toWn, quint64 *fromByte, quint64 *toByte)
{
    quint64 startIndex;   // Start byte number in the file
    quint64 endIndex;     // End byte number in the file
    quint64 middleIndex = 0;  // Index of the middle record in the list of records
    quint64 fileBytes;    // Total tumber of bytes in the file
    double prevWn;        // Wavenumber found on the previous step
    QLocale c;       // Object for the data conversion from string to numeric vlaues (locality based)
    QString auxStr;  // Auxiliary string used to extract values from the total line string

    QFile inFile(this->hitranDbFilePath);  // Name of the HITRAN file in a text form to use
    QString lines;   // Line to read the data from
    HitranOneRec values;     // Values of a single record read in from the HITRAN file


    // Opening the HITRAN database text file for reading
    inFile.open(QIODevice::ReadOnly);
    QTextStream in(&inFile);

    // Processing events
    QApplication::processEvents();

    // Getting the total file size in bytes
    fileBytes = inFile.size();

    // Determining the middle index
    startIndex = 0;
    endIndex = floor((double) (fileBytes / 162)) * 162;  // just in case there are extra characters at the end of the file

    // Determining the starting index of the HITRAN dataset to be read in from a file
    prevWn = 1;
    values.setWn(0);
    while (!((prevWn == values.getWn()) || ((endIndex - startIndex) <= 2)))
    {
          // Setting the previously found wavenumber
          prevWn = values.getWn();

          middleIndex = round((double)((startIndex + endIndex) / 324)) * 162;

          // Readin in one line of data
          in.seek(middleIndex);
          lines = in.read(162);

          // Extracting data values
          auxStr = lines.mid(0, 2);
          values.setMolNum(c.toInt(auxStr));  // Molecule number

          // Extracting data values
          extractLineByLineData2004Hitran(lines, &values);

          // Processing events
          QApplication::processEvents();

          // Setting new index range
          if (fromWn > values.getWn()) { startIndex = middleIndex; }
          else { endIndex = middleIndex; }
    }
    *fromByte = endIndex;


    // Determining the middle index
    startIndex = 0;
    endIndex = floor((double) (fileBytes / 162)) * 162;  // just in case there are extra characters at the end of the file

    // Determining the starting index of the HITRAN dataset to be read in from a file
    prevWn = 1;
    values.setWn(0);
    while (!((prevWn == values.getWn()) || ((endIndex - startIndex) <= 2)))
    {
        // Setting the previously found wavenumber
        prevWn = values.getWn();

        middleIndex = round((double)((startIndex + endIndex) / 324)) * 162;

        // Readin in one line of data
        in.seek(middleIndex);
        lines = in.read(162);

        // Extracting data values
        extractLineByLineData2004Hitran(lines, &values);

        // Processing events
        QApplication::processEvents();

        // Setting new index range
        if (toWn > values.getWn()) { startIndex = middleIndex; }
        else { endIndex = middleIndex; }
    }
    *toByte = endIndex;
}

// Method to get the number of the start and end record corresponding to a given start and end wavenumbers
void HitranInputOutput::getCumulativeStartEndRecNum(QString fileName, double fromWn, double toWn, quint64 *fromByte, quint64 *toByte)
{
    quint64 startIndex;   // Start byte number in the file
    quint64 endIndex;     // End byte number in the file
    quint64 middleIndex = 0;  // Index of the middle record in the list of records
    quint64 fileBytes;    // Total tumber of bytes in the file
    double prevWn;        // Wavenumber found on the previous step
    QLocale c;       // Object for the data conversion from string to numeric vlaues (locality based)
    QString auxStr;  // Auxiliary string used to extract values from the total line string

    QFile inFile(fileName);  // Name of the HITRAN file in a text form to use
    QString lines;   // Line to read the data from
    HitranOneRec values;     // Values ofor a single record read in from the HITRAN file


    // Opening the HITRAN database text file for reading
    inFile.open(QIODevice::ReadOnly);
    QTextStream in(&inFile);

    // Processing events
    QApplication::processEvents();

    // Getting the total file size in bytes
    fileBytes = inFile.size();

    // Determining the middle index
    startIndex = 0;
    endIndex = floor((double) (fileBytes / 162)) * 162;  // just in case there are extra characters at the end of the file

    // Determining the starting index of the HITRAN dataset to be read in from a file
    prevWn = 1;
    values.setWn(0);
    while (!((prevWn == values.getWn()) || ((endIndex - startIndex) <= 2)))
    {
          // Setting the previously found wavenumber
          prevWn = values.getWn();

          middleIndex = round((double)((startIndex + endIndex) / 324)) * 162;

          // Readin in one line of data
          in.seek(middleIndex);
          lines = in.read(162);

          // Extracting data values
          auxStr = lines.mid(0, 2);
          values.setMolNum(c.toInt(auxStr));  // Molecule number

          // Extracting data values
          extractLineByLineData2004Hitran(lines, &values);

          // Processing events
          QApplication::processEvents();

          // Setting new index range
          if (fromWn > values.getWn()) { startIndex = middleIndex; }
          else { endIndex = middleIndex; }
    }
    *fromByte = endIndex;


    // Determining the middle index
    startIndex = 0;
    endIndex = floor((double) (fileBytes / 162)) * 162;  // just in case there are extra characters at the end of the file

    // Determining the starting index of the HITRAN dataset to be read in from a file
    prevWn = 1;
    values.setWn(0);
    while (!((prevWn == values.getWn()) || ((endIndex - startIndex) <= 2)))
    {
        // Setting the previously found wavenumber
        prevWn = values.getWn();

        middleIndex = round((double)((startIndex + endIndex) / 324)) * 162;

        // Readin in one line of data
        in.seek(middleIndex);
        lines = in.read(162);

        // Extracting data values
        extractLineByLineData2004Hitran(lines, &values);

        // Processing events
        QApplication::processEvents();

        // Setting new index range
        if (toWn > values.getWn()) { startIndex = middleIndex; }
        else { endIndex = middleIndex; }
    }
    *toByte = endIndex;
}

// Method to get the number of the start and end record corresponding to a given start and end wavenumbers
void HitranInputOutput::getCumulativeStartEndRecNum(QString fileName, double wn, quint64 *byte)
{
    quint64 startIndex;   // Start byte number in the file
    quint64 endIndex;     // End byte number in the file
    quint64 middleIndex = 0;  // Index of the middle record in the list of records
    quint64 fileBytes;    // Total tumber of bytes in the file
    double prevWn;        // Wavenumber found on the previous step
    QLocale c;       // Object for the data conversion from string to numeric vlaues (locality based)
    QString auxStr;  // Auxiliary string used to extract values from the total line string

    QFile inFile(fileName);  // Name of the HITRAN file in a text form to use
    QString lines;   // Line to read the data from
    HitranOneRec values;     // Values ofor a single record read in from the HITRAN file


    // Opening the HITRAN database text file for reading
    inFile.open(QIODevice::ReadOnly);
    QTextStream in(&inFile);

    // Processing events
    QApplication::processEvents();

    // Getting the total file size in bytes
    fileBytes = inFile.size();

    // Determining the middle index
    startIndex = 0;
    endIndex = floor((double) (fileBytes / 162)) * 162;  // just in case there are extra characters at the end of the file

    // Determining the starting index of the HITRAN dataset to be read in from a file
    prevWn = 1;
    values.setWn(0);
    while (!((prevWn == values.getWn()) || ((endIndex - startIndex) <= 2)))
    {
          // Setting the previously found wavenumber
          prevWn = values.getWn();

          middleIndex = round((double)((startIndex + endIndex) / 324)) * 162;

          // Readin in one line of data
          in.seek(middleIndex);
          lines = in.read(162);

          // Extracting data values
          auxStr = lines.mid(0, 2);
          values.setMolNum(c.toInt(auxStr));  // Molecule number

          // Extracting data values
          extractLineByLineData2004Hitran(lines, &values);

          // Processing events
          QApplication::processEvents();

          // Setting new index range
          if (wn > values.getWn()) { startIndex = middleIndex; }
          else { endIndex = middleIndex; }
    }
    *byte = endIndex;
}

// Method to set the byte array and convert it to HITRAN variables
void HitranInputOutput::setByteArrayData(QByteArray *hitranData)
{
    QStringList splitLines;  // HITRAN lines read in from a file
    QString lines;   // String with the data
    int i;   // Loop variable


    // Splitting the read in data into separate lines
    lines = *hitranData;
    splitLines = lines.split("\n", QString::SkipEmptyParts);

    // Converting the read in data from the string representation into the data values
    data.resize(splitLines.count());
    for (i = 0; i < splitLines.count(); i++)
    {
        extractLineByLineData2004Hitran(splitLines[i], &(data[i]));
        // strLines.append(splitLines[i]);
    }
}

// Method to read HITRAN records from the cumulative HITRAN file based on the start and end indexes as opposed to wavenumber range
void HitranInputOutput::readCumulativeHitranData(quint64 fromByte, quint64 toByte)
{
    readCumulativeHitranData(this->hitranDbFilePath, fromByte, toByte);
}

// Method to read HITRAN records from the cumulative HITRAN file based on the start and end indexes as opposed to wavenumber range
bool HitranInputOutput::readCumulativeHitranData(QString filePath, quint64 fromByte, quint64 toByte)
{
    QFile inFile(filePath);  // Name of the HITRAN file in a text form to use
    int i;           // Loop variable
    QString lines;   // Line to read the data from
    QStringList splitLines;  // HITRAN lines read in from a file
    quint64 bytesToRead;  // Number of bytes to read
    bool eofReached;  // Variable indicates if the end of file has been reached


    // Assuming the end of file has not been reached
    eofReached = false;

    // Opening the HITRAN database text file for reading
    if (inFile.open(QIODevice::ReadOnly) == true)
    {
        // Initializing the text stream file
        QTextStream in(&inFile);

        // Reading the middle index HITRAN record
        in.seek(fromByte);

        // Reading in the relevant segment of data from the file
        bytesToRead = inFile.size() - fromByte;
        if (bytesToRead < (toByte - fromByte))
        {
            eofReached = true;
            lines = in.readAll();  // Reading everything available up to the end of file
        }
        else
        {
            lines = in.read(toByte - fromByte);  // Reading only the number of bytes specified
        }

        // Splitting the read in data into separate lines
        splitLines = lines.split("\n", QString::SkipEmptyParts);

        // Converting the read in data from the string representation into the data values
        data.resize(splitLines.count());
        for (i = 0; i < splitLines.count(); i++)
        {
            extractLineByLineData2004Hitran(splitLines[i], &(data[i]));
            // strLines.append(splitLines[i]);
        }

        // Closing the input file
        inFile.close();
    }

    // Returning a variable indicating if the end of file has been reached
    return eofReached;
}

// Reading records from a certain wavenumber up to a given wavenumber from the HITRAN database
void HitranInputOutput::readRecordsText(QString fileName, double fromWn, double toWn)
{
    quint64 startIndex;   // Start byte number in the file
    quint64 endIndex;     // End byte number in the file
    quint64 middleIndex = 0;  // Index of the middle record in the list of records
    quint64 fileBytes;    // Total tumber of bytes in the file
    quint64 fromIndex;    // Byte index in a file corresponding to the from Wavenumber
    quint64 toIndex;      // Byte index in a file corresponding to the to wavenumber
    double prevWn;        // Wavenumber found on the previous step
    QLocale c;       // Object for the data conversion from string to numeric vlaues (locality based)
    QString auxStr;  // Auxiliary string used to extract values from the total line string

    QFile inFile(fileName);  // Name of the HITRAN file in a text form to use
    int i;           // Loop variable
    QString lines;   // Line to read the data from
    QStringList splitLines;  // HITRAN lines read in from a file    
    HitranOneRec values;     // Values ofor a single record read in from the HITRAN file


    // Opening the HITRAN database text file for reading
    inFile.open(QIODevice::ReadOnly);
    QTextStream in(&inFile);

    // Processing events
    QApplication::processEvents();

    // Getting the total file size in bytes
    fileBytes = inFile.size();

    // Determining the middle index
    startIndex = 0;
    endIndex = floor((double) (fileBytes / 162)) * 162;  // just in case there are extra characters at the end of the file

    // Determining the starting index of the HITRAN dataset to be read in from a file
    prevWn = 1;
    values.setWn(0);
    while (!((prevWn == values.getWn()) || ((endIndex - startIndex) <= 2)))
    {
          // Setting the previously found wavenumber
          prevWn = values.getWn();

          middleIndex = round((double)((startIndex + endIndex) / 324)) * 162;

          // Readin in one line of data
          in.seek(middleIndex);
          lines = in.read(162);

          // Extracting data values
          auxStr = lines.mid(0, 2);
          values.setMolNum(c.toInt(auxStr));  // Molecule number

          // Extracting data values
          extractLineByLineData2004Hitran(lines, &values);

          // Processing events
          QApplication::processEvents();

          // Setting new index range
          if (fromWn > values.getWn()) { startIndex = middleIndex; }
          else { endIndex = middleIndex; }
    }
    fromIndex = endIndex;


    // Determining the middle index
    startIndex = 0;
    endIndex = floor((double) (fileBytes / 162)) * 162;  // just in case there are extra characters at the end of the file

    // Determining the starting index of the HITRAN dataset to be read in from a file
    prevWn = 1;
    values.setWn(0);
    while (!((prevWn == values.getWn()) || ((endIndex - startIndex) <= 2)))
    {
        // Setting the previously found wavenumber
        prevWn = values.getWn();

        middleIndex = round((double)((startIndex + endIndex) / 324)) * 162;

        // Readin in one line of data
        in.seek(middleIndex);
        lines = in.read(162);

        // Extracting data values
        extractLineByLineData2004Hitran(lines, &values);

        // Processing events
        QApplication::processEvents();

        // Setting new index range
        if (toWn > values.getWn()) { startIndex = middleIndex; }
        else { endIndex = middleIndex; }
    }
    toIndex = endIndex;


    // Reading the middle index HITRAN record
    in.seek(fromIndex);

    // Reading in the relevant segment of data from the file
    lines = in.read(toIndex - fromIndex);

    // Splitting the read in data into separate lines
    splitLines = lines.split("\n", QString::SkipEmptyParts);

    // Converting the read in data from the string representation into the data values
    data.resize(splitLines.count());
    for (i = 0; i < splitLines.count(); i++)
    {
        extractLineByLineData2004Hitran(splitLines[i], &(data[i]));
        // strLines.append(splitLines[i]);
    }
}
// Reading the HITRAN database segment for a given region using the internally saved HITRAN database file location
void HitranInputOutput::readRecordsText(double fromWn, double toWn)
{
    readRecordsText(this->hitranDbFilePath, fromWn, toWn);
}

// Method to read a segment from the segmented HITRAN folder and return new starting points variables
void HitranInputOutput::readSegmentedHitran(quint64 *fromByte, int *fromFileIndex, quint64 downloadBytes)
{
    QString fileName;  // Name of the file to read data from
    QLocale loc;  // Object to convert to numbers from strings
    QDir dir(this->segmentedHitranFolder);   // Directory object with the HITRAN segmented files
    int downloadedSegmentNum;   // Segment number of the downloaded region
    QStringList files;      // List of files contained in the directory
    QStringList fileNameSplitList;  // Split name of a file containing the segment number and the spectral range
    int i;  // Loop variable
    bool eofReached;  // Variable indicates if the end of file has been reached


    // Finding the file with the file index required
    // Getting the list of files already in the folder
    files = dir.entryList(QDir::NoDotAndDotDot | QDir::Files);

    // Need to re-arrange the files in the list to be in accending file number order
    bubbleSort(&files);

    // Getting the indexes of the files already in place (to determine what needs to be downloaded)
    i = 0;
    fileName = files[0];
    fileNameSplitList = files[0].split("-", QString::SkipEmptyParts);
    downloadedSegmentNum = loc.toInt(fileNameSplitList[0]);
    while (downloadedSegmentNum != *fromFileIndex)
    {
        i++;
        fileName = files[i];
        fileNameSplitList = files[i].split("-", QString::SkipEmptyParts);
        downloadedSegmentNum = loc.toInt(fileNameSplitList[0]);
    }

    // Reading in as many bytes as are available in the first file up to the records step specified or the end of file
    // If the end of file is reached before the number of records in the step is traversed, setting the next pointer to the
    // beginning of the next file for the next read.  This is a simplification procedure.
    eofReached = readCumulativeHitranData(this->segmentedHitranFolder + "/" + fileName, *fromByte, *fromByte + downloadBytes);

    if (eofReached == true)
    {
        *fromFileIndex = *fromFileIndex + 1;
        *fromByte = 0;
    }
    else
    {
        *fromFileIndex = *fromFileIndex;
        *fromByte = *fromByte + downloadBytes;
    }
}

// Bubble sort for the lidar of file name strings
void HitranInputOutput::bubbleSort(QStringList *arr) {

    bool swapped = true;
    int j = 0;
    QString tmp;
    int num;
    int numNext;
    QLocale loc;


    while (swapped)
    {
        swapped = false;
        j++;

        for (int i = 0; i < arr->count() - j; i++)
        {
            num = loc.toInt(((*arr)[i].split("-", QString::SkipEmptyParts))[0]);
            numNext = loc.toInt(((*arr)[i + 1].split("-", QString::SkipEmptyParts))[0]);

            if (num > numNext) {
                tmp = (*arr)[i];
                (*arr)[i] = (*arr)[i + 1];
                (*arr)[i + 1] = tmp;
                swapped = true;
                num = loc.toInt(((*arr)[i].split("-", QString::SkipEmptyParts))[0]);
                numNext = loc.toInt(((*arr)[i + 1].split("-", QString::SkipEmptyParts))[0]);
            }
        }
    }
}

// Method to get the HITRAN database start and end indexes and byte numbers in case segmented HITRAN files are in use
void HitranInputOutput::getSegmentedStartEndRecNum(double fromWn, double toWn, int *fromFileIndex, int *toFileIndex, quint64 *fromByte, quint64 *toByte)
{
    QLocale loc;  // Object to convert to numbers from strings
    QDir dir(this->segmentedHitranFolder);   // Directory object with the HITRAN segmented files
    int downloadedSegmentNum;   // Segment number of the downloaded region
    double segmentStartWn;  // Rounded to integer start wavenumber
    double segmentEndWn;    // Rounded to integer end wavenumber
    QStringList files;      // List of files contained in the directory
    QStringList fileNameSplitList;  // Split name of a file containing the segment number and the spectral range
    int i;  // Loop variable


    // Getting the list of files already in the folder
    files = dir.entryList(QDir::NoDotAndDotDot | QDir::Files);

    // Rearranging the files to be in accending order in terms of their numbering (each file is numbered within the file name)
    bubbleSort(&files);

    // Removing the file extension
    for (i = 0; i < files.count(); i++)
    {
        files[i] = (files[i].split("."))[0];
    }

    // Getting the indexes of the files already in place (to determine what needs to be downloaded)
    i = 0;
    fileNameSplitList = files[0].split("-", QString::SkipEmptyParts);
    downloadedSegmentNum = loc.toInt(fileNameSplitList[0]);
    segmentStartWn = loc.toInt(fileNameSplitList[1]);
    segmentEndWn = loc.toInt(fileNameSplitList[2]);
    while (!((fromWn >= segmentStartWn) && (fromWn <= segmentEndWn)))
    {        
        fileNameSplitList = files[i].split("-", QString::SkipEmptyParts);
        downloadedSegmentNum = loc.toInt(fileNameSplitList[0]);
        segmentStartWn = loc.toInt(fileNameSplitList[1]);
        segmentEndWn = loc.toInt(fileNameSplitList[2]);
        i++;
    }
    if (i > 0) { i--; }

    // Finding the record numbers within the files
    *fromFileIndex = downloadedSegmentNum;
    getCumulativeStartEndRecNum(this->segmentedHitranFolder + "/" + files[i] + ".par", fromWn, fromByte);

    i = 1;
    fileNameSplitList = files[0].split("-", QString::SkipEmptyParts);
    downloadedSegmentNum = loc.toInt(fileNameSplitList[0]);
    segmentStartWn = loc.toInt(fileNameSplitList[1]);
    segmentEndWn = loc.toInt(fileNameSplitList[2]);

    while (!((toWn >= segmentStartWn) && (toWn <= segmentEndWn)))
    {
        fileNameSplitList = files[i].split("-", QString::SkipEmptyParts);
        downloadedSegmentNum = loc.toInt(fileNameSplitList[0]);
        segmentStartWn = loc.toInt(fileNameSplitList[1]);
        segmentEndWn = loc.toInt(fileNameSplitList[2]);
        i++;
    }    
    if (i > 0) { i--; }
    *toFileIndex = i;

    // Finding the record numbers within the files
    *toFileIndex = downloadedSegmentNum;
    getCumulativeStartEndRecNum(this->segmentedHitranFolder + "/" + files[i] + ".par", toWn, toByte);
}

// Method to get the number of records for segmented HITRAN files based on the start and end index and file number
quint64 HitranInputOutput::getNumSegmentedRecsToProcess(int fromFileIndex, int toFileIndex, quint64 fromByte, quint64 toByte)
{
    QLocale loc;  // Object to convert to numbers from strings
    QDir dir(this->segmentedHitranFolder);   // Directory object with the HITRAN segmented files
    QFileInfo info;  // Object to get the file information
    int downloadedSegmentNum;   // Segment number of the downloaded region
    QStringList files;      // List of files contained in the directory
    QStringList fileNameSplitList;  // Split name of a file containing the segment number and the spectral range
    int i;  // Loop variable
    quint64 numRecs;


    // Getting the list of files already in the folder
    files = dir.entryList(QDir::NoDotAndDotDot | QDir::Files);

    // Removing the file extension
    for (i = 0; i < files.count(); i++)
    {
        files[i] = (files[i].split("."))[0];
    }

    // Calculating the total size in all files and the number of records
    numRecs = 0;
    for (i = 0; i < files.count(); i++)
    {
        fileNameSplitList = files[i].split("-", QString::SkipEmptyParts);
        downloadedSegmentNum = loc.toInt(fileNameSplitList[0]);
        if ((downloadedSegmentNum >= fromFileIndex) && (downloadedSegmentNum <= (toFileIndex - 1)))
        {
            info.setFile(this->segmentedHitranFolder + "/" + files[i] + ".par");
            numRecs = numRecs + info.size();
        }
    }
    numRecs = numRecs + toByte - fromByte;
    numRecs = numRecs / 162;

    return numRecs;
}

// Method to get the array with the read in HITRAN data
QVector<HitranOneRec> *HitranInputOutput::getReadInData() { return &(this->data); }


// Function to extract line-by-line data for a single spectral line
void HitranInputOutput::extractLineByLineData2004Hitran(QString lineStr, HitranOneRec *lineData) {

    QString auxStr;  // Auxiliary string used to extract values from the total line string
    QLocale c(QLocale::English, QLocale::UnitedStates);    // Object for the data conversion from string to numeric vlaues (locality based)
    // The locale has to be set to United states for this one because the HITRAN data was generated in the USA


    auxStr = lineStr.mid(0, 2);
    lineData->setMolNum(c.toInt(auxStr));  // Molecule number
    auxStr = lineStr.mid(2, 1);
    lineData->setIsoNum(c.toInt(auxStr));  // Isotopologue number
    auxStr = lineStr.mid(3, 12);
    lineData->setWn(c.toDouble(auxStr));      // Vacuum wavenumber (cm-1)
    auxStr = lineStr.mid(15, 10);
    lineData->setIntens(c.toDouble(auxStr));  // Line intensity (cm-1 / (molecule * cm^-2) at standard 296K)
    lineData->setR(c.toDouble(auxStr));       // Weighted square of the transition moment (Debye^2) for an electric dipole transition
    auxStr = lineStr.mid(25, 10);
    lineData->setA(c.toDouble(auxStr));       // Einstein A-coefficient (S^-1)
    auxStr = lineStr.mid(35, 5);
    lineData->setAirHw(c.toDouble(auxStr));   // Air-broadening half-width
    auxStr = lineStr.mid(40, 5);
    lineData->setSelfHw(c.toDouble(auxStr));  // Self-broadening half-width
    auxStr = lineStr.mid(45, 10);
    lineData->setLowE(c.toDouble(auxStr));    // Lower state energy (cm-1)
    auxStr = lineStr.mid(55, 4);
    lineData->setNAir(c.toDouble(auxStr));    // Temperature dependence exponent
    auxStr = lineStr.mid(59, 8);
    lineData->setAirPShift(c.toDouble(auxStr));  // Air pressure shift (cm^-1 * atm^-1) at 296K

    // These additional fields are to be implemented later
    //    auxStr = lineStr.mid(67, 15);
    //    lineData->setUpperV(auxStr);    // Upper state global quanta
    //    auxStr = lineStr.mid(82, 15);
    //    lineData->setLowerV(auxStr);    // Lower state global quanta
    //    auxStr = lineStr.mid(197, 15);
    //    lineData->setUpperQ(auxStr);    // Upper state local quanta
    //    auxStr = lineStr.mid(112, 15);
    //    lineData->setLowerQ(auxStr);    // Lower state local quanta
    //    auxStr = lineStr.mid(127, 6);
    //    lineData->setIntUncWn(c.toInt(auxStr));      // Wavenumber uncertainty index
    //    auxStr = lineStr.mid(133, 12);
    //    lineData->setIntUncIntens(c.toInt(auxStr));  // Intensity uncertainty index
    //    auxStr = lineStr.mid(145, 1);
    //    lineData->setIntUncAirHw(c.toInt(auxStr));   // Air-broadening half-width uncertainty index
    //    auxStr = lineStr.mid(146, 7);
    //    lineData->setIntUncSelfHw(c.toInt(auxStr));  // Self-broadening half-width uncertainty index
    //    auxStr = lineStr.mid(153, 7);
    //    lineData->setIntUncNAir(c.toInt(auxStr));    // Temperature dependent exponent uncertainty index

    //    The values below are obtained from the above read information and additional datafiles
    //    lineData->setIntUncAirPShift(0);   // Air-pressure shift uncertainty index
    //    lineData->setDblUncWn(0);      // Wavenumber uncertainty value
    //    lineData->setDblUncIntens(0);  // Intensity uncertainty value
    //    lineData->setDblUncAirHw(0);   // Air-broadening half-width uncertainty value
    //    lineData->setDblUncSelfHw(0);  // Self-broadening half-width uncertainty value
    //    lineData->setDblUncNAir(0);    // Temperature dependent exponent uncertainty value
    //    lineData->setDblUncAirPShift(0);  // Air-pressure shift uncertainty value
    //    lineData->setRefWn(0);      // Wavenumber uncertainty reference article number
    //    lineData->setRefIntens(0);  // Intensity uncertainty reference article number
    //    lineData->setRefAirHw(0);   // Air-broadening half-width uncertainty reference article number
    //    lineData->setRefSelfHw(0);  // Self-broadening half-width uncertainty reference article number
    //    lineData->setRefNAir(0);    // Temperature dependent exponent uncertainty reference article number
    //    lineData->setRefAirPShift(0);  // Air-pressure shift uncertainty reference article number
    //    lineData->setLMix("");   // Availablility of programs and data for the case of line-mixing
    //    lineData->setGUpper(0);  // Statistical weight of the upper state
    //    lineData->setGLower(0);  // Statistical weight of the lower state
}

// Method to get the string lines read in from the HITRAN database (no conversion to numerical representation)
QVector<QString> *HitranInputOutput::getStrLines()
{
    return &(this->strLines);
}

// Method to save data to file (usually appending at the right place)
void HitranInputOutput::saveDataToFile(QString *data)
{
    QFile hitranFile(this->hitranDbFilePath);

    // Need to find the location within the file where the data is to be saved
    double fromWn;        // Wavenumber to find
    quint64 startIndex;   // Start byte number in the file
    quint64 endIndex;     // End byte number in the file
    quint64 middleIndex = 0;  // Index of the middle record in the list of records
    quint64 fileBytes;    // Total tumber of bytes in the file
    quint64 fromIndex;    // Byte index in a file corresponding to the from Wavenumber
    double prevWn;        // Wavenumber found on the previous step
    QLocale c;       // Object for the data conversion from string to numeric vlaues (locality based)
    QString auxStr;  // Auxiliary string used to extract values from the total line string
    QString line;   // Line to read the data from
    HitranOneRec values;  // Values ofor a single record read in from the HITRAN file
    QString oneRecStr;    // String to contain a single HITRAN database record
    QMessageBox msgBox;   // Message box to display errors to the user
    QString buffer;       // Buffer to read in selected portion of a file to insert text data into a file


    // Need to work with data read in from a file for the entire spectral range in question and then insert
    // this segment back into the file after the insersion of the downloaded regions has been carried out

    // Maybe better algorithm could be written so that it is not necessary to separately hande the very first and the very last indices
    if (hitranFile.open(QIODevice::ReadWrite))
    {
        QTextStream ioStream(&hitranFile);

        // Getting the total file size in bytes
        fileBytes = hitranFile.size();

        // Determining the fromWn wavenumber from the data string to save to a file
        oneRecStr = data->mid(0, 162);
        extractLineByLineData2004Hitran(oneRecStr, &values);
        fromWn = values.getWn();

        // Determining the middle index
        startIndex = 0;
        endIndex = ceil((double) (fileBytes / 162)) * 162;  // Just in case there are extra characters at the end of the file

        // Determining the starting index of the HITRAN dataset to be read in from a file
        prevWn = 1;
        values.setWn(0);
        while (!((prevWn == values.getWn()) || ((endIndex - startIndex) <= 2)))
        {
            // Setting the previously found wavenumber
            prevWn = values.getWn();

            // Determining the mid index in the dataset
            middleIndex = floor((double)((startIndex + endIndex) / 324)) * 162;

            // Reading in one line of data
            ioStream.seek(middleIndex);
            line = ioStream.read(162);
            extractLineByLineData2004Hitran(line, &values);

            // Setting new index range
            if (fromWn > values.getWn()) { startIndex = middleIndex; }
            else { endIndex = middleIndex; }
        }
        // Probably need to move to the fromIndex location (carry out the seek operation one more time)
        fromIndex = endIndex;

        // Seeking the location in the file to write the data out at
        ioStream.seek(fromIndex);

        // Reading in the trailing data from the file (used to insert the text in the middle of a file)
        buffer = ioStream.read(hitranFile.size() - fromIndex);

        // Saving the data to a file at a specified position
        ioStream.seek(fromIndex); // Returning back from the previous position
        ioStream << (*data);
        ioStream << buffer;

        // Closing the file after the writing has been completed
        hitranFile.close();
    }
    else
    {
        msgBox.setText("Error opening HITRAN database for writing.");
        msgBox.exec();
    }
}

// Method to find the HITRAN download grid
void findHitranDownloadGrid(QString fileName, QString outputFile)
{
    QLocale c;       // Object for the data conversion from string to numeric vlaues (locality based)
    QString auxStr;  // Auxiliary string used to extract values from the total line string

    QFile inFile(fileName);  // Name of the HITRAN file in a text form to use
    QFile outFile(outputFile);
    QString lines;   // Line to read the data from
    HitranOneRec values;     // Values ofor a single record read in from the HITRAN file
    int currWn;  // Current wavenumber threshold
    int prevWn;  // Previous wavenumber threshold
    int wnStep = 10;  // Wavenumber step


    // Opening the HITRAN database text file for reading
    inFile.open(QIODevice::ReadOnly);
    QTextStream in(&inFile);

    // Opening the output file for writing
    outFile.open(QIODevice::WriteOnly);
    QTextStream out(&outFile);

    prevWn = 0;
    currWn = 0 + wnStep;
    out << prevWn;
    out << "; ";
    out << currWn;
    out << "\n";
    while(!inFile.atEnd())
    {
        lines = in.readLine();
        //extractLineByLineData2004Hitran(lines, &values);
        if (values.getWn() > currWn)
        {
            out << prevWn;
            out << "; ";
            out << currWn;
            out << "\n";
            prevWn = currWn;
            currWn = currWn + wnStep;
        }
    }

    // Closing the output and input files
    inFile.close();
    outFile.close();
}

// Method to read a segment from the segmented HITRAN folder and return new starting points variables
void readSegmentedHitran(quint64 *fromByte, int fromFileIndex, QVector<HitranOneRec> *data);
