/**
 * Copyright (c) 2017 - 2020, Nordic Semiconductor ASA
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form, except as embedded into a Nordic
 *    Semiconductor ASA integrated circuit in a product or a software update for
 *    such product, 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.
 *
 * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * 4. This software, with or without modification, must only be used with a
 *    Nordic Semiconductor ASA integrated circuit.
 *
 * 5. Any software provided in binary form under this license must not be reverse
 *    engineered, decompiled, modified and/or disassembled.
 *
 * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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.
 *
 */
#ifndef CCS811_INTERNAL_H
#define CCS811_INTERNAL_H

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @brief CCS811 sensor registers.
 */
#define CCS811_REG_STATUS               0x00
#define CCS811_REG_MEAS_MODE            0x01
#define CCS811_REG_ALG_RESULT_DATA      0x02
#define CCS811_REG_RAW_DATA             0x03
#define CCS811_REG_ENV_DATA             0x05
#define CCS811_REG_NTC                  0x06
#define CCS811_REG_THRESHOLDS           0x10
#define CCS811_REG_BASELINE             0x11
#define CCS811_REG_HW_ID                0x20
#define CCS811_REG_HW_VER               0x21
#define CCS811_REG_FW_BOOT_VER          0x23
#define CCS811_REG_FW_APP_VER           0x24
#define CCS811_REG_ERROR_ID             0xE0
#define CCS811_REG_SW_RESET             0xFF
#define CCS811_REG_APP_START            0xF4

/**
 * @brief CCS811 Default configuration values.
 */
#define CCS811_DEFAULT_HUMIDITY         50
#define CCS811_DEFAULT_TEMPERATURE      25
#define CCS811_DEFAULT_LOW_THR          1500
#define CCS811_DEFAULT_HIGH_THR         2500
#define CCS811_DEFAULT_HYSTERESIS       50

/**
 * @brief CCS811 Environment temperature offset.
 */
#define CCS811_TEMPERATURE_OFFSET       25

/**
 * @brief Status register bitmasks.
 */

// Bitmasks for FW_MODE.
#define CCS811_FW_MODE_POS              7
#define CCS811_FW_MODE_MASK             (1 << CCS811_FW_MODE_POS)

// Bitmasks for APP_VALID.
#define CCS811_APP_VALID_POS            4
#define CCS811_APP_VALID_MASK           (1 << CCS811_APP_VALID_POS)

// Bitmasks for DATA_READY.
#define CCS811_DATA_READY_POS           3
#define CCS811_DATA_READY_MASK          (1 << CCS811_DATA_READY_POS)

// Bitmasks for ERROR.
#define CCS811_ERROR_POS                0
#define CCS811_ERROR_MASK               (1 << CCS811_ERROR_POS)


/**
 * @brief Meas mode register bitmasks.
 */

// Register validity mask.
#define CCS811_MEAS_MODE_VALID_MASK     0x83U

// Bitmasks for DRIVE_MODE.
#define CCS811_DRIVE_MODE_POS           4
#define CCS811_DRIVE_MODE_MASK          (7 << CCS811_DRIVE_MODE_POS)

// Bitmasks for INTERRUPT.
#define CCS811_INTERRUPT_POS            3
#define CCS811_INTERRUPT_MASK           (1 << CCS811_INTERRUPT_MASK)

// Bitmasks for THRESH.
#define CCS811_THRESH_POS               2
#define CCS811_THRESH_MASK              (1 << CCS811_THRESH_POS)

/**
 * @brief Algorithm results data bytes.
 */

#define CCS811_ALG_RESULT_BYTE_NUM      8
#define CCS811_ALG_ECO2_H_BYTE          0
#define CCS811_ALG_ECO2_L_BYTE          1
#define CCS811_ALG_TVOC_H_BYTE          2
#define CCS811_ALG_TVOC_L_BYTE          3
#define CCS811_ALG_STATUS_BYTE          4
#define CCS811_ALG_ERROR_BYTE           5
#define CCS811_ALG_RAW_DATA1_BYTE       6
#define CCS811_ALG_RAW_DATA2_BYTE       7

/**
 * @brief Environment data register.
 */

#define CCS811_ENV_HUMIDITY_H_BYTE      0

// Humidity percent position.
#define CCS811_ENV_HUM_PERCENT_POS      9

// Bitmasks for humidity fraction.
#define CCS811_ENV_HUM_FRACTION_MASK    (0x01FF)


// Temperature value position.
#define CCS811_ENV_TEMP_VALUE_POS       9

