/**
 * @file BNO08xRpt.cpp
 * @author Myles Parfeniuk
 */

#include "BNO08xRpt.hpp"

/**
 * @brief Enables a sensor report such that the BNO08x begins sending it.
 *
 * @param report_period_us The period/interval of the report in microseconds.
 * @param sensor_cfg Sensor special configuration (optional, see
 * BNO08xPrivateTypes::default_sensor_cfg for defaults).
 *
 * @return True if report was successfully enabled.
 */
bool BNO08xRpt::enable(uint32_t time_between_reports, sh2_SensorConfig_t sensor_cfg)
{
    int sh2_res = SH2_OK;

    EventBits_t report_en_bits = xEventGroupGetBits(*_evt_grp_rpt_en);

    sensor_cfg.reportInterval_us = time_between_reports;

    lock_sh2_HAL();
    sh2_res = sh2_setSensorConfig(ID, &sensor_cfg);
    unlock_sh2_HAL();

    if (sh2_res != SH2_OK)
    {
        return false;
    }
    else
    {
        vTaskDelay(30UL / portTICK_PERIOD_MS); // delay a bit to allow command to execute
        period_us = time_between_reports;      // update the period

        // if not already enabled (ie user called this, not re_enable_reports())
        if (!(report_en_bits & rpt_bit))
        {
            _en_report_ids->push_back(ID);                 // add report ID to enabled report IDs
            xEventGroupSetBits(*_evt_grp_rpt_en, rpt_bit); // set the event group bit
        }

        return true;
    }
}

/**
 * @brief Disables a sensor report by setting its period to 0us such that the BNO08x stops sending
 * it.
 *
 * @param sensor_ID The ID of the sensor for the respective report to be disabled.
 * @param sensor_cfg Sensor special configuration.
 *
 * @return ESP_OK if report was successfully disabled.
 */
bool BNO08xRpt::disable(sh2_SensorConfig_t sensor_cfg)
{
    int16_t idx = -1;

    if (!enable(0UL, sensor_cfg))
    {
        return false;
    }
    else
    {
        for (int i = 0; i < _en_report_ids->size(); i++)
        {
            if (_en_report_ids->at(i) == ID)
            {
                idx = i;
                break;
            }
        }

        vTaskDelay(30UL / portTICK_PERIOD_MS); // delay a bit to allow command to execute
        period_us = 0UL;                       // update the period

        if (idx != -1)
            _en_report_ids->erase(_en_report_ids->begin() + idx);

        xEventGroupClearBits(*_evt_grp_rpt_en, rpt_bit); // Set the event group bit
    }

    return true;
}

/**
 * @brief Registers a callback to execute when new data from a specific report is received.
 *
 * @param cb_fxn Pointer to the call-back function should be of void return type void input param.
 *
 * @return void, nothing to return
 */
bool BNO08xRpt::register_cb(std::function<void(void)> cb_fxn)
{
    if (_cb_list->size() < CONFIG_ESP32_BNO08X_CB_MAX)
    {
        _cb_list->push_back(BNO08xCbParamVoid(cb_fxn, ID));
        return true;
    }
    return false;
}

/**
 * @brief Checks if a new report has been received since the last time this function was called.
 *
 *
 * @return True if a new report was received since the last time this function was called.
 */
bool BNO08xRpt::has_new_data()
{
    bool new_data = false;

    if (xEventGroupGetBits(*_evt_grp_rpt_data_available) & rpt_bit)
    {
        new_data = true;
        xEventGroupClearBits(*_evt_grp_rpt_data_available, rpt_bit);
    }

    return new_data;
}

/**
 * @brief Flush all buffered reports for this sensor/report module.
 *
 * @return True if flush operation succeeded.
 */
bool BNO08xRpt::flush()
{
    int success = SH2_OK;

    lock_sh2_HAL();
    success = sh2_flush(ID);
    unlock_sh2_HAL();

    return (success != SH2_OK) ? false : true;
}

/**
 * @brief Gets sample counts for this sensor (see SH-2 ref manual 6.4.3.1)
 *
 * @param Struct to store requested data.
 *
 * @return True get counts operation succeeded.
 */
bool BNO08xRpt::get_sample_counts(bno08x_sample_counts_t& sample_counts)
{
    int success = SH2_OK;
    sh2_Counts_t pCounts;

    lock_sh2_HAL();
    success = sh2_getCounts(ID, &pCounts);
    unlock_sh2_HAL();

    if (success != SH2_OK)
    {
        return false;
    }
    else
    {
        sample_counts = pCounts;
        return true;
    }
}

/**
 * @brief Clears BNO08x internal sample counts for this sensor. (see SH-2 ref manual 6.4.3.1)
 *
 * @return True clear counts operation succeeded.
 */
bool BNO08xRpt::clear_sample_counts()
{
    int success = SH2_OK;

    lock_sh2_HAL();
    success = sh2_clearCounts(ID);
    unlock_sh2_HAL();

    return (success == SH2_OK);
}

/**
 * @brief Retrieves meta data for this sensor/report by reading respective record in FRS (flash
 * record system).
 *
 * Can be used to retrieve the minimum period, maximum period, actual Q points, resolution, and
 * other info for a given sensor.
 *
 * @return True clear get meta data operation succeeded.
 */
bool BNO08xRpt::get_meta_data(bno08x_meta_data_t& meta_data)
{
    int success = SH2_OK;

    sh2_SensorMetadata_t sensor_meta_data;

    lock_sh2_HAL();
    success = sh2_getMetadata(ID, &sensor_meta_data);
    unlock_sh2_HAL();

    if (success == SH2_OK)
        meta_data = sensor_meta_data;

    return (success == SH2_OK);
}

/**
 * @brief Locks sh2 HAL lib to only allow the calling task to call its APIs.
 *
 * @return void, nothing to return
 */
void BNO08xRpt::lock_sh2_HAL()
{
    xSemaphoreTake(*_sh2_HAL_lock, portMAX_DELAY);
}

/**
 * @brief Unlocks sh2 HAL lib to allow other tasks to call its APIs.
 *
 * @return void, nothing to return
 */
void BNO08xRpt::unlock_sh2_HAL()
{
    xSemaphoreGive(*_sh2_HAL_lock);
}

/**
 * @brief Locks locks user data to only allow the calling task to read/modify it.
 *
 * @return void, nothing to return
 */
void BNO08xRpt::lock_user_data()
{
    xSemaphoreTake(*_data_lock, portMAX_DELAY);
}

/**
 * @brief Unlocks user data to allow other tasks to read/modify it.
 *
 * @return void, nothing to return
 */
void BNO08xRpt::unlock_user_data()
{
    xSemaphoreGive(*_data_lock);
}

/**
 * @brief Signals to BNO08x::data_available() that a new report has arrived.
 *
 * @return void, nothing to return
 */
void BNO08xRpt::signal_data_available()
{
    xEventGroupSetBits(*_evt_grp_rpt_data_available, rpt_bit);
    xEventGroupSetBits(
            *_evt_grp_bno08x_task, BNO08xPrivateTypes::EVT_GRP_BNO08x_TASK_DATA_AVAILABLE);
}
