// 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 bytran logo is distributed under the Creative Commons 3.0 Attribution License: https://creativecommons.org/licenses/by/3.0/us/
//
// 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 retrieve values from sensors (pressure, temperature, humidity)
#include "sensors.h"
#include <QMessageBox>
#include <QDebug>
#include <QSensor>
#include <QList>

Sensors::Sensors(QObject *parent, OneSlantLayer *inHorizPathParams) : QObject(parent)
{
    // Transfering in the horizontal path parameters
    horizParams = inHorizPathParams;

    // Creating the total pressure sensor object
    pressureSensor = new QPressureSensor();
    pressureSensor->setAlwaysOn(true);
    // Creating the temperature sensor object
    temperatureSensor = new QAmbientTemperatureSensor();
    temperatureSensor->setAlwaysOn(true);

    // Creating the humidity sensor
    #if QT_VERSION >= QT_VERSION_CHECK(5,9,0)
        humiditySensor = new QHumiditySensor();
        humiditySensor->setAlwaysOn(true);
    #endif

    // Initializing the accumulation time and the number of summed samples
    this->numSummedSamples = 0;
    // Values used to calculate averaged total pressure, temperature and humidity
    this->summedTotalPressure = 0;
    this->summedTemperature = 0;
    this->summedHumidity = 0;

    // Object used to convert pressure from the specified units to the default (atm)
    pressConverter = new ConcPressUnitsConvert();

    // Object used to convert temperature from specified units to the default (Kelvin)
    tempConverter = new TemperatureUnitsConvert();

    // Object to convert the humidity from the sensor units to the default units
    humidityConverter = new HumidityConvert();

    // Initializing the timer object
    timer = new QTimer(this);
    timer->start();

    int i;
    i = 0;
    while (i < QSensor::sensorTypes().count())
    {
        qDebug() << QSensor::sensorTypes().at(i);
        i++;
    }

    // Connections
    connect(this->timer, SIGNAL(timeout()), this, SLOT(getUpdatedReadings()));
    connect(this->pressureSensor, SIGNAL(activeChanged()), this, SIGNAL(barometerActiveChanged()));
    connect(this->temperatureSensor, SIGNAL(activeChanged()), this, SIGNAL(thermometerActiveChanged()));    
    #if QT_VERSION >= QT_VERSION_CHECK(5,9,0)
        connect(this->humiditySensor, SIGNAL(activeChanged()), this, SIGNAL(humiditySensorActiveChanged()));
    #endif
}

// Methods to get the last recorded values from sensors
double Sensors::getLastTotalPressure() { return this->totalPressure; }
double Sensors::getLastTemperature() { return this->temperature; }
double Sensors::getLastHumidity() { return this->humidity; }

// Methods to get and set the saved total pressure, temperature and humidity values
double Sensors::getSavedTotalPressure() { return this->savedTotalPressure; }
void Sensors::setSavedTotalPressure(double newSavedTotalPressure) { this->savedTotalPressure = newSavedTotalPressure; }
double Sensors::getSavedTemperature() { return this->savedTemperature; }
void Sensors::setSavedTemperature(double newSavedTemperature) { this->savedTemperature = newSavedTemperature; }
double Sensors::getSavedHumidity() { return this->savedHumidity; }
void Sensors::setSavedHumidity(double newSavedHumidity) { this->savedHumidity = newSavedHumidity; }

// Method to get and set the sampling period
int Sensors::getSamplingPeriod() { return timer->interval(); }
void Sensors::setSampligPeriod(int mSec) { timer->setInterval(mSec); }

// Methods to start and stop the timer (may be used to temporarily disable sample acquisition)
void Sensors::stopTimer() { timer->stop(); }
void Sensors::startTimer() { timer->start(); }

// Methods to get and set the averaging time
int Sensors::getNumSamplesToAverage() { return this->numSamplesToAverage; }
void Sensors::setNumSamplesToAverage(int number) { this->numSamplesToAverage = number; }

// Methods to enable the sensors to record data
void Sensors::setEnableTotalPressureSensor(bool enable)
{
    if (enable == true) {
        // Only do this if the pressure sensor was in an inactive state
        if (pressureSensor->isActive() == false)
        {
            this->savedTotalPressure = horizParams->getTotalPress();
            pressureSensor->start();
            pressureSensor->setActive(true);
        }
    }
    else {
        // Only do this if the sensor was previously active, otherwise the saved garbage value will be copied
        if (pressureSensor->isActive()) {
            horizParams->setTotalPress(this->savedTotalPressure);
            pressureSensor->stop();
            pressureSensor->setActive(false);
            emit totalPressureUpdated();
        }
    }
}
bool Sensors::getTotalPressureSensorIsEnabled() { return pressureSensor->isActive(); }

void Sensors::setEnableTemperatureSensor(bool enable)
{
    if (enable == true) {
        // Only do this if the sensor was in an inactive state
        if (temperatureSensor->isActive() == false)
        {
            this->savedTemperature = horizParams->getTemp();
            temperatureSensor->setActive(true);
            temperatureSensor->start();
        }
    }
    else {
        // Only do this if the sensor was previously active, otherwise the saved garbage value will be copied
        if (temperatureSensor->isActive()) {
            horizParams->setTemp(this->savedTemperature);
            temperatureSensor->stop();
            temperatureSensor->setActive(false);
            emit temperatureUpdated();
        }
    }
}
bool Sensors::getTemperatureSensorIsEnabled() { return temperatureSensor->isActive(); }

