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



// Class is used to save and load the configuration information from the previous program run
#include "configinputoutput.h"
#include <QFile>
#include <QTextStream>
#include <QMessageBox>
#include <QLocale>
#include <QDebug>

ConfigInputOutput::ConfigInputOutput()
{

}

// Method to get and set the configuration file path
QString ConfigInputOutput::getConfigFilePath() { return this->configFilePath; }
void ConfigInputOutput::setConfigFilePath(QString newConfigFilePath) { this->configFilePath = newConfigFilePath; }

// Method to save the configuration to a configuration file
void ConfigInputOutput::saveToConfig()
{
    int i;  // Loop variable
    QFile configFile(this->configFilePath);
    QString auxStr;  // Auxiliary string
    QMessageBox msgBox; // Message box to display information to the user
    QLocale loc(QLocale::English, QLocale::UnitedStates);   // Object to convert to string representatioon and back


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


        // Saving the pathlength and pathlength units
        auxStr = loc.toString(this->params->getHorizParams()->getPathLength(), 'e', 6) + "\t" + "Horizontal path length, meters\n";
        ioStream << auxStr;   // Horizontal path length, meters
        auxStr = loc.toString(this->pathUnitsConvert->getUnits()) + "\t" + "Pathlength units\n";
        ioStream << auxStr;   // Pathlength units
        ioStream << "\n";

        // Saving the total pressure and pressure units
        auxStr = loc.toString(this->params->getHorizParams()->getTotalPress(), 'e', 6) + "\t" + "Total pressure, Atm\n";
        ioStream << auxStr;   // Total pressure, Atm
        auxStr = loc.toString(this->concPressUnitsConvert->getPressUnits()) + "\t" + "Total pressure units\n";
        ioStream << auxStr;   // Total pressure units
        auxStr = loc.toString(this->concPressUnitsConvert->getCombinedUnits()) + "\t" + "Concentration/pressure units to display molecular concentrations\n";
        ioStream << auxStr;   // Concentration/pressure units to display molecular concentrations
        ioStream << "\n";

        // Saving the temperature and temperature units
        auxStr = loc.toString(this->params->getHorizParams()->getTemp(), 'e', 6) + "\t" + "Horizontal path temperature, Kelvin\n";
        ioStream << auxStr;   // Horizontal path temperature, Kelvin
        auxStr = loc.toString(this->tempUnitsConvert->getUnits()) + "\t" + "Temperature units\n";
        ioStream << auxStr;   // Horizontal path units
        ioStream << "\n";

        // Saving the vertial axis parameters (minimum and maximum values and the units)
        auxStr = loc.toString(this->yaxis->getMinAtten(), 'e', 6) + "\t" + "Vertical axis minimum scale value as absorption in %\n";
        ioStream << auxStr;   // Vertical axis minimum value
        auxStr = loc.toString(this->yaxis->getMaxAtten(), 'e', 6) + "\t" + "Vertical axis maximum scale value as absorption in %\n";
        ioStream << auxStr;   // Vertical axis maximum value
        auxStr = loc.toString(this->yaxis->getYPlotUnits()) + "\t" + "Vertical axis units (0 - Absorption cross-section, 1 - Optical depth, 2 - Absorption, 3 - Transmission)\n";
        ioStream << auxStr;   // Vertical axis units
        auxStr = loc.toString(this->yaxis->getEnableRulers()) + "\t" + "Enable / Disable rulers on zoom\n";
        ioStream << auxStr;   // Rullers on zoom
        // Cross-section related vertical axis parameters
        auxStr = loc.toString(this->yaxis->getMinCrossSection(), 'e', 6) + "\t" + "Cross-section minimum scale value\n";
        ioStream << auxStr;   // Cross-section minimum scale value
        auxStr = loc.toString(this->yaxis->getMaxCrossSection(), 'e', 6) + "\t" + "Cross-section maximum scale value\n";
        ioStream << auxStr;   // Cross-section maximum scale value
        auxStr = loc.toString(this->yaxis->getCrossSectionAutoRecalc()) + "\t" + "Enable / Disable cross-section maximum scale value auto-recalculation\n";
        ioStream << auxStr;   // Enable / Disable cross-section maximum scale value auto-recalculation
        // Radiance related vertical axis parameters
        auxStr = loc.toString(this->yaxis->getMinRadiance(), 'e', 6) + "\t" + "Radiance minimum scale value\n";
        ioStream << auxStr;   // Radiance minimum scale value
        auxStr = loc.toString(this->yaxis->getMaxRadiance(), 'e', 6) + "\t" + "Radiance maximum scale value\n";
        ioStream << auxStr;   // Radiance maximum scale value
        auxStr = loc.toString(this->yaxis->getRadianceAutoRecalc()) + "\t" + "Enable / Disable radiance maximum scale value auto-recalculation\n";
        ioStream << auxStr;   // Enable / Disable radiance maximum scale value auto-recalculation
        ioStream << "\n";

        // Saving the wavelength units, spectral range and resolution
        auxStr = loc.toString(this->wavelength->getStartWn(), 'e', 6) + "\t" + "Minimum wavenumber, cm-1\n";
        ioStream << auxStr;   // Minimum wavenumber, cm-1
        auxStr = loc.toString(this->wavelength->getEndWn(), 'e', 6) + "\t" + "Maximum wavenumber, cm-1\n";
        ioStream << auxStr;   // Maximum wavenumber, cm-1
        auxStr = loc.toString(this->wavelength->getNumPoints()) + "\t" + "Number of calculation points (resolution)\n";
        ioStream << auxStr;   // Number of calculation points (resolution)
        auxStr = loc.toString(this->wavelength->getUnits()) + "\t" + "Wavelength units\n";
        ioStream << auxStr;   // Wavelength units
        ioStream << "\n";

        // Saving the HITRAN database access option
        auxStr = loc.toString(this->hitranDownloader->getHitranFileUsage()) + "\t" + "HITRAN database access option\n";
        ioStream << auxStr;   // HITRAN database access option
        ioStream << "\n";

        // Saving the line options
        auxStr = loc.toString(this->params->getLineshapeProfile()) + "\t" + "Lineshape profile\n";
        ioStream << auxStr;   // Lineshape profile
        auxStr = loc.toString(this->params->getIntensThresh(), 'e', 6) + "\t" + "Intensity threshold\n";
        ioStream << auxStr;   // Intensity threshold
        auxStr = loc.toString(this->params->getWingContribSatHw()) + "\t" + "Line wing contribution (in saturated halfwidths)\n";
        ioStream << auxStr;   // Line wing contribution (in saturated halfwidths)
        auxStr = loc.toString(this->params->getWingContribAbs(), 'e', 6) + "\t" + "Line wing contribution (absolute, in cm-1)\n";
        ioStream << auxStr;   // Line wing contribution (absolute, in cm-1)
        auxStr = loc.toString(this->params->getLineBroadening()) + "\t" + "Broadening type\n";
        ioStream << auxStr;   // Broadening type
        auxStr = loc.toString(this->params->getUseLineShift()) + "\t" + "Line shift usage\n";
        ioStream << auxStr;   // Line shift usage
        ioStream << "\n";

        // Saving the instrument function parameters
        auxStr = loc.toString(this->instrFunct->getInstrFunctIndex()) + "\t" + "Instrument function type\n";
        ioStream << auxStr;   // Instrument function type        
        auxStr = loc.toString(this->instrFunct->getInstrResol(), 'e', 6) + "\t" + "Instrument resolution, cm-1\n";
        ioStream << auxStr;   // Instrument resolution, cm-1        
        auxStr = loc.toString(this->instrFunct->getInstrFunctWing(), 'e', 6) + "\t" + "Instrument function wing contribution, cm-1\n";
        ioStream << auxStr;   // Instrument function wing contribution, cm-1
        ioStream << "\n";

        // Saving the state of the flags indicating if sensor data input is to be used instead of manual entry
        auxStr = loc.toString(this->sensors->getHumiditySensorIsEnabled()) + "\t" + "Humidity sensor enabled/disabled\n";
        ioStream << auxStr;   // Humidity sensor enabled/disabled
        auxStr = loc.toString(this->sensors->getTotalPressureSensorIsEnabled()) + "\t" + "Total pressure sensor enabled/disabled\n";
        ioStream << auxStr;   // Total pressure sensor enabled/disabled
        auxStr = loc.toString(this->sensors->getTemperatureSensorIsEnabled()) + "\t" + "Temperature sensor enabled/disabled\n";
        ioStream << auxStr;   // Temperature sensor enabled/disabled
        ioStream << "\n";

        // Saving the state of the flag indicating if calculations are to be only performed for selected molecules
        auxStr = loc.toString(this->params->getCalcNotSelectedMols()) + "\t" + "Calculate for not selected molecules enabled/disabled\n";
        ioStream << auxStr;  // Variable indicating if molecules not selected are to be included into calculations
        ioStream << "\n";

        // Saving the molecular concentrations
        ioStream << "Molecular concentrations, ppmv\n";
        for (i = 0; i < this->params->getMol()->count(); i++)
        {
            auxStr = loc.toString(this->params->getHorizParams()->getPartPress(i), 'e', 6) + "\t" + this->params->getMolAt(i)->getChemForm() + "\n";
            ioStream << auxStr;
        }
        ioStream << "\n";

        // Saving the molecule colors
        ioStream << "Molecule colors\n";
        for (i = 0; i < this->params->getMol()->count(); i++)
        {
            auxStr = this->params->getMolAt(i)->getColor() + "\t" + this->params->getMolAt(i)->getChemForm() + "\n";
            ioStream << auxStr;
        }
        ioStream << "\n";

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

// Method to load the configuration from the configuration file
void ConfigInputOutput::loadToConfig()
{
    QFile configFile(this->configFilePath);
    QMessageBox msgBox;  // Message box to display information to the user
    QLocale loc(QLocale::English, QLocale::UnitedStates);      // Object to convert to string representatioon and back
    QString auxStr;  // Auxiliary string variable    
    int i;  // Loop variable


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


        // Reading the pathlength and pathlength units
        this->params->getHorizParams()->setPathLength(loc.toDouble((ioStream.readLine().split("\t"))[0]));  // Horizontal path length
        this->pathUnitsConvert->setUnits(loc.toUInt((ioStream.readLine().split("\t"))[0]));  // Pathlength units
        ioStream.readLine();

        // Reading the total pressure and pressure units
        this->params->getHorizParams()->setTotalPress(loc.toDouble((ioStream.readLine().split("\t"))[0]));  // Total pressure
        this->concPressUnitsConvert->setPressUnits(loc.toUInt((ioStream.readLine().split("\t"))[0]));       // Total pressure units
        this->concPressUnitsConvert->setCombinedUnits(loc.toUInt((ioStream.readLine().split("\t"))[0]));    // Concentration units
        ioStream.readLine();

        // Reading the temperature and tempearture units
        this->params->getHorizParams()->setTemp(loc.toDouble((ioStream.readLine().split("\t"))[0]));  // Horizontal path temperature
        this->tempUnitsConvert->setUnits(loc.toUInt((ioStream.readLine().split("\t"))[0]));  // Horizontal path units
        ioStream.readLine();

        // Reading the vertial axis parameters (minimum and maximum values and the units)
        this->yaxis->setMinYPlot(loc.toDouble((ioStream.readLine().split("\t"))[0]));   // Vertical axis minimum value
        this->yaxis->setMaxYPlot(loc.toDouble((ioStream.readLine().split("\t"))[0]));   // Vertical axis maximum value
        this->yaxis->setYPlotUnits(loc.toUInt((ioStream.readLine().split("\t"))[0]));   // Vertical axis units
        this->yaxis->setEnableRulers(loc.toInt((ioStream.readLine().split("\t"))[0]));  // Enable / Disable rulers on zoom
        // Cross-section vertical axis settings
        this->yaxis->setMinCrossSection(loc.toDouble((ioStream.readLine().split("\t"))[0]));   // Cross-section minimum value
        this->yaxis->setMaxCrossSection(loc.toDouble((ioStream.readLine().split("\t"))[0]));   // Cross-section maximum value
        this->yaxis->setCrossSectionAutoRecalc(loc.toInt((ioStream.readLine().split("\t"))[0]));   // Enable / Disable cross-section maximum scale auto recalculation
        // Radiance vertical axis settings
        this->yaxis->setMinRadiance(loc.toDouble((ioStream.readLine().split("\t"))[0]));   // Radiance minimum value
        this->yaxis->setMaxRadiance(loc.toDouble((ioStream.readLine().split("\t"))[0]));   // Radiance maximum value
        this->yaxis->setRadianceAutoRecalc(loc.toInt((ioStream.readLine().split("\t"))[0]));   // Enable / Disable radiance maximum scale auto recalculation

        ioStream.readLine();

        // Reading the wavelength units, spectral range and resolution
        this->wavelength->setStartWn(loc.toDouble((ioStream.readLine().split("\t"))[0])); // Minimum wavenumber, cm-1
        this->wavelength->setEndWn(loc.toDouble((ioStream.readLine().split("\t"))[0]));   // Maximum wavenumber, cm-1
        this->wavelength->setNumPoints(loc.toInt((ioStream.readLine().split("\t"))[0]));  // Number of calculation points (resolution)
        this->wavelength->setUnits(loc.toUInt((ioStream.readLine().split("\t"))[0]));     // Wavelength units
        ioStream.readLine();

        // Reading the HITRAN database access option
        this->hitranDownloader->setHitranFileUsage(loc.toUInt((ioStream.readLine().split("\t"))[0]));   // HITRAN database access option
        ioStream.readLine();

        // Reading the line options
        this->params->setLineshapeProfile(loc.toUInt((ioStream.readLine().split("\t"))[0]));    // Lineshape profile
        this->params->setIntensThresh(loc.toDouble((ioStream.readLine().split("\t"))[0]));      // Intensity threshold
        this->params->setWingContribSatHw(loc.toDouble((ioStream.readLine().split("\t"))[0]));  // Line wing contribution (in saturated halfwidths)
        this->params->setWingContribAbs(loc.toDouble((ioStream.readLine().split("\t"))[0]));    // Line wing contribution (absolute, in cm-1)
        this->params->setLineBroadening(loc.toUInt((ioStream.readLine().split("\t"))[0]));   // Broadening type
        this->params->setUseLineShift(loc.toDouble((ioStream.readLine().split("\t"))[0]));   // Line shift usage
        ioStream.readLine();

        // Reading the instrument function parameters
        this->instrFunct->setInstrFunctIndex(loc.toUInt((ioStream.readLine().split("\t"))[0]));   // Instrument function type
        this->instrFunct->setInstrResol(loc.toDouble((ioStream.readLine().split("\t"))[0]));      // Instrument resolution, cm-1
        this->instrFunct->setInstrFunctWing(loc.toDouble((ioStream.readLine().split("\t"))[0]));  // Instrument function wing contribution, cm-1
        ioStream.readLine();

        // Saving the state of the flags indicating if sensor data input is to be used instead of manual entry
        this->sensors->setEnableHumiditySensor(loc.toInt((ioStream.readLine().split("\t"))[0]));       // Humidity sensor enabled/disabled
        this->sensors->setEnableTotalPressureSensor(loc.toInt((ioStream.readLine().split("\t"))[0]));  // Total pressure sensor enabled/disabled
        this->sensors->setEnableTemperatureSensor(loc.toInt((ioStream.readLine().split("\t"))[0]));    // Temperature sensor enabled/disabled
        ioStream.readLine();

        // Saving the state of the flag indicating if calculations are to be only performed for selected molecules
        this->params->setCalcNotSelectedMols(loc.toInt((ioStream.readLine().split("\t"))[0]));   // Variable indicating if molecules not selected are to be included into calculations
        ioStream.readLine();

        // Reading in the partial pressure concentrations
        ioStream.readLine();
        for (i = 0; i < this->params->getMol()->count(); i++)
        {                
            this->params->getHorizParams()->setPartPress(i, loc.toDouble((ioStream.readLine().split("\t"))[0]));
        }
        ioStream.readLine();

        // Reading in the molecule colors
        ioStream.readLine();
        for (i = 0; i < this->params->getMol()->count(); i++)
        {
            this->params->getMolAt(i)->setColor((ioStream.readLine().split("\t"))[0]);
        }
        ioStream.readLine();

        // Closing the file after the writing has been completed
        configFile.close();
    }
    else
    {
        msgBox.setText("Error loading configuration from the configuration file.");
        msgBox.exec();
    }
}

