/*******************************************************************************
 * @file    meas_pd_48.c
 * @brief   M48 LED-PD measurement module for NIRS system
 * @author  Charles KWON <charleskwon@medithings.co.kr>
 * @version V2.0.0
 * @date    2025-12-31
 *
 * Copyright (c) 2025 Medithings Inc.
 * All rights reserved.
 *
 * This module implements the M48 (24/48 LED) measurement sequence for the
 * NIRS bladder monitoring system. It handles sequential LED activation,
 * ADC sampling via PPI-triggered SAADC, and data transmission over BLE.
 *
 * ============================================================================
 * MEASUREMENT FLOW (mcj? command example - ADC_PD_MODE 2)
 * ============================================================================
 *
 * 1. Command Reception (parser.c - Cmd_mcj):
 *    - BLE/UART receives "mcj?" command
 *    - Sets ADC_PD_MODE = 2 (Half 0-23, single shot)
 *    - Sets info4 = true (include batt, temp, IMU, pressure data)
 *    - Calls m48_adc_start_init() to begin measurement
 *
 * 2. Initialization (m48_adc_start_init):
 *    - Stops battery timer (to prevent ADC conflict)
 *    - Copies LED_list_m48_1h[24] -> CURRENT_list_m48[24]
 *    - Sets CURRENT_LED_NO = 24
 *    - Initializes I2C, SAADC, GPIOTE, PPI
 *    - Configures first LED (LED0) via CAT9532 I2C
 *    - Sets MCP4725 DAC value from led_pd_dac_v[0] (from EEPROM/AGC)
 *    - Starts ADC sampling
 *
 * 3. ADC Sampling Loop (m48_voltage_handler - called 24 times):
 *    For each LED[i] (i = 0 to 23):
 *    a) PPI triggers 32 SAADC samples on ADA2200 SYNCO rising edge
 *    b) When buffer full, m48_voltage_handler() interrupt fires:
 *       - Averages 32 samples -> m48_led_pd_data[i]
 *       - Turns off current LED
 *       - If more LEDs remain:
 *         * Increments m_pd_adc_cnt
 *         * Configures next LED via CAT9532
 *         * Sets DAC value for next LED
 *         * Calls m48_adc_start() for next measurement
 *       - If last LED done:
 *         * Calls m48_adc_end_final()
 *
 * 4. Finalization (m48_adc_end_final):
 *    - Disables PPI, GPIOTE, SAADC
 *    - Uninitializes peripherals
 *    - If info4 == true, reads:
 *      * Battery voltage (uint16_t)
 *      * Temperature (uint16_t)
 *      * IMU data (6 x uint16_t)
 *      * Pressure P1, P2 (2 x uint16_t)
 *    - Formats response with "rcj:" tag
 *    - Sends via BLE: [batt, temp, imu[6], p1, p2, led_data[24]]
 *    - Restarts battery timer
 *    - Clears processing flag
 *
 * ============================================================================
 * RESPONSE DATA FORMAT (rcj: tag - 72 bytes total)
 * ============================================================================
 *
 * Byte[0-3]:   "rcj:" tag (4 bytes)
 * Byte[4-5]:   Battery voltage (uint16_t, big-endian)
 * Byte[6-7]:   Temperature (uint16_t, big-endian)
 * Byte[8-19]:  IMU data (6 x uint16_t = 12 bytes)
 * Byte[20-21]: Pressure P1 (uint16_t)
 * Byte[22-23]: Pressure P2 (uint16_t)
 * Byte[24-71]: LED measurement data (24 x uint16_t = 48 bytes)
 *
 * ============================================================================
 * Measurement Modes (ADC_PD_MODE):
 * ============================================================================
 *   0: Full 48 LED measurement (single shot)
 *   1: Full 48 LED measurement (continuous)
 *   2: Half measurement - LEDs 0-23 (single shot) - mcj? command
 *   3: Half measurement - LEDs 0-23 (continuous)
 *   4: Half measurement - LEDs 24-47 (single shot)
 *   5: Half measurement - LEDs 24-47 (continuous)
 *
 * Hardware Dependencies:
 *   - ADA2200 Lock-in Amplifier (SYNCO pin P0.17 for PPI trigger)
 *   - MCP4725 DAC for PD gain control (I2C)
 *   - CAT9532 LED driver (I2C)
 *   - SAADC for differential ADC measurement (AIN0/AIN1)
 *   - EEPROM for storing AGC calibration values (addr 0x0480)
 ******************************************************************************/

