// 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 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 transfer over data from the sensors C++ class to/from the QML interface
#include "sensorsqml.h"

SensorsQml::SensorsQml(QObject *parent, BytranCore *inCore) : QObject(parent)
{
    // Transferring a reference to the core object to the sensors data object
    core = inCore;

    // So that commas are not inserted into numbers
    loc.setNumberOptions(QLocale::OmitGroupSeparator);

    // Getting the enabled states for sensors
    m_totalPressureSensorEnabled = core->getSensors()->getTotalPressureSensorIsEnabled();
    m_temperatureSensorEnabled = core->getSensors()->getTemperatureSensorIsEnabled();
    m_humiditySensorEnabled = core->getSensors()->getHumiditySensorIsEnabled();
    m_useSensorTag = core->getSensors()->getUseSensorTag();

    // Passing C++ variable into the QML data transfer class
    this->m_selectedDeviceIndex = core->getSensors()->getSensorTag()->getBluetooth()->getBluetoothDevicesDiscover()->getSelectedDeviceIndex();
    this->m_sensorTagConnected = core->getSensors()->getSensorTag()->getSensorTagConnected();
    this->m_tempMode = core->getSensors()->getSensorTag()->getTempMode();

    // Saving the sampling period and the number of data points to sample
    this->m_samplingPeriod = core->getSensors()->getSamplingPeriod();
    this->m_numSamplesToAverage = core->getSensors()->getNumSamplesToAverage();

    // Connecting a sensor update signal
    connect(this->core->getSensors(), SIGNAL(totalPressureUpdated()), this, SLOT(updateTotalPressureValue()));
    connect(this->core->getSensors(), SIGNAL(temperatureUpdated()), this, SLOT(updateTemperatureValue()));
    connect(this->core->getSensors(), SIGNAL(humidityUpdated()), this, SLOT(updateHumidityValue()));    
    connect(this->core->getSensors(), SIGNAL(totalPressureUpdated()), this, SIGNAL(totalPressureSensorEnabledChanged()));
    connect(this->core->getSensors(), SIGNAL(temperatureUpdated()), this, SIGNAL(temperatureSensorEnabledChanged()));
    connect(this->core->getSensors(), SIGNAL(humidityUpdated()), this, SIGNAL(humiditySensorEnabledChanged()));
    connect(this->core->getSensors(), SIGNAL(barometerActiveChanged()), this, SLOT(updateBarometerActiveState()));
    connect(this->core->getSensors(), SIGNAL(thermometerActiveChanged()), this, SLOT(updateThermometerActiveState()));
    connect(this->core->getSensors(), SIGNAL(humiditySensorActiveChanged()), this, SLOT(updateHumiditySensorActiveState()));
    // All of these connections are to update the interface when the SensorTag is conected or enabled
    connect(this->core->getSensors()->getSensorTag(), SIGNAL(sensorTagConnectedChanged()), this, SIGNAL(totalPressureSensorIsPresentChanged()));
    connect(this->core->getSensors()->getSensorTag(), SIGNAL(sensorTagConnectedChanged()), this, SIGNAL(temperatureSensorIsPresentChanged()));
    connect(this->core->getSensors()->getSensorTag(), SIGNAL(sensorTagConnectedChanged()), this, SIGNAL(humiditySensorIsPresentChanged()));
    connect(this->core->getSensors()->getSensorTag(), SIGNAL(sensorTagConnectedChanged()), this, SIGNAL(humiditySensorIsSupportedChanged()));
    connect(this->core->getSensors(), SIGNAL(useSensorTagStateChanged()), this, SIGNAL(humiditySensorLabelTextChanged()));
    connect(this->core->getSensors(), SIGNAL(useSensorTagStateChanged()), this, SIGNAL(temperatureSensorLabelTextChanged()));
    connect(this->core->getSensors(), SIGNAL(useSensorTagStateChanged()), this, SIGNAL(totalPressSensorLabelTextChanged()));
    connect(this->core->getSensors(), SIGNAL(useSensorTagStateChanged()), this, SIGNAL(useSensorTagChanged()));
    connect(this->core->getSensors()->getSensorTag(), SIGNAL(sensorTagConnectedChanged()), this, SIGNAL(humiditySensorLabelTextChanged()));
    connect(this->core->getSensors()->getSensorTag(), SIGNAL(sensorTagConnectedChanged()), this, SIGNAL(temperatureSensorLabelTextChanged()));
    connect(this->core->getSensors()->getSensorTag(), SIGNAL(sensorTagConnectedChanged()), this, SIGNAL(totalPressSensorLabelTextChanged()));
    connect(this->core->getSensors()->getSensorTag(), SIGNAL(sensorTagConnectedChanged()), this, SIGNAL(connectButtonTextChanged()));
    // Updates to the values using the remote control
    connect(this->core, SIGNAL(valuesUpdated()), this, SIGNAL(samplingPeriodChanged()));
    connect(this->core, SIGNAL(valuesUpdated()), this, SIGNAL(numSamplesToAverageChanged()));
}

