#include "tkl_adc.h"
#include "tkl_memory.h"
#include "tkl_output.h"
#include "tuya_error_code.h"

#include "gpio_pub.h"
#include "saradc_pub.h"
#include "BkDriverGpio.h"
#include "FreeRTOS.h"
#include "task.h"

/*============================ MACROS ========================================*/
#define ADC_DEV_NUM         1
#define ADC_DEV_CHANNEL_SUM 6

#define ADC_REGISTER_VAL_MAX  4096
#define ADC_VOLTAGE_MAX  3600   //mv
#define ADC_BUF_SIZE_MIN  15
#define ADC_BUF_SIZE_MAX  200



static saradc_desc_t adc_desc = {0};  //ADC结构体
static unsigned char g_adc_init[ADC_DEV_CHANNEL_SUM] = {FALSE};
static unsigned char adc_ch_nums = 0;   // 实际使用的通道数
static unsigned short read_adc_buf[ADC_BUF_SIZE_MAX];  //最大read buf size 200


/**
 * @brief tuya kernel adc init
 * 
 * @param[in] unit_num: adc unit number
 * @param[in] cfg: adc config
 *
 * @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
 */
OPERATE_RET tkl_adc_init(TUYA_ADC_NUM_E port_num, TUYA_ADC_BASE_CFG_T *cfg)
{
    unsigned char i = 0;
    unsigned char channel = 0;

    // cfg->ch_list channel number start from 0
    if ((port_num > ADC_DEV_NUM-1) || (cfg->ch_nums > ADC_DEV_CHANNEL_SUM) || (cfg->ch_list == NULL)) {
        tkl_log_output("error port num: %d:%d\r\n", port_num, __LINE__);
        return OPRT_INVALID_PARM;
    }     

    memset(g_adc_init, 0x00, ADC_DEV_CHANNEL_SUM);
    memset(&adc_desc, 0x00, sizeof(adc_desc));
    
    if (TUYA_ADC_SINGLE == cfg->mode) {
        adc_desc.mode = (ADC_CONFIG_MODE_STEP << 0)
                        | (ADC_CONFIG_MODE_4CLK_DELAY << 2);
    } else if (TUYA_ADC_CONTINUOUS == cfg->mode) {
        adc_desc.mode = (ADC_CONFIG_MODE_CONTINUE << 0)
                        | (ADC_CONFIG_MODE_4CLK_DELAY << 2)
                        | (ADC_CONFIG_MODE_SHOULD_OFF);
    } else {
        return OPRT_INVALID_PARM;
    }
    adc_desc.data_buff_size = cfg->conv_cnt;
    adc_desc.pre_div = 8;
    adc_desc.samp_rate = 0x20;  // bk advise not to change
    adc_desc.p_Int_Handler = saradc_disable;
    
    for (i = 0; i < ADC_DEV_CHANNEL_SUM; i++) {
        channel = cfg->ch_list[i];
        if (channel < ADC_DEV_CHANNEL_SUM) {
            g_adc_init[channel] = TRUE;
        }
    }
    adc_ch_nums = cfg->ch_nums;

    return OPRT_OK;
}

/**
 * @brief adc deinit
 * 
 * @param[in] unit_num: adc unit number

 *
 * @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
 */
OPERATE_RET tkl_adc_deinit(TUYA_ADC_NUM_E port_num)
{
    return OPRT_OK;
}

/**
 * @brief get adc width
 * 
 * @param[in] unit_num: adc unit number

 *
 * @return adc width
 */
UINT8_T tkl_adc_width_get(TUYA_ADC_NUM_E port_num)
{
    return 12;
}


/**
 * @brief get adc reference voltage
 * 
 * @param[in] NULL

 *
 * @return adc reference voltage(bat: mv)
 */
UINT32_T tkl_adc_ref_voltage_get(TUYA_ADC_NUM_E port_num)
{
    return ADC_VOLTAGE_MAX;
}