#include "sdk_common.h"

#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "nrf.h"
#include "boards.h"
#include "app_error.h"
#include "nrf_drv_saadc.h"
#include "nrfx_gpiote.h"
#include "app_timer.h"
#include "nrf_drv_timer.h"
#include "nrf_delay.h"
#include "nrf_drv_ppi.h"
#include "ada2200_spi.h"
#include "ble_nus.h"
#include "measurements.h"
#include "meas_pd_48.h"
#include "mcp4725_i2c.h"
//#include "ad5272_i2c.h"
#include "main_timer.h"
#include "battery_saadc.h"
#include "tmp235_q1.h"
#include "main.h"
#include "app_raw_main.h"
#include <cmd_parse.h>
#include "debug_print.h"

/*============================================================================*/
/* External Variables - Charles KWON                                          */
/*============================================================================*/

/** @brief Flag to include additional sensor data (batt, temp, IMU, pressure) */
extern bool info4;

/*============================================================================*/
/* Configuration Variables - Charles KWON                                     */
/*============================================================================*/

/**
 * @brief Current measurement mode
 * - Charles KWON
 *
 * Values:
 *   0: Full 48 LED (single)    1: Full 48 LED (continuous)
 *   2: Half 0-23 (single)      3: Half 0-23 (continuous)
 *   4: Half 24-47 (single)     5: Half 24-47 (continuous)
 */
uint8_t ADC_PD_MODE = 0;

/** @brief Number of ADC samples per LED measurement */
uint8_t m48_samples_in_buffer = 8;

/** @brief Current number of LEDs to measure */
uint8_t CURRENT_LED_NO = 24;

/** @brief Working LED index list for current measurement */
uint8_t CURRENT_list_m48[m48_LED_NO] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23};

/*============================================================================*/
/* LED Configuration Arrays - Charles KWON                                    */
/*============================================================================*/

/** @brief Full LED list (0-23) */
static const uint8_t LED_list_m48[m48_LED_NO] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23};

/** @brief First half LED list (0-23) for modes 2,3 */
static const uint8_t LED_list_m48_1h[m48_LED_NO_H] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23};

/** @brief Second half LED list (24-47) for modes 4,5 */
static const uint8_t LED_list_m48_2h[m48_LED_NO_H] = {24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47};

/** @brief PD delay in microseconds */
extern uint16_t m_pd_delay_us;

/*============================================================================*/
/* State Variables - Charles KWON                                             */
/*============================================================================*/

/** @brief Command processing flag */
extern volatile bool processing;

/** @brief BLE TX in progress flag */
extern volatile bool data_tx_in_progress;

/** @brief BLE connection status */
extern volatile bool ble_connection_st;

/** @brief Temperature measurement trigger */
extern bool go_temp;

/** @brief Battery measurement trigger */
extern bool go_batt;

/*============================================================================*/
/* Sensor Data Storage - Charles KWON                                         */
/*============================================================================*/

/** @brief Battery level (mV) */
uint16_t volatile info_batt = 0;

/** @brief Temperature reading */
uint16_t volatile info_temp = 0;

/** @brief IMU data array [accX, accY, accZ, gyrX, gyrY, gyrZ] */
uint16_t volatile info_imu[6] = {0, 1, 2, 3, 4, 5};

/** @brief Pressure sensor 1 value */
uint16_t volatile info_p1 = 0;

/** @brief Pressure sensor 2 value */
uint16_t volatile info_p2 = 0;

/*============================================================================*/
/* Measurement State Machine Variables - Charles KWON                         */
/*============================================================================*/

/** @brief Measurement cycle counter */
uint32_t m48_cnt;