// Method to save and load the partial pressures
void ConfigInputOutput::saveConcentrations(QString fileName)
{
    int i;  // Loop variable
    QFile configFile(fileName);
    QString auxStr;  // Auxiliary string
    QMessageBox msgBox; // Message box to display information to the user
    QLocale loc(QLocale::English, QLocale::UnitedStates);   // Object to convert to string representatioon and back


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

        // Saving the molecular concentrations
        ioStream << "Molecular concentrations, ppmv\n";
        for (i = 0; i < this->params->getMol()->count(); i++)
        {
            auxStr = loc.toString(this->params->getHorizParams()->getPartPress(i), 'e', 6) + "\t" + this->params->getMolAt(i)->getChemForm() + "\n";
            ioStream << auxStr;
        }
        ioStream << "\n";

        // Closing the file after the writing has been completed
        configFile.close();
    }
    else
    {
        msgBox.setText("Error opening the concentration file for writing.");
        msgBox.exec();
    }
}
void ConfigInputOutput::loadConcentrations(QString fileName)
{
    QFile configFile(fileName);
    QMessageBox msgBox;  // Message box to display information to the user
    QLocale loc(QLocale::English, QLocale::UnitedStates);    // Object to convert to string representatioon and back
    QString auxStr;  // Auxiliary string variable
    int i;  // Loop variable


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


        // Reading in the partial pressure concentrations
        ioStream.readLine();
        for (i = 0; i < this->params->getMol()->count(); i++)
        {
            this->params->getHorizParams()->setPartPress(i, loc.toDouble((ioStream.readLine().split("\t"))[0]));
        }
        ioStream.readLine();

        // Closing the file after the writing has been completed
        configFile.close();
    }
    else
    {
        msgBox.setText("Error loading molecular concentration parameters from the concentration file.");
        msgBox.exec();
    }
}

// Method to set references to all objects for which the configuration is to be saved or loaded
void ConfigInputOutput::setObjects(Wavelength *inWavelength,
                                   Params *inParams,
                                   Atmosphere *inAtmosphere,
                                   InstrumentFunction *inInstrFunct,
                                   HitranOnlineDownloader *inHitranDownloader,
                                   TemperatureUnitsConvert *inTempUnitsConvert,
                                   PathlengthUnitsConvert *inPathUnitsConvert,
                                   ConcPressUnitsConvert *inConcPressUnitsConvert,
                                   YAxis *inYaxis,
                                   Sensors *inSensors)
{
    wavelength = inWavelength;
    params = inParams;
    atmosphere = inAtmosphere;
    instrFunct = inInstrFunct;
    hitranDownloader = inHitranDownloader;
    tempUnitsConvert = inTempUnitsConvert;
    pathUnitsConvert = inPathUnitsConvert;
    concPressUnitsConvert = inConcPressUnitsConvert;
    yaxis = inYaxis;
    sensors = inSensors;
}