/**
 * @brief adc read
 * 
 * @param[in] unit_num: adc unit number
 * @param[out] buff: points to the list of data read from the ADC register
 * @param[out] len:  buff len
 *
 * @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
 */
OPERATE_RET tkl_adc_read_data(TUYA_ADC_NUM_E port_num, INT32_T *buff, UINT16_T len)
{
    OPERATE_RET ret = OPRT_OK;
    unsigned char i = 0, j = 0;
    
    if (port_num > ADC_DEV_NUM-1) {
        tkl_log_output("error port num: %d:%d\r\n", port_num, __LINE__);
        return OPRT_INVALID_PARM;
    }
    if (adc_ch_nums * adc_desc.data_buff_size > len) {
        ret = OPRT_COM_ERROR;
    }

    for (i = 0; i < ADC_DEV_CHANNEL_SUM; i++) {
        if (g_adc_init[i]) {
            ret = tkl_adc_read_single_channel(port_num, i, &buff[j*adc_desc.data_buff_size]);
            j++;
        }
    }

    return ret;
}



typedef UINT16 heap_t;
#if 0
/**
 * minheap
 * A complete binary tree which satisfies the heap ordering property
 * the value of each node is greater than or equal to the value of its parent,
 * with the minimum-value element at the root.
 *
 * heap[i]'s parent:   heap[(i-1)/2]
 * heap[i]'s children: heap[2i+1], heap[2i+2]
 */
size_t MinHeapInsert(heap_t *heap, size_t heap_size, heap_t x) {
    int p = heap_size;
    //put x to heap[p], and compare with parents
    for (; (p != 0) && (heap[(p - 1) / 2] > x); p = (p - 1) / 2) {
        heap[p] = heap[(p - 1) / 2];
    }
    heap[p] = x;
    return heap_size + 1;
}

heap_t MinHeapReplace(heap_t *heap, size_t heap_size, heap_t x) {
    heap_t top = heap[0];
    int p = 0;

    for (; p < heap_size; ) {
        if ((2 * p + 1 < heap_size) && (x > heap[2 * p + 1])) {
            if ((2 * p + 2 < heap_size) && (heap[2 * p + 1] > heap[2 * p + 2])) {
                // minimum is right_child
                heap[p] = heap[2 * p + 2];
                p = 2 * p + 2;
            } else {
                // minimum is left_child
                heap[p] = heap[2 * p + 1];
                p = 2 * p + 1;
            }
        } else if ((2 * p + 2 < heap_size) && (x > heap[2 * p + 2])) {
            // minimum is right_child
                heap[p] = heap[2 * p + 2];
                    p = 2 * p + 2;
        } else {
            // minimum is x
            break;
        }
    }
    heap[p] = x;
    return top;
}



#endif