/** @brief Current PD index (-1 = not started) */
static int8_t pd_no = -1;

/** @brief Current LED index (-1 = not started) */
static int8_t led_no = -1;

/** @brief Current buffer index */
static int8_t buf_no = 0;

/** @brief Elapsed time in milliseconds */
static int32_t t_ms = 0;

/** @brief Order counter for response sequencing */
uint8_t order_pd = 0;

/*============================================================================*/
/* ADC Data Buffers - Charles KWON                                            */
/*============================================================================*/

/**
 * @brief Raw ADC sample buffer for each LED
 * - Charles KWON
 *
 * Dimensions: [24 LEDs][32 samples max per LED]
 */
int16_t buff_m48_cycle[m48_LED_NO][m48_CYCLE_CNT] = {0};

/** @brief Summed ADC values for each LED (signed) */
int16_t m48_cycle_send_buff[m48_LED_NO] = {0};

/** @brief Summed ADC values for each LED (unsigned, for BLE TX) */
uint16_t single_bi_m48_cycle_send_buff[m48_LED_NO] = {0};

/** @brief ADC buffer size including delay samples */
#define SAMPLES_IN_BUFFER   (4095 + 32)

/** @brief Double-buffered ADC sample storage */
static nrf_saadc_value_t pd_m48_adc_buf[2][SAMPLES_IN_BUFFER];

/**
 * @brief Combined sensor + LED data buffer for BLE transmission
 * - Charles KWON
 *
 * Layout: [batt, temp, imu[6], p1, p2, led_data[24]]
 */
uint16_t single_info_m48_cycle_send_buff[m48_LED_NO] = {0};

/*============================================================================*/
/* Measurement State Flags - Charles KWON                                     */
/*============================================================================*/

/** @brief M48 measurement start trigger */
bool pd_adc_m48_start = false;

/** @brief M48 continuous measurement running */
bool pd_adc_m48_running = false;

/** @brief M48 measurement in progress */
bool m48_testing = false;

/** @brief New BLE data received flag */
extern bool ble_got_new_data;

/** @brief IMU raw data enabled flag */
extern bool motion_raw_data_enabled;

/*============================================================================*/
/* Communication Buffers - Charles KWON                                       */
/*============================================================================*/

/** @brief BLE binary transmission buffer */
uint8_t m48_bin_buffer[BLE_NUS_MAX_DATA_LEN];

/** @brief Command source type */
extern which_cmd_t cmd_type_t;

/*============================================================================*/
/* Timer Definitions - Charles KWON                                           */
/*============================================================================*/

/** @brief Timer for measurement time tracking */
APP_TIMER_DEF(m_m48_check_loop_timer_id);

/** @brief Send interval for delayed transmission */
#if FEATURE_DELAY
#define m48_SEND_LOOP_INTERVAL  500
#else
#define m48_SEND_LOOP_INTERVAL  100
#endif

/** @brief Check loop interval (1ms) */
#define m48_CHECK_LOOP_INTERVAL 1

/** @brief PPI channel for SYNCO->SAADC trigger */
static nrf_ppi_channel_t m_ppi_channel;

/*============================================================================*/
/* PPI Configuration - Charles KWON                                           */
/*============================================================================*/

#if !FEATURE_PRINTF
/**
 * @brief Initialize PPI for ADA2200 SYNCO triggered ADC sampling
 * - Charles KWON
 *
 * Connects ADA2200 SYNCO rising edge to SAADC sample task.
 */
void m48_ppi_init(void)
{
    ret_code_t err_code;

    err_code = nrf_drv_ppi_init();
    APP_ERROR_CHECK(err_code);

    uint32_t gpiote_event_addr = nrf_drv_gpiote_in_event_addr_get(ADA2200_SYNCO_PIN);
    uint32_t saadc_sample_task_addr = nrf_drv_saadc_sample_task_get();

    err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_ppi_channel_assign(m_ppi_channel,
                                          gpiote_event_addr,
                                          saadc_sample_task_addr);
    APP_ERROR_CHECK(err_code);
}