// Methods set the variable indicating if the sesnor tag is to be used (on/off)
bool SensorsQml::useSensorTag() const { return this->core->getSensors()->getUseSensorTag(); }
void SensorsQml::setUseSensorTag(const bool &useSensorTag)
{
    if (useSensorTag != m_useSensorTag)
    {
        m_useSensorTag = useSensorTag;
        this->core->getSensors()->setUseSensorTag(m_useSensorTag);
        emit useSensorTagChanged();
    }
}

QString SensorsQml::totalPressure() const
{
    double totalPressAtm;        // Total pressure in default internal units (atm)
    double totalPressCurrUnits;  // Total pressure in current pressure units

    totalPressAtm = core->getParams()->getHorizParams()->getTotalPress();
    totalPressCurrUnits = core->getConcPressUnitsConverter()->fromDefaultToCurrentPressUnits(totalPressAtm);
    return loc.toString(totalPressCurrUnits, 'g', 6) + " (" + core->getConcPressUnitsConverter()->getCurrPressUnitsAbbrevStr() + ")";
}
QString SensorsQml::temperature() const
{
    double tempK;          // Temperature in Kelvin units
    double tempCurrUnits;  // Temperature in current units

    tempK = core->getParams()->getHorizParams()->getTemp();
    tempCurrUnits = core->getTemperatureUnitsConverter()->fromDefaultToCurrent(tempK);
    return loc.toString(tempCurrUnits, 'g', 6) + " (" + core->getTemperatureUnitsConverter()->getCurrTempUnitsAbbrevStr() + ")";
}
QString SensorsQml::humidity() const
{
    QString concStr = "0";
    double h2oPpmv;  // H2O concentration in PPMV
    double conc;     // Concentration value, %

    // Updating the concentration values to be displayed
    h2oPpmv = core->getParams()->getHorizParams()->getPartPress(0);
    conc = core->getConcPressUnitsConverter()->fromDefaultToCurrentConcUnits(h2oPpmv, core->getParams()->getHorizParams()->getTotalPress());
    concStr = loc.toString(conc, 'g', 5);

    return concStr + " (" + core->getConcPressUnitsConverter()->getCurrCombinedUnitsAbbrevStr() + ")";
}

int SensorsQml::samplingPeriod() const { return core->getSensors()->getSamplingPeriod(); }
void SensorsQml::setSamplingPeriod(int const samplingPeriod)
{
    if (samplingPeriod != this->m_samplingPeriod)
    {
        m_samplingPeriod = samplingPeriod;
        core->getSensors()->setSampligPeriod(m_samplingPeriod);
        emit samplingPeriodChanged();
        emit averagingTimeChanged();
    }
}
int SensorsQml::numSamplesToAverage() const { return core->getSensors()->getNumSamplesToAverage(); }
void SensorsQml::setNumSamplesToAverage(int const numSamplesToAverage)
{
    if (numSamplesToAverage != this->m_numSamplesToAverage)
    {
        m_numSamplesToAverage = numSamplesToAverage;
        core->getSensors()->setNumSamplesToAverage(m_numSamplesToAverage);
        emit numSamplesToAverageChanged();
        emit averagingTimeChanged();
    }
}
int SensorsQml::averagingTime() const
{
    int avgTime;
    avgTime = this->m_samplingPeriod * this->m_numSamplesToAverage;
    return avgTime;
}

bool SensorsQml::totalPressureSensorEnabled() const { return core->getSensors()->getTotalPressureSensorIsEnabled(); }
void SensorsQml::setTotalPressureSensorEnabled(const bool totalPressureSensorEnabled)
{
    if (totalPressureSensorEnabled != m_totalPressureSensorEnabled)
    {
        m_totalPressureSensorEnabled = totalPressureSensorEnabled;
        core->getSensors()->setEnableTotalPressureSensor(m_totalPressureSensorEnabled);
        emit totalPressureSensorEnabledChanged();
    }
}
bool SensorsQml::temperatureSensorEnabled() const { return core->getSensors()->getTemperatureSensorIsEnabled(); }
void SensorsQml::setTemperatureSensorEnabled(const bool temperatureSensorEnabled)
{
    if (temperatureSensorEnabled != m_temperatureSensorEnabled)
    {
        m_temperatureSensorEnabled = temperatureSensorEnabled;
        core->getSensors()->setEnableTemperatureSensor(m_temperatureSensorEnabled);
        emit temperatureSensorEnabledChanged();
    }
}
bool SensorsQml::humiditySensorEnabled() const { return core->getSensors()->getHumiditySensorIsEnabled(); }
void SensorsQml::setHumiditySensorEnabled(const bool humiditySensorEnabled)
{
    if (humiditySensorEnabled != m_humiditySensorEnabled)
    {
        m_humiditySensorEnabled = humiditySensorEnabled;
        core->getSensors()->setEnableHumiditySensor(m_humiditySensorEnabled);
        emit humiditySensorEnabledChanged();
    }
}

