#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_CHANNEL_SUM 6

#define ADC_REGISTER_VAL_MAX  4096
#define ADC_VOLTAGE_MAX  3600   //mv
#define ADC_BUF_SIZE_MIN  5
#define ADC_BUF_SIZE_MAX  255

#define REG_READ(addr)          (*((volatile UINT32 *)(addr)))
#define REG_WRITE(addr, _data)  (*((volatile UINT32 *)(addr)) = (_data))

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 = NULL;

typedef UINT16 heap_t;
static UCHAR_T read_single_flag = FALSE;
TUYA_ADC_BASE_CFG_T tkl_adc_cfg;
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);

/**
 * @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
 */
 static void adc_isr_handle(void)
 {
	//ddev_close(adc_hdl);
	//saradc_ensure_close(); 
 }
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;

    if ((port_num > TUYA_ADC_NUM_MAX) || (cfg->ch_nums > ADC_DEV_CHANNEL_SUM)) {
        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;

    if ((adc_desc.data_buff_size + ADC_BUF_SIZE_MIN) > ADC_BUF_SIZE_MAX) {
        return OPRT_INVALID_PARM;
    }

    if (NULL == read_adc_buf) {
        read_adc_buf = (unsigned short *)tkl_system_malloc(sizeof(unsigned short) * ADC_BUF_SIZE_MAX);
        if (NULL == read_adc_buf) {
            return OPRT_MALLOC_FAILED;
        }
        memset(read_adc_buf, 0, sizeof(unsigned short) * ADC_BUF_SIZE_MAX);
    }

    adc_desc.pData = read_adc_buf;
    adc_desc.pre_div = 1;
    adc_desc.samp_rate = 3;
    adc_desc.p_Int_Handler = adc_isr_handle;

    for (i = 0; i < ADC_DEV_CHANNEL_SUM; i++) {
        if (cfg->ch_list.data & BIT(i)) {
            channel = i;
            if (channel < ADC_DEV_CHANNEL_SUM) {
                g_adc_init[channel] = TRUE;
            }
        } 
    }
    adc_ch_nums = cfg->ch_nums;
    memcpy(&tkl_adc_cfg, cfg, sizeof(tkl_adc_cfg));
    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)
{
    if (read_adc_buf) {
        tkl_system_free(read_adc_buf);
        read_adc_buf = NULL;
    }
    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 > TUYA_ADC_NUM_MAX) {
        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;
}

OPERATE_RET tkl_adc_read_single_channel(TUYA_ADC_NUM_E port_num, UINT8_T ch_id, INT32_T *data)
{
    UCHAR_T i = 0;
    unsigned int status = SARADC_FAILURE;
    UCHAR_T data_buff_size = 0;;
    UINT_T sum = 0;
    UINT_T index;
    int retries = 0;
    int adc_hdl = DD_HANDLE_UNVALID;
    int count = 0;
    INT32_T min_value = 0;
    INT32_T max_value = 0;
    UCHAR_T j = 0;

    if ((port_num > TUYA_ADC_NUM_MAX) || (ch_id > ADC_DEV_CHANNEL_SUM)) {
        return OPRT_INVALID_PARM;
    }

    if(!g_adc_init[ch_id]){
        return OPRT_OS_ADAPTER_COM_ERROR;
    }

    if(read_single_flag) {
        return OPRT_OS_ADAPTER_COM_ERROR;
    }
    read_single_flag = TRUE;

    if(NULL == adc_desc.pData) {
        read_single_flag = FALSE;
        return OPRT_MALLOC_FAILED;
    }

    adc_desc.channel = ch_id + 1;
    data_buff_size = tkl_adc_cfg.conv_cnt;
    adc_desc.data_buff_size  = data_buff_size + ADC_BUF_SIZE_MIN;

    GLOBAL_INT_DECLARATION();

    adc_desc.current_sample_data_cnt = 0;
    adc_desc.current_read_data_cnt = 0;

    do {
        GLOBAL_INT_DISABLE();
        if(saradc_check_busy() == 0) {
            adc_hdl = ddev_open(SARADC_DEV_NAME, &status, (UINT32)&adc_desc);
            if(DD_HANDLE_UNVALID != adc_hdl) {
                GLOBAL_INT_RESTORE();
                break;
            }
        }

        GLOBAL_INT_RESTORE();
        rtos_delay_milliseconds(5);
        retries++;
    } while (retries < 5);

     if ((DD_HANDLE_UNVALID == adc_hdl) || (SARADC_SUCCESS != status)) {
        if (SARADC_SUCCESS != status) {
            ddev_close(adc_hdl);
            tkl_adc_init(TUYA_ADC_NUM_0, &tkl_adc_cfg);
        }
        adc_hdl = DD_HANDLE_UNVALID;
        read_single_flag = FALSE;
        tkl_log_output("adc ddev_open error:%d\r\n", status);
        return OPRT_COM_ERROR;  
    }

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

    for (i = ADC_BUF_SIZE_MIN, j = 0; (i < adc_desc.data_buff_size) && (j < data_buff_size); i++, j++) {
        data[j] = adc_desc.pData[i];
    }
  
    read_single_flag = FALSE;
    ddev_close(adc_hdl);

    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;
}