/**
 * @brief Uninitialize PPI
 * - Charles KWON
 */
void m48_ppi_uninit(void)
{
    ret_code_t err_code;
    err_code = nrf_drv_ppi_uninit();
    APP_ERROR_CHECK(err_code);
}

/**
 * @brief Enable PPI channel for ADC sampling
 * - Charles KWON
 */
void m48_sampling_event_enable(void)
{
    ret_code_t err_code = nrf_drv_ppi_channel_enable(m_ppi_channel);
    APP_ERROR_CHECK(err_code);
}

/**
 * @brief Disable PPI channel
 * - Charles KWON
 */
void m48_sampling_event_disable(void)
{
    ret_code_t err_code = nrf_drv_ppi_channel_disable(m_ppi_channel);
    APP_ERROR_CHECK(err_code);
}
#endif

/*============================================================================*/
/* ADC Interrupt Handler - Charles KWON                                       */
/*============================================================================*/

/**
 * @brief SAADC event handler for M48 measurement
 * - Charles KWON
 *
 * Implements the LED-PD measurement state machine:
 * 1. On first call: activate first LED and set DAC gain
 * 2. Store ADC samples for current LED
 * 3. Advance to next LED or complete measurement
 * 4. On completion: sum samples, format response, send via BLE
 *
 * Response tags by mode:
 *   - Mode 0: rsj: (Full with sensor data)
 *   - Mode 2: rcj: (Half 0-23 with sensor data)
 *   - Mode 3: rdj: (Half 0-23 continuous)
 *   - Mode 4: rej: (Half 24-47 with sensor data)
 *   - Mode 5: rfj: (Half 24-47 continuous)
 *
 * @param[in] p_event Pointer to SAADC event structure
 */
