/**
 * Copyright (C) 2017 - 2018 Bosch Sensortec GmbH
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * Neither the name of the copyright holder nor the names of the
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
 * OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 * OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
 *
 * The information provided is believed to be accurate and reliable.
 * The copyright holder assumes no responsibility
 * for the consequences of use
 * of such information nor for any infringement of patents or
 * other rights of third parties which may result from its use.
 * No license is granted by implication or otherwise under any patent or
 * patent rights of the copyright holder.
 *
 * @file	bsec.h
 * @date	31 Jan 2018
 * @version	1.0
 *
 */

#ifndef BSEC_CLASS_H
#define BSEC_CLASS_H

/* Includes */
#include "Arduino.h"
#include "Wire.h"
#include "SPI.h"
#include "inc/bsec_datatypes.h"
#include "inc/bsec_interface.h"
#include "bme680/bme680.h"

/* BSEC class definition */
class Bsec
{
public:
	/* Public variables */
	bsec_version_t version;		// Stores the version of the BSEC algorithm
	int64_t nextCall;			// Stores the time when the algorithm has to be called next in ms
	int8_t bme680Status;		// Placeholder for the BME680 driver's error codes
	bsec_library_return_t status;
	float iaqEstimate, rawTemperature, pressure, rawHumidity, gasResistance, stabStatus, runInStatus, temperature, humidity,
	      staticIaq, co2Equivalent, breathVocEquivalent, compGasValue, gasPercentage;
	uint8_t iaqAccuracy, staticIaqAccuracy, co2Accuracy, breathVocAccuracy, compGasAccuracy, gasPercentageAcccuracy;
	int64_t outputTimestamp;	// Timestamp in ms of the output
	static TwoWire *wireObj;
	static SPIClass *spiObj;

	/* Public APIs */
	/**
	 * @brief Constructor
	 */
	Bsec();

	/**
	 * @brief Function to initialize the BSEC library and the BME680 sensor
	 * @param devId		: Device identifier parameter for the read/write interface functions
	 * @param intf		: Physical communication interface
	 * @param read		: Pointer to the read function
	 * @param write		: Pointer to the write function
	 * @param idleTask	: Pointer to the idling task
	 */
	void begin(uint8_t devId, enum bme680_intf intf, bme680_com_fptr_t read, bme680_com_fptr_t write, bme680_delay_fptr_t idleTask);
	
	/**
	 * @brief Function to initialize the BSEC library and the BME680 sensor
	 * @param i2cAddr	: I2C address
	 * @param i2c		: Pointer to the TwoWire object
	 */
	void begin(uint8_t i2cAddr, TwoWire &i2c);
	
	/**
	 * @brief Function to initialize the BSEC library and the BME680 sensor
	 * @param chipSelect	: SPI chip select
	 * @param spi			: Pointer to the SPIClass object
	 */
	void begin(uint8_t chipSelect, SPIClass &spi);

	/**
	 * @brief Function that sets the desired sensors and the sample rates
	 * @param sensorList	: The list of output sensors
	 * @param nSensors		: Number of outputs requested
	 * @param sampleRate	: The sample rate of requested sensors
	 */
	void updateSubscription(bsec_virtual_sensor_t sensorList[], uint8_t nSensors, float sampleRate = BSEC_SAMPLE_RATE_ULP);

	/**
	 * @brief Callback from the user to trigger reading of data from the BME680, process and store outputs
	 * @return true if there are new outputs. false otherwise
	 */
	bool run(void);

	/**
	 * @brief Function to get the state of the algorithm to save to non-volatile memory
	 * @param state			: Pointer to a memory location that contains the state
	 */
	void getState(uint8_t *state);

	/**
	 * @brief Function to set the state of the algorithm from non-volatile memory
	 * @param state			: Pointer to a memory location that contains the state
	 */
	void setState(uint8_t *state);

	/**
	 * @brief Function to set the configuration of the algorithm from memory
	 * @param state			: Pointer to a memory location that contains the configuration
	 */
	void setConfig(const uint8_t *config);

	/**
	 * @brief Function to set the temperature offset
	 * @param tempOffset	: Temperature offset in degree Celsius
	 */
	void setTemperatureOffset(float tempOffset)
	{
		_tempOffset = tempOffset;
	}
	
		
	/**
	 * @brief Function to calculate an int64_t timestamp in milliseconds
	 */
	int64_t getTimeMs(void);
	
	/**
	* @brief Task that delays for a ms period of time
	* @param period	: Period of time in ms
	*/
	static void delay_ms(uint32_t period);

	/**
	* @brief Callback function for reading registers over I2C
	* @param devId		: Library agnostic parameter to identify the device to communicate with
	* @param regAddr	: Register address
	* @param regData	: Pointer to the array containing the data to be read
	* @param length	: Length of the array of data
	* @return	Zero for success, non-zero otherwise
	*/
	static int8_t i2cRead(uint8_t devId, uint8_t regAddr, uint8_t *regData, uint16_t length);

	/**
	* @brief Callback function for writing registers over I2C
	* @param devId		: Library agnostic parameter to identify the device to communicate with
	* @param regAddr	: Register address
	* @param regData	: Pointer to the array containing the data to be written
	* @param length	: Length of the array of data
	* @return	Zero for success, non-zero otherwise
	*/
	static int8_t i2cWrite(uint8_t devId, uint8_t regAddr, uint8_t *regData, uint16_t length);

	/**
	* @brief Callback function for reading and writing registers over SPI
	* @param devId		: Library agnostic parameter to identify the device to communicate with
	* @param regAddr	: Register address
	* @param regData	: Pointer to the array containing the data to be read or written
	* @param length	: Length of the array of data
	* @return	Zero for success, non-zero otherwise
	*/
	static int8_t spiTransfer(uint8_t devId, uint8_t regAddr, uint8_t *regData, uint16_t length);

private:
	/* Private variables */
	struct bme680_dev _bme680;
	struct bme680_field_data _data;
	float _tempOffset;
	// Global variables to help create a millisecond timestamp that doesn't overflow every 51 days.
	// If it overflows, it will have a negative value. Something that should never happen.
	uint32_t millisOverflowCounter;
	uint32_t lastTime;

	/* Private APIs */
	/**
	 * @brief Get the version of the BSEC library
	 */
	void getVersion(void);

	/**
	 * @brief Read data from the BME680 and process it
	 * @param currTimeNs: Current time in ns
	 * @param bme680Settings: BME680 sensor's settings
	 * @return true if there are new outputs. false otherwise
	 */
	bool readProcessData(int64_t currTimeNs, bsec_bme_settings_t bme680Settings);

	/**
	 * @brief Set the BME680 sensor's configuration
	 * @param bme680Settings: Settings to configure the BME680
	 * @return BME680 return code. BME680_OK for success, failure otherwise
	 */
	int8_t setBme680Config(bsec_bme_settings_t bme680Settings);

	/**
	 * @brief Common code for the begin function
	 */
	void beginCommon(void);

	/**
	 * @brief Function to zero the outputs
	 */
	void zeroOutputs(void);
};

#endif
