// 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
//
// Sensor tag technical information
// http://processors.wiki.ti.com/index.php/SensorTag_User_Guide
// http://processors.wiki.ti.com/index.php/CC2650_SensorTag_User%27s_Guide#Humidity_Sensor
// https://doc.qt.io/qt-5.10/qtbluetooth-heartrate-game-example.html
//
// 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 is used to get the sensor data from the SensorTag device
#include "sensortag.h"
#include "QThread"
#include <QLowEnergyCharacteristicData>
#include <QLowEnergyServiceData>
#include <QLowEnergyDescriptorData>

SensorTag::SensorTag(QObject *parent) : QObject(parent)
{
    // Initializing the default variables
    this->sensorTagConnected = false;
    emit sensorTagConnectedChanged();

    // Object to connect to a bluetooth device
    bluetoothLowEnergy = new BluetoothLowEnergy();

    // Default temperature mode;
    tempMode = 2;

    // Connecting signals to process the bluetooth response recieved
    connect(this->bluetoothLowEnergy, SIGNAL(servicesDiscovered()), this, SLOT(servicesDiscovered()));
    connect(this->bluetoothLowEnergy, SIGNAL(characteristicsDiscovered(QLowEnergyService*)), this, SLOT(serviceCharacteristicsDiscovered(QLowEnergyService*)));
    connect(this->bluetoothLowEnergy, SIGNAL(disconnected()), SLOT(setSensorTagDisconnected()));
}

// Method to get access to the bluetooth data object
BluetoothLowEnergy *SensorTag::getBluetooth() { return this->bluetoothLowEnergy; }

bool SensorTag::getSensorTagConnected() { return this->sensorTagConnected; }
void SensorTag::setSensorTagDisconnected()
{
    this->sensorTagConnected = false;
    emit sensorTagConnectedChanged();
}

// Methods to get the last recorded values from sensors
double SensorTag::getTotalPressure() { return this->totalPressure; }
double SensorTag::getTemperature()
{         
         if (tempMode == 0) { return this->temperatureHDC1000; }
    else if (tempMode == 1) { return this->temperatureBMP280; }
    else if (tempMode == 2) { return (this->temperatureBMP280 + this->temperatureHDC1000) * 0.5; }
                       else { return (this->temperatureBMP280 + this->temperatureHDC1000) * 0.5; }
}
double SensorTag::getHumidity() { return this->humidity; }

// Getting and setting the temperature mode
int SensorTag::getTempMode() { return this->tempMode; }
void SensorTag::setTempMode(int newTempMode) { this->tempMode = newTempMode; }

// Update frequency methods
void SensorTag::setSamplingPeriod(int newSamplingPeriod)
{
    quint8 value;
    QByteArray byteArray;


    if (sensorTagConnected == true)
    {
        // Converting to a 1 byte representation
        byteArray.resize(0);
        value = newSamplingPeriod * 0.1;
        if (value >= 255) { value = 255; }
        if (value < 10)  { value = 10; }
        byteArray[0] = value;

        // Setting the update rate (in msec)
        this->humidityService->writeCharacteristic(humidityService->characteristic(this->samplingPeriodHumidUuid), byteArray);
        this->pressService->writeCharacteristic(pressService->characteristic(this->samplingPeriodPressUuid), byteArray);
    }
}

// Method to connect the Sensor Tag and enable the data acquisition
void SensorTag::connectToDevice()
{
    // Connected the currently selected device
    bluetoothLowEnergy->discoverServicesForSelectedDevice();
}
void SensorTag::disconnectFromDevice()
{
    // Disconnecting from the bluetooth device
    bluetoothLowEnergy->disconnectFromDevice();
}

// Slot is used to connect to a given service after the service discovery has been completed
void SensorTag::servicesDiscovered()
{
    // Performing the discovery and activation of the Humidity sensor
    bluetoothLowEnergy->connectToServiceWithUuidContaining("AA20");

    // Performing the discovery and activation for the Pressure sensor
    bluetoothLowEnergy->connectToServiceWithUuidContaining("AA40");

    // Setting a variable indicating that the device has been connected
    this->sensorTagConnected = true;
    emit sensorTagConnectedChanged();
}