static void m48_voltage_handler(nrf_drv_saadc_evt_t const * p_event)
{
    ret_code_t err_code;
    int16_t sum = 0;
    uint32_t m48_clk_delay = m_pd_delay_us / 16;

    /* Handle BLE disconnection during measurement */
    if (ble_connection_st == 0) {
        DBG_PRINTF("m48 ADC STOP 1\r\n");
        led_off(99);
        pd_off(99);
        pd_adc_m48_start = false;
        pd_adc_m48_running = false;
        m48_testing = false;
        info4 = false;
        DBG_PRINTF("LOST_AT48\r\n");
        processing = false;
        go_batt = false;
        go_temp = false;
        m48_adc_end_final();
    } else {
        if (p_event->type == NRF_DRV_SAADC_EVT_DONE) {
            err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer,
                                                     m48_samples_in_buffer + m48_clk_delay);
            APP_ERROR_CHECK(err_code);

            /*----------------------------------------------------------------*/
            /* First call: Initialize LED/PD                                  */
            /*----------------------------------------------------------------*/
            if (led_no == -1) {
                led_no = 0;
                pd_no = 0;
                led_on(CURRENT_list_m48[led_no]);
                led_pd_matching_value_set(CURRENT_list_m48[led_no]);
            } else {
                /*------------------------------------------------------------*/
                /* Store ADC samples for current LED                          */
                /*------------------------------------------------------------*/
                for (uint16_t i = m48_clk_delay; i < m48_clk_delay + m48_samples_in_buffer; i++) {
                    buff_m48_cycle[buf_no][i - m48_clk_delay] = p_event->data.done.p_buffer[i];
                }
#if FEATURE_PRINTF
                DBG_PRINTF("-----------------Read ADC // led_no = %d(%d), pd_no = %d(%d), buf_no = %d\r\n\r\n",
                           led_no, LED_list_m48[led_no], pd_no, PD_list_m48[pd_no], buf_no);
#endif
                buf_no++;

                /*------------------------------------------------------------*/
                /* Advance to next LED or complete                            */
                /*------------------------------------------------------------*/
                if (pd_no >= -1) {
                    if (led_no < CURRENT_LED_NO - 1) {
                        /* Next LED */
                        led_no++;
                        led_on(CURRENT_list_m48[led_no]);
                        led_pd_matching_value_set(CURRENT_list_m48[led_no]);
                    } else if (led_no >= CURRENT_LED_NO - 1) {
                        /*----------------------------------------------------*/
                        /* All LEDs measured - process and send results       */
                        /*----------------------------------------------------*/
                        pd_no = -1;
                        led_no = -1;
#if FEATURE_PRINTF
                        DBG_PRINTF("\r\nEnded\r\n");
#endif
                        /* Sum samples for each LED */
                        uint8_t k = 0;
                        sum = 0;
                        for (uint16_t i = 0; i < CURRENT_LED_NO; i++) {
                            for (uint16_t j = 0; j < m48_samples_in_buffer; j++) {
                                sum += buff_m48_cycle[i][j];
                            }
                            m48_cycle_send_buff[k++] = sum;
                            sum = 0;
                        }
                        buf_no = 0;

                        if (ble_connection_st == 0) {
                            DBG_PRINTF("m24 ADC STOP 1");
                        } else {
                            pd_adc_m48_start = false;
                            DBG_PRINTF("FINISH SEND\r\n");
                            m48_testing = false;

                            if (cmd_type_t == CMD_UART) {
                                /* UART response handled elsewhere */
                            } else if (cmd_type_t == CMD_BLE) {
                                /* Convert to unsigned for BLE transmission */
                                for (uint16_t i = 0; i < CURRENT_LED_NO; i++) {
                                    DBG_PRINTF("%d,", m48_cycle_send_buff[i]);
                                    single_bi_m48_cycle_send_buff[i] = (uint16_t)(m48_cycle_send_buff[i]);
                                }

                                /*--------------------------------------------*/
                                /* Format response with sensor data           */
                                /*--------------------------------------------*/
                                if (info4 == true) {
                                    single_info_m48_cycle_send_buff[0] = info_batt;
                                    single_info_m48_cycle_send_buff[1] = info_temp;
                                    for (uint16_t i = 0; i < 6; i++) {
                                        single_info_m48_cycle_send_buff[i + 2] = info_imu[i];
                                    }
                                    single_info_m48_cycle_send_buff[8] = info_p1;
                                    single_info_m48_cycle_send_buff[9] = info_p2;

                                    for (uint16_t i = 0; i < CURRENT_LED_NO; i++) {
                                        single_info_m48_cycle_send_buff[i + 10] = single_bi_m48_cycle_send_buff[i];
                                    }

                                    /* Send response based on mode */
                                    if (ADC_PD_MODE == 0) {
                                        format_data(m48_bin_buffer, "rsj:", single_info_m48_cycle_send_buff, 56);
                                        binary_tx_handler(m48_bin_buffer, 58);
                                    } else if (ADC_PD_MODE == 2) {
                                        format_data(m48_bin_buffer, "rcj:", single_info_m48_cycle_send_buff, 34);
                                        binary_tx_handler(m48_bin_buffer, 36);
                                    } else if (ADC_PD_MODE == 3) {
                                        format_data(m48_bin_buffer, "rdj:", single_info_m48_cycle_send_buff, 34);
                                        binary_tx_handler(m48_bin_buffer, 36);
                                    } else if (ADC_PD_MODE == 4) {
                                        format_data(m48_bin_buffer, "rej:", single_info_m48_cycle_send_buff, 34);
                                        binary_tx_handler(m48_bin_buffer, 36);
                                    } else if (ADC_PD_MODE == 5) {
                                        format_data(m48_bin_buffer, "rfj:", single_info_m48_cycle_send_buff, 34);
                                        binary_tx_handler(m48_bin_buffer, 36);
                                    }
                                } else {
                                    /* Send LED data only (no sensor data) */
                                    char resp[4];

                                    if (ADC_PD_MODE == 2) {
                                        format_data(m48_bin_buffer, "rdj:", single_bi_m48_cycle_send_buff, 24);
                                        binary_tx_handler(m48_bin_buffer, 26);
                                    } else if (ADC_PD_MODE == 4) {
                                        format_data(m48_bin_buffer, "rfj:", single_bi_m48_cycle_send_buff, 24);
                                        binary_tx_handler(m48_bin_buffer, 26);
                                    } else if (ADC_PD_MODE == 3) {
                                        sprintf(resp, "rd%01X:", order_pd);
                                        format_data(m48_bin_buffer, resp, single_bi_m48_cycle_send_buff, 24);
                                        binary_tx_handler(m48_bin_buffer, 26);
                                    } else if (ADC_PD_MODE == 5) {
                                        sprintf(resp, "rf%01X:", order_pd);
                                        format_data(m48_bin_buffer, resp, single_bi_m48_cycle_send_buff, 24);
                                        binary_tx_handler(m48_bin_buffer, 26);
                                    }
                                }

                                DBG_PRINTF("\r\n %d ms \r\n", t_ms);
                            }

                            /*------------------------------------------------*/
                            /* Handle continuous mode or cleanup              */
                            /*------------------------------------------------*/
                            if (pd_adc_m48_running == true) {
                                if (info4 == true) {
                                    m48_adc_end_final();
                                    pd_adc_m48_start = true;
                                    go_batt = true;
                                    main_timer_start();
                                } else {
                                    t_ms = 0;
                                    m48_cnt = 0;
                                    m48_testing = true;
                                    m48_adc_start();
                                }
                            } else {
                                /* Single shot - cleanup */
                                if (info4 == true) {
                                    info4 = false;
                                }
                                led_off(99);
                                pd_off(99);
                                processing = false;
                                m48_adc_end_final();
                            }
                        }
                    }
                }
            }
        }
    }
}