// Bitmasks for temperature fraction.
#define CCS811_ENV_TEMP_FRACTION_MASK   (0x01FF)


/**
 * @brief Error register.
 */

// Bitmasks for HEATER_SUPPLY
#define CCS811_ERROR_HEATER_SUPPLY_POS      5
#define CCS811_ERROR_HEATER_SUPPLY_MASK     (1 << CCS811_ERROR_HEATER_SUPPLY_POS)

// Bitmasks for HEATER_FAULT
#define CCS811_ERROR_HEATER_FAULT_POS       4
#define CCS811_ERROR_HEATER_FAULT_MASK      (1 << CCS811_ERROR_HEATER_FAULT_POS)

// Bitmasks for MAX_RESISTANCE
#define CCS811_ERROR_MAX_RESISTANCE_POS     3
#define CCS811_ERROR_MAX_RESISTANCE_MASK    (1 << CCS811_ERROR_MAX_RESISTANCE_POS)

// Bitmasks for MEASMODE_INVALID
#define CCS811_ERROR_MEAS_MODE_POS          2
#define CCS811_ERROR_MEAS_MODE_MASK         (1 CCS811_ERROR_MEAS_MODE_POS)

// Bitmasks for READ_REG
#define CCS811_ERROR_READ_REG_POS           1
#define CCS811_ERROR_READ_REG_MASK          (1 << CCS811_ERROR_READ_REG_POS)

// Bitmasks for WRITE_REG
#define CCS811_ERROR_WRITE_REG_POS          0
#define CCS811_ERROR_WRITE_REG_MASK         (1 << CCS811_ERROR_WRITE_REG_POS)

/**
 * @brief Software reset register.
 */
#define CCS811_SW_RESET_BYTE0           0x11
#define CCS811_SW_RESET_BYTE1           0xE5
#define CCS811_SW_RESET_BYTE2           0x72
#define CCS811_SW_RESET_BYTE3           0x8A

/**
 * @brief Structure holding sensor instance
 */
typedef struct
{
    nrf_twi_sensor_t * const p_sensor_data;
    uint8_t const            sensor_addr;
} ccs811_instance_t;

/**
 * @brief Macro that creates sensor instance.
 */
#define CCS811_INTERNAL_INSTANCE_DEF(_ccs811_inst_name, _p_twi_sensor, _sensor_address)           \
    static ccs811_instance_t _ccs811_inst_name =                                                  \
    {                                                                                             \
        .p_sensor_data = _p_twi_sensor,                                                           \
        .sensor_addr   = _sensor_address,                                                         \
    }

#ifndef SUPPRESS_INLINE_IMPLEMENTATION

__STATIC_INLINE ret_code_t ccs811_status_read(ccs811_instance_t const * p_instance,
                                              nrf_twi_sensor_reg_cb_t   user_cb,
                                              uint8_t *                 p_reg_val)
{
    ASSERT(p_instance != NULL);
    ASSERT(p_reg_val != NULL);
    return nrf_twi_sensor_reg_read(p_instance->p_sensor_data,
                                   p_instance->sensor_addr,
                                   CCS811_REG_STATUS,
                                   user_cb,
                                   p_reg_val,
                                   1);
}

__STATIC_INLINE ret_code_t ccs811_hw_id_read(ccs811_instance_t const * p_instance,
                                             nrf_twi_sensor_reg_cb_t   user_cb,
                                             uint8_t *                 p_reg_val)
{
    ASSERT(p_instance != NULL);
    ASSERT(p_reg_val != NULL);
    return nrf_twi_sensor_reg_read(p_instance->p_sensor_data,
                                   p_instance->sensor_addr,
                                   CCS811_REG_HW_ID,
                                   user_cb,
                                   p_reg_val,
                                   1);
}

__STATIC_INLINE ret_code_t ccs811_error_read(ccs811_instance_t const * p_instance,
                                             nrf_twi_sensor_reg_cb_t   user_cb,
                                             uint8_t *                 p_reg_val)
{
    ASSERT(p_instance != NULL);
    ASSERT(p_reg_val != NULL);
    return nrf_twi_sensor_reg_read(p_instance->p_sensor_data,
                                   p_instance->sensor_addr,
                                   CCS811_REG_ERROR_ID,
                                   user_cb,
                                   p_reg_val,
                                   1);
}

#endif //SUPPRESS_INLINE_IMPLEMENTATION

#ifdef __cplusplus
}
#endif

#endif // CCS811_INTERNAL_H