// Slot is used when the service characteristics have been discovered
void SensorTag::serviceCharacteristicsDiscovered(QLowEnergyService *service)
{
    int i;  // Loop variable
    QBluetoothUuid valueUuid;   // Uuid of the characteristic containing the value to read in
    QBluetoothUuid enableUuid;  // Uuid of the characteristic containing the flag to enable the sonsor
    QBluetoothUuid freqUuid;    // Uuid of the characteristic containing the flag to set the update frequency

    // Saving the service pointer
    this->activeService = service;

    // Reading in the humidity, total pressure and temperature values
    if (this->activeService->serviceUuid().toString().toUpper().contains("AA20"))
    {
        // Saving a reference to the humidity service
        this->humidityService = this->activeService;

        // Finding the characteristic indexes in the list
        for (i = 0; i < this->activeService->characteristics().count(); i++)
        {
            // Humidity sensor
            if (this->activeService->characteristics().at(i).uuid().toString().toUpper().contains("AA21")) {
                valueUuid = this->activeService->characteristics().at(i).uuid();
                this->valueHumidUuid = valueUuid;
            }
            if (this->activeService->characteristics().at(i).uuid().toString().toUpper().contains("AA22")) {
                enableUuid = this->activeService->characteristics().at(i).uuid();
                this->enableHumidUuid = enableUuid;
            }
            if (this->activeService->characteristics().at(i).uuid().toString().toUpper().contains("AA23")) {
                freqUuid = this->activeService->characteristics().at(i).uuid();
                this->samplingPeriodHumidUuid = freqUuid;
            }
        }
    }

    // Pressure sensor
    if (this->activeService->serviceUuid().toString().toUpper().contains("AA40"))
    {
        // Saving a reference to the barometric total pressure service
        this->pressService = this->activeService;

        // Finding the characteristic indexes in the list
        for (i = 0; i < this->activeService->characteristics().count(); i++)
        {
            // Pressure sensor
            if (this->activeService->characteristics().at(i).uuid().toString().toUpper().contains("AA41")) {
                valueUuid = this->activeService->characteristics().at(i).uuid();
                this->valuePressUuid = valueUuid;
            }
            if (this->activeService->characteristics().at(i).uuid().toString().toUpper().contains("AA42")) {
                enableUuid = this->activeService->characteristics().at(i).uuid();
                this->enablePressUuid = enableUuid;
            }
            if (this->activeService->characteristics().at(i).uuid().toString().toUpper().contains("AA44")) {
                freqUuid = this->activeService->characteristics().at(i).uuid();
                this->samplingPeriodPressUuid = freqUuid;
            }
        }
    }

    // Enabling the humidity / temperature sensor
    connect(this->activeService, SIGNAL(stateChanged(QLowEnergyService::ServiceState)), this, SLOT(onStateChanged(QLowEnergyService::ServiceState)));
    connect(this->activeService, SIGNAL(characteristicChanged(QLowEnergyCharacteristic, QByteArray)), this, SLOT(onCharacteristicChanged(QLowEnergyCharacteristic, QByteArray)));
    connect(this->activeService, SIGNAL(characteristicWritten(QLowEnergyCharacteristic, QByteArray)), this, SLOT(onCharacteristicWritten(QLowEnergyCharacteristic, QByteArray)));

    const QLowEnergyCharacteristic stChar = activeService->characteristic(valueUuid);
    if (!stChar.isValid()) { ; }

    // Enabling notifications
    notificationDesc = stChar.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration);
    if (notificationDesc.isValid()) { this->activeService->writeDescriptor(notificationDesc, QByteArray::fromHex("0100")); }

    // Setting the frequency for the sensor

    // Enabling the sensor
    this->activeService->writeCharacteristic(activeService->characteristic(enableUuid), QByteArray::fromHex("01"));
}

// Slot is called when a characteristic is changed
void SensorTag::onCharacteristicChanged(QLowEnergyCharacteristic characteristic, QByteArray value)
{
    quint16 rawTempHDC1000 = 0; // Integer representation of temperature (raw read in value)
    quint16 rawHumid = 0;       // Integer representation of humidity (raw read in value)
    quint32 rawTotalPress = 0;  // Integer representation of total pressure (raw read in value) in Pa
    quint32 rawTempBMP280 = 0;  // Temperature measured from the BMP280 Barometric sensor in Celcius
    bool ok;  // Variable indicating if the conversion has been carried oout successfully
    QByteArray auxByteArray;    // Auxiliary byte array


    // Humidity sensor which also supplies the temperature data
    // http://processors.wiki.ti.com/index.php/CC2650_SensorTag_User%27s_Guide#Humidity_Sensor
    if (characteristic.uuid() == valueHumidUuid)
    {
        // Obtaining the raw temperature measurement from the HDC1000 sensor
        auxByteArray.resize(2);
        auxByteArray[0] = value[1];
        auxByteArray[1] = value[0];
        rawTempHDC1000 = auxByteArray.toHex().toInt(&ok, 16);
        // Obtaining the raw humidity measurement
        auxByteArray.resize(2);
        auxByteArray[0] = value[3];
        auxByteArray[1] = value[2];
        rawHumid = auxByteArray.toHex().toInt(&ok, 16);

        // Converting the temperature into Celsius units
        this->temperatureHDC1000 = ((double)(quint16) rawTempHDC1000 / 65536) * 165 - 40;

        // Converting the humidity into Relative humidity %
        rawHumid &= ~0x0003;  // Removing status bits
        this->humidity = ((double)rawHumid / 65536)*100;
    }

    // Barometric total pressure
    // http://processors.wiki.ti.com/index.php/CC2650_SensorTag_User%27s_Guide#Barometric_Pressure_Sensor
    if (characteristic.uuid() == valuePressUuid)
    {
        // Obtaining the raw temperature measurement from the BMP280 sensor
        auxByteArray.resize(4);
        auxByteArray[0] = 0;
        auxByteArray[1] = value[2];
        auxByteArray[2] = value[1];
        auxByteArray[3] = value[0];
        rawTempBMP280 = auxByteArray.toHex().toInt(&ok, 16);
        // Obtaining the barometric total pressure
        auxByteArray.resize(4);
        auxByteArray[0] = 0;
        auxByteArray[1] = value[5];
        auxByteArray[2] = value[4];
        auxByteArray[3] = value[3];
        rawTotalPress = auxByteArray.toHex().toInt(&ok, 16);

        // Obtaining the temperature value in the units of Celsius
        this->temperatureBMP280 = rawTempBMP280 / 100.0f;

        // Obtaining the total pressure in the units of Pa
        this->totalPressure = rawTotalPress;
    }
}

void SensorTag::onStateChanged(QLowEnergyService::ServiceState state)
{
    ;
}

void SensorTag::onCharacteristicWritten(QLowEnergyCharacteristic characteristic, QByteArray array)
{
    ;
}