/*============================================================================*/
/* Timer Callback - Charles KWON                                              */
/*============================================================================*/

/**
 * @brief Check timer callback for measurement timing
 * - Charles KWON
 *
 * Tracks elapsed time during measurement.
 *
 * @param[in] p_context Unused timer context
 */
void m48_check_loop(void * p_context)
{
    UNUSED_PARAMETER(p_context);
    m48_check_timer_stop();

    if (m48_testing == false) {
        DBG_PRINTF("%d ms \r\n", t_ms);
    } else {
        t_ms++;
        m48_check_timer_start();
    }
}

/*============================================================================*/
/* Measurement Control Functions - Charles KWON                               */
/*============================================================================*/

/**
 * @brief Initialize and start M48 measurement based on mode
 * - Charles KWON
 *
 * Called from main_timer to begin measurement sequence.
 * Configures LED list based on ADC_PD_MODE setting.
 */
void m48_adc_start_init(void)
{
    /* Handle new data received during continuous mode */
    if (ble_got_new_data == true) {
        if (pd_adc_m48_running == true) {
            led_off(99);
            pd_off(99);
            pd_adc_m48_start = false;
            pd_adc_m48_running = false;
            DBG_PRINTF("FINISH NEWDATA\r\n");
            processing = false;
            go_batt = false;
            go_temp = false;
            motion_raw_data_enabled = false;
            m48_testing = false;

            if (info4 == true) {
                info4 = false;
            } else {
                m48_adc_end_final();
            }
        }
    } else if (pd_adc_m48_start == true) {
        /* Configure measurement based on mode */
        switch (ADC_PD_MODE) {
            case 0:  /* Full 48 LED (single) */
                pd_adc_m48_running = false;
                CURRENT_LED_NO = m48_LED_NO;
                for (uint16_t i = 0; i < CURRENT_LED_NO; i++) {
                    CURRENT_list_m48[i] = LED_list_m48[i];
                }
                break;

            case 1:  /* Full 48 LED (continuous) */
                pd_adc_m48_running = true;
                CURRENT_LED_NO = m48_LED_NO;
                for (uint16_t i = 0; i < CURRENT_LED_NO; i++) {
                    CURRENT_list_m48[i] = LED_list_m48[i];
                }
                break;

            case 2:  /* Half 0-23 (single) - mcj? */
                pd_adc_m48_running = false;
                CURRENT_LED_NO = m48_LED_NO_H;
                for (uint16_t i = 0; i < CURRENT_LED_NO; i++) {
                    CURRENT_list_m48[i] = LED_list_m48_1h[i];
                }
                break;

            case 3:  /* Half 0-23 (continuous) */
                pd_adc_m48_running = true;
                CURRENT_LED_NO = m48_LED_NO_H;
                for (uint16_t i = 0; i < CURRENT_LED_NO; i++) {
                    CURRENT_list_m48[i] = LED_list_m48_1h[i];
                }
                break;

            case 4:  /* Half 24-47 (single) */
                pd_adc_m48_running = false;
                CURRENT_LED_NO = m48_LED_NO_H;
                for (uint16_t i = 0; i < CURRENT_LED_NO; i++) {
                    CURRENT_list_m48[i] = LED_list_m48_2h[i];
                    DBG_PRINTF("%d,", CURRENT_list_m48[i]);
                }
                break;

            case 5:  /* Half 24-47 (continuous) */
                pd_adc_m48_running = true;
                CURRENT_LED_NO = m48_LED_NO_H;
                for (uint16_t i = 0; i < CURRENT_LED_NO; i++) {
                    CURRENT_list_m48[i] = LED_list_m48_2h[i];
                }
                break;

            default:
                pd_adc_m48_running = false;
                CURRENT_LED_NO = m48_LED_NO;
                for (uint16_t i = 0; i < CURRENT_LED_NO; i++) {
                    CURRENT_list_m48[i] = LED_list_m48[i];
                }
                break;
        }

        t_ms = 0;
        m48_cnt = 0;
        pd_adc_m48_start = false;
        m48_testing = true;

        m48_adc_start2();
        m48_check_timer_start();
    }
}