// Private slot to update the sensor values
void SensorsQml::updateTotalPressureValue() { emit totalPressureChanged(); }
void SensorsQml::updateTemperatureValue() { emit temperatureChanged(); }
void SensorsQml::updateHumidityValue() { emit humidityChanged(); }

// Private slots to update the sensor active states
void SensorsQml::updateBarometerActiveState() { emit totalPressureSensorEnabledChanged(); }
void SensorsQml::updateThermometerActiveState() { emit temperatureSensorEnabledChanged(); }
void SensorsQml::updateHumiditySensorActiveState() { emit humiditySensorEnabledChanged(); }

// Methods to establish if one of the below sensors is present
bool SensorsQml::totalPressureSensorIsPresent() const { return core->getSensors()->getTotalPressureSensorIsPresent(); }
bool SensorsQml::temperatureSensorIsPresent() const { return core->getSensors()->getTemperatureSensorIsPresent(); }
bool SensorsQml::humidityBuiltInSensorIsPresent() const { return core->getSensors()->getSensorsCellPhone()->getHumiditySensorIsPresent(); }

// Methods to provide the text to be assigned to the auxiliary labels
// Auxiliary humidity sensor comment label text to be displayed when sensors are not available or not enabled
QString SensorsQml::humiditySensorLabelText() const
{
    if (core->getSensors()->getSenseHat()->getSenseHatIsPresent() == true) { return ""; }
    if (core->getSensors()->getSensorsCellPhone()->getHumiditySensorIsPresent() == false)
    {
        if (core->getSensors()->getSensorTag()->getSensorTagConnected())
        {
            if (core->getSensors()->getUseSensorTag())
                return "";
            else
                return tr(" SensorTag is connected\n but not enabled");
        }
        else { return tr(" No humidity sensors\n (manual value used)"); }
    }
    else { return ""; }
}
// Auxiliary temperature comment label text to be displayed when sensors are not available or not enabled
QString SensorsQml::temperatureSensorLabelText() const
{
    if (core->getSensors()->getSenseHat()->getSenseHatIsPresent() == true) { return ""; }
    if (core->getSensors()->getSensorsCellPhone()->getTemperatureSensorIsPresent() == false)
    {
        if (core->getSensors()->getSensorTag()->getSensorTagConnected())
        {
            if (core->getSensors()->getUseSensorTag())
               { return ""; }
            else
               { return tr(" SensorTag is connected\n but not enabled"); }
        }
        else { return tr(" No thermometer found\n (manual value used)"); }
    }
    else { return ""; }
}
// Auxiliary total pressure comment label text to be displayed when sensors are not available or not enabled
QString SensorsQml::totalPressSensorLabelText() const
{
    if (core->getSensors()->getSenseHat()->getSenseHatIsPresent() == true) { return ""; }
    if (core->getSensors()->getSensorsCellPhone()->getTotalPressureSensorIsPresent() == false)
    {
        if (core->getSensors()->getSensorTag()->getSensorTagConnected())
        {
            if (core->getSensors()->getUseSensorTag())
                return "";
            else
                return tr(" SensorTag is connected\n but not enabled");
        }
        else { return tr(" No barometer detected\n (manual value used)"); }
    }
    else { return ""; }
}

// Method to connect to a SensorTag device
void SensorsQml::connectSensorTagDevice()
{
    if (core->getSensors()->getSensorTag()->getSensorTagConnected() == false) { core->getSensors()->connectSensorTagDevice(); }
                                                                         else { core->getSensors()->disconnectSensorTagDevice(); }
}

// Method notifies if the sensor tag is connected
bool SensorsQml::sensorTagConnected() const { return core->getSensors()->getSensorTag()->getSensorTagConnected(); }

// Temperature sensor mode (HDC1000 value, BPM280 value, or their average value)
int SensorsQml::tempMode() const { return this->m_tempMode; }
void SensorsQml::setTempMode(const int &tempMode)
{
    if (tempMode != m_tempMode)
    {
        m_tempMode = tempMode;
        core->getSensors()->getSensorTag()->setTempMode(m_tempMode);
        emit tempModeChanged();
    }
}

// Setting the selected device index
int SensorsQml::selectedDeviceIndex() const { return this->m_selectedDeviceIndex; }
void SensorsQml::setSelectedDeviceIndex(const int &selectedDeviceIndex)
{
    if (selectedDeviceIndex != m_selectedDeviceIndex)
    {
        m_selectedDeviceIndex = selectedDeviceIndex;
        core->getSensors()->getSensorTag()->getBluetooth()->getBluetoothDevicesDiscover()->setSelectedDeviceIndex(m_selectedDeviceIndex);
        emit selectedDeviceIndexChanged();
    }
}

// Method to discover available bluetooth devices
void SensorsQml::discoverDevices()
{
    // Discovering available devices
    core->getSensors()->getSensorTag()->getBluetooth()->getBluetoothDevicesDiscover()->discoverDevices(true);
}

// Text of the bluetooth low energy connect button
QString SensorsQml::connectButtonText() const
{
    if (core->getSensors()->getSensorTag()->getSensorTagConnected() == true)
        return " Disconnect ";
    else
        return "  Connect  ";
}