static UCHAR_T read_single_flag = FALSE;
extern size_t MinHeapInsert(heap_t *heap, size_t heap_size, heap_t x);
extern heap_t MinHeapReplace(heap_t *heap, size_t heap_size, heap_t x);
OPERATE_RET tkl_adc_read_single_channel(TUYA_ADC_NUM_E port_num, UINT8_T ch_id, INT32_T *data)
{
    signed char i = 0;
    unsigned int status;
    UCHAR_T data_buff_size = 0;;
    UINT_T sum = 0;
    UINT_T index;
    int adc_hdl;
    if ((port_num > ADC_DEV_NUM-1) || (ch_id > ADC_DEV_CHANNEL_SUM)) {
        return OPRT_INVALID_PARM;
    }     

    if(!g_adc_init[ch_id]){
        tkl_log_output("adc not init!\r\n");
        return OPRT_OS_ADAPTER_COM_ERROR;
    }

    if(read_single_flag) {
        tkl_log_output("adc is busy!\r\n");
        return OPRT_OS_ADAPTER_COM_ERROR;
    }
    read_single_flag = TRUE;

    adc_desc.channel = ch_id + 1;
    memset(&read_adc_buf[0], 0, ADC_BUF_SIZE_MAX);
    adc_desc.pData = (UINT16*)&read_adc_buf[0];;
    if(NULL == adc_desc.pData) {
        read_single_flag = FALSE;
        return OPRT_MALLOC_FAILED;
    }
    
    GLOBAL_INT_DECLARATION();
    GLOBAL_INT_DISABLE();

    adc_desc.current_sample_data_cnt = 0;
    adc_desc.current_read_data_cnt = 0;
    if(adc_desc.data_buff_size < ADC_BUF_SIZE_MIN) {
        data_buff_size = adc_desc.data_buff_size;
        adc_desc.data_buff_size = ADC_BUF_SIZE_MIN;
    }
    adc_hdl = ddev_open(SARADC_DEV_NAME, &status, (unsigned int)&adc_desc); 
    if ((DD_HANDLE_UNVALID == adc_hdl) || (SARADC_SUCCESS != status))
    {
        if (SARADC_SUCCESS != status)
        {
            ddev_close(adc_hdl);
        }
        adc_hdl = DD_HANDLE_UNVALID;
        GLOBAL_INT_RESTORE();
        tkl_log_output("adc ddev_open error:%d\r\n", status);
        read_single_flag = FALSE;
        return OPRT_COM_ERROR;  
    }
    GLOBAL_INT_RESTORE();

    while (1) {
        if (adc_desc.current_sample_data_cnt == adc_desc.data_buff_size) {
            ddev_close(adc_hdl);
            break;
        }
    }

    heap_t heap[ADC_BUF_SIZE_MAX / 2];
    int count = 0;
    for (index = 0; index < (adc_desc.data_buff_size + 1)/2; index++) {
        MinHeapInsert(heap, index, (heap_t)adc_desc.pData[index]);
    }

    for (index = (adc_desc.data_buff_size + 1)/2; index < adc_desc.data_buff_size; index++) {
        if (heap[0] < (heap_t)adc_desc.pData[index]) {
            MinHeapReplace(heap, (adc_desc.data_buff_size + 1)/2, (heap_t)adc_desc.pData[index]);
        }
    }

    for (index = 0; index < adc_desc.data_buff_size; index++) {
        //error [-0.5%, 0.5%] ==> [-5, 5]
        if ((adc_desc.pData[index] > heap[0] + 0x5) || (heap[0] > adc_desc.pData[index] + 0x5)) {
            continue;
        }

        count++;
        sum += adc_desc.pData[index];
    }

    if(data_buff_size < ADC_BUF_SIZE_MIN) {
        adc_desc.data_buff_size = data_buff_size;
    }
    
    adc_desc.pData[0] = (UINT16)(sum / count);
    //bk_printf("heap:%d sum:%d count:%d\r\n", heap[0], sum,count);

    for (i = adc_desc.data_buff_size - 1; i >= 0; i--) {
        data[i] = adc_desc.pData[i];
    }
    read_single_flag = FALSE;

    return OPRT_OK;   
}



/**
 * @brief adc get temperature
 *
 * @return temperature(bat: 'C)
 */
INT32_T tkl_adc_temperature_get(VOID_T)
{
    return OPRT_NOT_SUPPORTED;
}

/**
 * @brief read voltage
 *
 * @param[in] port_num: adc port number
 * @param[out] data: convert voltage, voltage range to -vref - +vref
 *
 * @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
 *
 */
OPERATE_RET tkl_adc_read_voltage(TUYA_ADC_NUM_E port_num, INT32_T *buff, UINT16_T len)
{
    OPERATE_RET ret = OPRT_COM_ERROR;
    UINT32_T ref = tkl_adc_ref_voltage_get(port_num);
    INT32_T i = 0;

    ret = tkl_adc_read_data(port_num, buff, len);
    if (OPRT_OK == ret) {
        for (i = 0; i < len; i++) {
            buff[i] = (buff[i] * ref) / ADC_REGISTER_VAL_MAX;
        }
    }
    
    return ret;
}