/**
 * @brief Initialize measurement buffers and state
 * - Charles KWON
 */
void m48_adc_start(void)
{
    pd_no = -1;
    led_no = -1;
    buf_no = 0;

    /* Clear measurement buffers */
    for (uint16_t i = 0; i < m48_LED_NO; i++) {
        for (uint16_t j = 0; j < m48_CYCLE_CNT; j++) {
            buff_m48_cycle[i][j] = 0;

            if (ble_got_new_data == true) {
                if (pd_adc_m48_running == true) {
                    pd_adc_m48_start = false;
                    pd_adc_m48_running = false;
                }
            }
        }
    }

    /* Update order counter for continuous mode */
    if (order_pd >= 15) {
        order_pd = 0;
    } else {
        order_pd++;
    }
}

/**
 * @brief Start M48 measurement with full initialization
 * - Charles KWON
 */
void m48_adc_start2(void)
{
    m48_adc_start();
    m48_adc_init();
    m48_irq_init();
    m48_ppi_init();
    m48_sampling_event_enable();
}

/**
 * @brief Stop ADC sampling (partial cleanup)
 * - Charles KWON
 */
void m48_adc_end(void)
{
    DBG_PRINTF("m48_adc_end\r\n");
    m48_sampling_event_disable();
}

/**
 * @brief Complete measurement cleanup
 * - Charles KWON
 *
 * Disables PPI, uninitializes SAADC, restarts battery timer.
 */
void m48_adc_end_final(void)
{
    DBG_PRINTF("adc_end_final\r\n");
    m48_sampling_event_disable();
    m48_irq_uninit();
    m48_ppi_uninit();
    m48_adc_uninit();
    battery_timer_start();
}

/*============================================================================*/
/* SAADC Configuration - Charles KWON                                         */
/*============================================================================*/

/**
 * @brief Initialize SAADC for M48 measurement
 * - Charles KWON
 *
 * Configures differential ADC on AIN0/AIN1 with 10-bit resolution.
 */