#if 1
INT32_T aa[251] = { 0 };
#include "tkl_thread.h"
TKL_THREAD_HANDLE x_thread = NULL;
//ADC_INFO_S *adc_port_arry = NULL;
void x_thread_cb(void *arg)
{
    UCHAR_T idx = 0;
    INT32_T adc_buf[6] = {0};
    UINT8_T channel = 0;
    TUYA_ADC_BASE_CFG_T adc_cfg;
    UINT32_T ch_data = 0;
    //INT32_T aa = 0;
#if 0
    for (idx = 0; idx < adc_port_arry->len; idx++) {
        channel = tkl_io_pin_to_func(adc_port_arry->data[idx], TUYA_IO_TYPE_ADC);
        ch_data |= BIT(channel & 0xFF);
    }
#endif
    ch_data |= BIT(2 & 0xFF);
    adc_cfg.ch_list.data = ch_data;
    adc_cfg.ch_nums = 1;
    adc_cfg.type = TUYA_ADC_INNER_SAMPLE_VOL;
    adc_cfg.width = 12;
    adc_cfg.mode = TUYA_ADC_CONTINUOUS;
    adc_cfg.conv_cnt = 250;

    if (OPRT_OK != tkl_adc_init(0, &adc_cfg)) {
        bk_printf("%s: adc init failed\r\n", __func__);
    }

    bk_printf("%s: adc init\r\n", __func__);

    while(1) {
        if(OPRT_OK == tkl_adc_read_voltage(0, aa, 250)) {
            //tkl_adc_read_voltage(0, &aa, 1);
            for (int i = 0; i < adc_cfg.conv_cnt; i++) {
            bk_printf("%s: i %d volt %u \r\n", __func__, i, aa[i]);
            }
        } else {
        //bk_printf("%s: call tkl_adc_read_voltage error \r\n", __func__);
        }
        //tkl_adc_read_voltage(0, adc_buf, 6);
        //bk_printf("%s: volt %u %u %u %u %u %u\r\n", __func__, adc_buf[0], adc_buf[1], adc_buf[2], adc_buf[3], adc_buf[4], adc_buf[5]);
        tkl_system_sleep(1000);
    }
#if 0
    for (idx = 0; idx < adc_port_arry->len; idx++) {
        channel = tkl_io_pin_to_func(adc_port_arry->data[idx], TUYA_IO_TYPE_ADC);
        PR_NOTICE("channel:%d,volt = %u mv", channel, adc_buf[idx]);
    }
#endif
    tkl_adc_deinit(0);    
}
void tkl_adc_unit_test(void)
{
    tkl_thread_create(&x_thread, "x_thread", 1024, 5, x_thread_cb, NULL);
}
#endif