void Sensors::setEnableHumiditySensor(bool enable)
{
    // The humidity sensor segment only makes sense for Qt above 5.9 (not available prior to Qt 5.9)
    // Maybe should modify this code later to extract the humidity into a separate class or something for better looks
    #if QT_VERSION >= QT_VERSION_CHECK(5,9,0)

    if (enable == true)
    {
        // Only do this if the sensor was in an inactive state
        if (humiditySensor->isActive() == false)
        {
            this->savedHumidity = horizParams->getPartPress(0);
            humiditySensor->setActive(true);
            humiditySensor->start();
        }
    }
    else
    {
        // Only do this if the sensor was previously active, otherwise the saved garbage value will be copied
        if (humiditySensor->isActive())
        {
            horizParams->setPartPress(0, this->savedHumidity);
            humiditySensor->stop();
            humiditySensor->setActive(false);
            emit humidityUpdated();
        }
    }

    #endif
}
bool Sensors::getHumiditySensorIsEnabled()
{
    #if QT_VERSION >= QT_VERSION_CHECK(5,9,0)
        return humiditySensor->isActive();
    #else
        return false;
    #endif
}

// Slot is called to get the updated readings from the active sensors
void Sensors::getUpdatedReadings()
{
    // Getting the updated pressure reading from the pressure sensor
    if (pressureSensor->isActive())
    {
        if (this->numSummedSamples < this->numSamplesToAverage) {
            summedTotalPressure = summedTotalPressure + pressConverter->fromSpecifiedToDefaultPressUnits(3, pressureSensor->reading()->pressure());
        }
        else {
            this->totalPressure = this->summedTotalPressure / this->numSamplesToAverage;
            this->summedTotalPressure = 0;
            horizParams->setTotalPress(totalPressure);
            emit totalPressureUpdated();
        }
    }

    // Getting the updated temperature reading from the temperature sensor
    if (temperatureSensor->isActive())
    {
        if (this->numSummedSamples < this->numSamplesToAverage) {
            summedTemperature = summedTemperature + tempConverter->fromSpecifiedToCurrent(1, temperatureSensor->reading()->temperature());
        }
        else {
            this->temperature = this->summedTemperature / this->numSamplesToAverage;
            this->summedTemperature = 0;
            horizParams->setTemp(temperature);
            emit temperatureUpdated();
        }
    }

    // Humidity sensor is unavailable for Qt lower than version 5.9
    #if QT_VERSION >= QT_VERSION_CHECK(5,9,0)

    // Getting the updated humidity reading from the sensor
    if (humiditySensor->isActive())
    {
        if (this->numSummedSamples < this->numSamplesToAverage) {
            summedHumidity = summedHumidity + humidityConverter->fromRelativeHumidityToPpmv(humiditySensor->reading()->relativeHumidity(), tempConverter->fromDefaultToSpecified(1, horizParams->getTemp()), pressConverter->fromDefaultToSpecifiedPressUnits(horizParams->getTotalPress(), 3));
        }
        else
        {
            this->humidity = this->summedHumidity / this->numSamplesToAverage;
            this->summedHumidity = 0;
            horizParams->setPartPress(0, humidity);
            emit humidityUpdated();
        }
    }

    #endif

    #if QT_VERSION >= QT_VERSION_CHECK(5,9,0)

        // If at least one of the sensors is active, then updating the number of summed samples
        if ((pressureSensor->isActive() == true) || (temperatureSensor->isActive()) || (humiditySensor->isActive()))
        {
            // Reinitializing the number of summed samples
            if (this->numSummedSamples < this->numSamplesToAverage) { this->numSummedSamples++; }
                                                               else { this->numSummedSamples = 0; }
        }

    #else

        // If at least one of the sensors is active, then updating the number of summed samples
        if ((pressureSensor->isActive() == true) || (temperatureSensor->isActive()))
        {
            // Reinitializing the number of summed samples
            if (this->numSummedSamples < this->numSamplesToAverage) { this->numSummedSamples++; }
                                                               else { this->numSummedSamples = 0; }
        }

    #endif
}

// Methods to establish if one of the below sensors is present
bool Sensors::getTotalPressureSensorIsPresent()
{
    if (QSensor::sensorsForType("QPressureSensor").count() > 0) { return true; }
                                                           else { return false; }
}
bool Sensors::getTemperatureSensorIsPresent()
{
    if (QSensor::sensorsForType("QAmbientTemperatureSensor").count() > 0) { return true; }
                                                                     else { return false; }
}
bool Sensors::getHumiditySensorIsPresent()
{
    if (QSensor::sensorsForType("QHumiditySensor").count() > 0) { return true; }
                                                           else { return false; }
}

// Method determines if the usage of the humidity sensor is supported
bool Sensors::getHumiditySensorIsSupported()
{
    #if QT_VERSION >= QT_VERSION_CHECK(5,10,0)
        return true;
    #else
        return false;
    #endif
}