void m48_adc_init(void)
{
#if FEATURE_PRINTF
    DBG_PRINTF("m48_adc_init\r\n");
#endif

    static nrfx_saadc_config_t default_config;
    default_config.resolution         = (nrf_saadc_resolution_t)NRFX_SAADC_CONFIG_RESOLUTION;
    default_config.oversample         = (nrf_saadc_oversample_t)NRFX_SAADC_CONFIG_OVERSAMPLE;
    default_config.interrupt_priority = NRFX_SAADC_CONFIG_IRQ_PRIORITY;
    default_config.low_power_mode     = NRFX_SAADC_CONFIG_LP_MODE;

    static nrf_saadc_channel_config_t config;
    config.resistor_p = NRF_SAADC_RESISTOR_DISABLED;
    config.resistor_n = NRF_SAADC_RESISTOR_DISABLED;
    config.gain       = NRF_SAADC_GAIN1_6;
    config.reference  = NRF_SAADC_REFERENCE_INTERNAL;
    config.acq_time   = NRF_SAADC_ACQTIME_3US;
    config.mode       = NRF_SAADC_MODE_DIFFERENTIAL;
    config.burst      = NRF_SAADC_BURST_DISABLED;
    config.pin_p      = (nrf_saadc_input_t)(NRF_SAADC_INPUT_AIN0);
    config.pin_n      = (nrf_saadc_input_t)(NRF_SAADC_INPUT_AIN1);

    ret_code_t err_code = nrf_drv_saadc_init(&default_config, m48_voltage_handler);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_saadc_channel_init(0, &config);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_saadc_buffer_convert(pd_m48_adc_buf[0],
                                             m48_samples_in_buffer + m_pd_delay_us / 16);
    APP_ERROR_CHECK(err_code);
}

/**
 * @brief Uninitialize SAADC
 * - Charles KWON
 */
void m48_adc_uninit(void)
{
#if FEATURE_PRINTF
    DBG_PRINTF("pd_m48_adc_uninit\r\n");
#endif

    nrf_drv_saadc_uninit();
    nrf_drv_saadc_channel_uninit(0);
}

/*============================================================================*/
/* GPIO Interrupt Configuration - Charles KWON                                */
/*============================================================================*/

#if !FEATURE_PRINTF
/**
 * @brief Initialize GPIOTE for ADA2200 SYNCO pin
 * - Charles KWON
 *
 * Configures rising edge detection on SYNCO for PPI trigger.
 */
void m48_irq_init(void)
{
    ret_code_t err_code;

    if (!nrfx_gpiote_is_init()) {
        err_code = nrfx_gpiote_init();
        APP_ERROR_CHECK(err_code);
    }

    nrfx_gpiote_in_config_t in_config = NRFX_GPIOTE_CONFIG_IN_SENSE_LOTOHI(true);
    in_config.pull = NRF_GPIO_PIN_PULLDOWN;

    err_code = nrfx_gpiote_in_init(ADA2200_SYNCO_PIN, &in_config, NULL);
    APP_ERROR_CHECK(err_code);

    nrfx_gpiote_in_event_enable(ADA2200_SYNCO_PIN, true);
}

/**
 * @brief Uninitialize GPIOTE for SYNCO pin
 * - Charles KWON
 */
void m48_irq_uninit(void)
{
    nrfx_gpiote_in_event_disable(ADA2200_SYNCO_PIN);
    nrfx_gpiote_in_uninit(ADA2200_SYNCO_PIN);
}
#endif

/*============================================================================*/
/* Timer Control Functions - Charles KWON                                     */
/*============================================================================*/

/**
 * @brief Start measurement check timer
 * - Charles KWON
 */
void m48_check_timer_start(void)
{
    APP_ERROR_CHECK(app_timer_start(m_m48_check_loop_timer_id,
                                     APP_TIMER_TICKS(m48_CHECK_LOOP_INTERVAL), NULL));
}

/**
 * @brief Stop measurement check timer
 * - Charles KWON
 */
void m48_check_timer_stop(void)
{
    APP_ERROR_CHECK(app_timer_stop(m_m48_check_loop_timer_id));
}

/**
 * @brief Initialize measurement check timer
 * - Charles KWON
 */
void m48_check_timer_init(void)
{
    APP_ERROR_CHECK(app_timer_create(&m_m48_check_loop_timer_id,
                                      APP_TIMER_MODE_SINGLE_SHOT, m48_check_loop));
}
