/*******************************************************************************
 * @file    measurements.c
 * @brief   LED/PD measurement control 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 provides LED and photodetector (PD) control functions for the
 * NIRS (Near-Infrared Spectroscopy) bladder monitoring system.
 *
 * ============================================================================
 * MODULE OVERVIEW
 * ============================================================================
 *
 * Hardware Components Controlled:
 *   - 48 NIR LEDs (LED0-LED47) via CAT9532 I2C LED driver
 *   - 4 Photodetectors (PD0, PD1, MOD, MOD2) via FSA5157P6X analog switch
 *   - MCP4725 DAC for PD gain control (I2C, 12-bit)
 *   - DS3930 EEPROM for LED power calibration storage
 *
 * LED-PD Mapping:
 *   - LED 0-23  -> PD0 (photodetector 0)
 *   - LED 24-47 -> PD1 (photodetector 1)
 *   - MOD/MOD2  -> Lock-in amplifier modulation channels
 *
 * DAC Gain Range:
 *   - MCP4725 output: 0.1V ~ 1.1V
 *   - DAC value range: 125 ~ 1366 (12-bit)
 *   - Default value: 1000 (mid-range)
 *
 * ============================================================================
 * MAIN FUNCTIONS
 * ============================================================================
 *
 * LED Control:
 *   - led_on(index)      : Turn on specific LED (0-47)
 *   - led_off(index)     : Turn off LED (99=all off, 98=clear)
 *   - led_select(index)  : Select LED via GPIO multiplexer
 *
 * PD Control:
 *   - pd_on(index)       : Turn on specific PD (0-3)
 *   - pd_off(index)      : Turn off PD (99=all off)
 *   - pd_select(index)   : Select PD channel
 *
 * Gain Control:
 *   - pd_gain_set(led)   : Set PD gain from led_pd_dac_v[] lookup table
 *   - imm_gain_set(dac)  : Set immediate DAC value
 *   - led_pd_mod_set()   : Set modulation gain for lock-in
 *
 * EEPROM Operations:
 *   - led_power_read(index)       : Read LED power from ROM
 *   - led_power_save_mem(index)   : Save LED power to ROM
 *   - led_power_save_mem_48()     : Save all 48 LED powers
 *
 ******************************************************************************/

/*============================================================================*/
/* Includes - Charles KWON                                                    */
/*============================================================================*/
#include "sdk_common.h"
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "nrf.h"
#include "nrf_drv_gpiote.h"
#include "nrf_drv_spi.h"
#include "nrf_drv_saadc.h"
#include "nrf_drv_ppi.h"
#include "nrf_drv_timer.h"
#include "boards.h"
#include "bsp.h"
#include "app_error.h"
#include "nrf_delay.h"
#include "app_util_platform.h"
#include "nrf_pwr_mgmt.h"
#include "nrf_log.h"
#include "ble_nus.h"
#include "LED_Parse.h"
#include "app_timer.h"
#include "measurements.h"
#include "main.h"
//#include "ad5272_i2c.h"
#include "mcp4725_i2c.h"
#include <cmd_parse.h>
#include "debug_print.h"
#include "i2c_manager.h"

/*============================================================================*/
/* Configuration Constants - Charles KWON                                     */
/*============================================================================*/

/**
 * @brief Total number of LEDs in the system
 * - Charles KWON
 *
 * Currently using 24 LEDs (LED0-LED23) for single PD configuration.
 * Can be extended to 48 for dual PD configuration.
 */
#define LED_NUM 24

/** @brief Timer interval for BLE data transmission (ms) */
#define MEA_SEND_LOOP_INTERVAL 100

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

/**
 * @brief Currently activated LED index
 * - Charles KWON
 *
 * Tracks which LED is currently turned on.
 * Value 99 indicates no LED is active (all off).
 */
uint8_t activated_led = 99;

/**
 * @brief Currently activated photodetector index
 * - Charles KWON
 *
 * Tracks which PD channel is currently selected.
 * Value 99 indicates no PD is active (all off).
 */
uint8_t activated_pd = 99;

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

/** @brief Timer for sequenced BLE data transmission */
APP_TIMER_DEF(m_mea_send_loop_timer_id);

/** @brief Counter for BLE transmission sequence */
static uint8_t cnt = 0;

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

/** @brief TX buffer for BLE data transmission */
char mea_tx_buffer[BLE_NUS_MAX_DATA_LEN];

/** @brief External flag indicating command processing state */
extern volatile bool processing;

/*============================================================================*/
/* LED-PD Gain Calibration Table - Charles KWON                               */
/*============================================================================*/

/**
 * @brief DAC values for LED-PD gain matching
 * - Charles KWON
 *
 * Each LED has a corresponding DAC value that sets the optimal
 * PD amplifier gain for that LED-PD pair. Values are calibrated
 * during AGC (Automatic Gain Control) and stored in EEPROM.
 *
 * DAC Range:
 *   - Minimum: 125  (0.1V output)
 *   - Maximum: 1366 (1.1V output)
 *   - Default: 1000 (mid-range)
 *
 * This array is populated from EEPROM at startup or after AGC.
 * Defined in device_config.c
 */
extern uint16_t led_pd_dac_v[];

/**
 * @brief DAC value when all LEDs are off
 * - Charles KWON
 *
 * Used as baseline/reference gain when no LED is active.
 */
uint16_t led_off_dac_v = 1360;

/**
 * @brief Default PD channel when LED is off
 * - Charles KWON
 */
uint8_t led_off_pd = 0;

/*============================================================================*/
/* BLE Data Transmission Functions - Charles KWON                             */
/*============================================================================*/

/**
 * @brief Start LED-PD gain array printout via BLE
 * - Charles KWON
 *
 * Initiates sequenced transmission of the led_pd_dac_v[] array
 * over BLE. Data is sent in chunks to comply with BLE MTU limits.
 */
void led_pd_gain_array_printout(void)
{
	cnt = 0;
	mea_send_timer_start();
}


/**
 * @brief Timer callback for sending LED-PD gain data via BLE
 * - Charles KWON
 *
 * Called periodically to transmit gain calibration data.
 * Sends data in 3 chunks (cnt 0-2), then outputs debug info
 * and clears the processing flag.
 *
 * Output Format: "Tag-Currrnt [cnt], [dac0], [dac1], ..., [dac23]\r\n"
 *
 * @param[in] p_context Unused timer context
 */
void mea_send_loop(void *p_context)
{
	UNUSED_PARAMETER(p_context);
	DBG_PRINTF("\r\n1CNT =%d\r\n", cnt);
	mea_send_timer_stop();

	if (cnt < 6) {
		uint8_t i = (int)(cnt);
		if (cnt < 3) {
			/* Build string dynamically for easy LED_NUM changes */
			int offset = sprintf(mea_tx_buffer, "Tag-Currrnt %d", i);
			for (uint8_t j = 0; j < LED_NUM; j++) {
				offset += sprintf(mea_tx_buffer + offset, ",\t%d", led_pd_dac_v[j]);
			}
			sprintf(mea_tx_buffer + offset, "\r\n");
			data_tx_handler(mea_tx_buffer);
		}
		DBG_PRINTF("\r\n2CNT =%d\r\n", cnt);
		mea_send_timer_start();
	} else {
		/* Final iteration: print debug summary */
		DBG_PRINTF("\r\nLED-PD Gain Array =\r\n");
		for (uint16_t j = 0; j < LED_NUM; j++) {
			DBG_PRINTF("%d,\t", led_pd_dac_v[j]);
		}
		DBG_PRINTF("\r\n");
		processing = false;
	}

	cnt++;
}

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

/**
 * @brief Turn on specified LED
 * - Charles KWON
 *
 * Activates the specified LED through the CAT9532 LED driver.
 * Also enables the analog switch trigger for signal routing.
 *
 * @param[in] led_index LED index (0-47 for specific LED, <=100 for valid range)
 * @return NRF_SUCCESS on success, NRF_ERROR_INTERNAL on failure
 *
 * @note Sets activated_led to track current LED state
 * @note Calls trig_SW(true) to enable signal path
 */
ret_code_t led_on(uint8_t led_index)
{
	uint32_t err_code = NRF_SUCCESS;

#if FEATURE_PRINTF
	DBG_PRINTF("\tled_on, %d =====================\r\n", led_index);
#endif

	if (led_index <= 100) {
		activated_led = led_index;
		trig_SW(true);  /* Enable analog switch for signal routing */

		if (NRF_SUCCESS != led_select(led_index)) {
			DBG_PRINTF("ERR!!! LED Select\r\n");
			return NRF_ERROR_INTERNAL;
		}
	} else {
		DBG_PRINTF("ERR!!! led_index Failed! %d\r\n", led_index);
		return NRF_ERROR_INTERNAL;
	}

	return err_code;
}


/**
 * @brief Turn off LED
 * - Charles KWON
 *
 * Deactivates LED and disables the analog switch trigger.
 *
 * @param[in] led_index 99 = all LEDs off, 98 = clear state
 * @return NRF_SUCCESS on success, NRF_ERROR_INTERNAL on invalid index
 *
 * @note Calls trig_SW(false) to disable signal path
 */
ret_code_t led_off(uint8_t led_index)
{
	uint32_t err_code = NRF_SUCCESS;
	trig_SW(false);  /* Disable analog switch */

#if FEATURE_PRINTF
	DBG_PRINTF("led_off\r\n");
#endif

	if (led_index == 99) {
		LED_ALLOFF();  /* Turn off all LEDs */
		activated_led = 99;
	} else if (led_index == 98) {
		LED99();  /* Clear LED state */
		DBG_PRINTF("clear \r\n");
		activated_led = 98;
	} else {
		DBG_PRINTF("ERR!!! led_index Failed! %d\r\n", led_index);
		return NRF_ERROR_INTERNAL;
	}

	return err_code;
}

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

/**
 * @brief Set modulation gain via MCP4725 DAC
 * - Charles KWON
 *
 * Configures the DAC for lock-in amplifier modulation mode.
 * Used when measuring with the MOD photodetector channel.
 *
 * @param[in] mod_gain DAC value (0-2000)
 * @return NRF_SUCCESS on success, NRF_ERROR_INTERNAL on failure
 *
 * @note Initializes SW I2C if not already done
 * @note Activates MOD channel (PD index 2)
 */
ret_code_t led_pd_mod_set(uint16_t mod_gain)
{
	uint32_t err_code = NRF_SUCCESS;

	if (mod_gain <= 2000) {
		sw_i2c_init_once();  /* Ensure I2C is initialized */
		mcp4725_writeFastMode(mod_gain);

		if (NRF_SUCCESS != pd_on(2)) {  /* Enable MOD channel */
			DBG_PRINTF("ERR!!! MOD_on\r\n");
			return NRF_ERROR_INTERNAL;
		}
	} else {
		DBG_PRINTF("ERR!!! lockin Test Failed!\r\n");
		return NRF_ERROR_INTERNAL;
	}

	return err_code;
}


/**
 * @brief Set PD gain matching value for specified LED
 * - Charles KWON
 *
 * Configures the PD amplifier gain based on the LED index.
 * Automatically selects the appropriate PD channel:
 *   - LED 0-23  -> PD0
 *   - LED 24-47 -> PD1
 *   - LED 99    -> Default PD (led_off_pd)
 *
 * @param[in] led_index LED index for gain lookup
 * @return NRF_SUCCESS on success, NRF_ERROR_INTERNAL on failure
 */
ret_code_t led_pd_matching_value_set(uint8_t led_index)
{
	uint32_t err_code = NRF_SUCCESS;

	if (led_index <= 100) {
		/* Set DAC gain from lookup table */
		if (NRF_SUCCESS != pd_gain_set(led_index)) {
			DBG_PRINTF("ERR!!! pd_gain_set\r\n");
			return NRF_ERROR_INTERNAL;
		}

		/* Determine PD channel based on LED index */
		uint8_t pd_index;
		if (led_index < 24) {
			pd_index = 0;  /* PD0 for LED 0-23 */
		} else if ((led_index < 48) && (led_index > 23)) {
			pd_index = 1;  /* PD1 for LED 24-47 */
		} else if (led_index == 99) {
			pd_index = led_off_pd;  /* Default PD when off */
		}

		/* Activate the selected PD channel */
		if (NRF_SUCCESS != pd_on(pd_index)) {
			DBG_PRINTF("ERR!!! pd_on\r\n");
			return NRF_ERROR_INTERNAL;
		}
	} else {
		DBG_PRINTF("ERR!!! led_indexFailed!  %d\r\n", led_index);
		return NRF_ERROR_INTERNAL;
	}

	return err_code;
}

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

/**
 * @brief Turn on specified photodetector
 * - Charles KWON
 *
 * Activates one of the 4 PD channels via FSA5157P6X analog switch.
 *
 * PD Channels:
 *   - 0: PD0 (for LED 0-23)
 *   - 1: PD1 (for LED 24-47)
 *   - 2: MOD (lock-in modulation)
 *   - 3: MOD2 (secondary modulation)
 *
 * @param[in] pd_index Photodetector index (0-3)
 * @return NRF_SUCCESS on success, NRF_ERROR_INTERNAL on failure
 */
ret_code_t pd_on(uint8_t pd_index)
{
	uint32_t err_code = NRF_SUCCESS;

#if FEATURE_FOR_SCOPE
	/* Debug pulse for oscilloscope timing */
	nrf_gpio_pin_set(PD_CLK_26);
	nrf_gpio_pin_clear(PD_CLK_26);
#endif

#if FEATURE_PRINTF
	DBG_PRINTF("pd_on, %d ===== \r\n", pd_index);
#endif

	if (pd_index <= 3) {
		activated_pd = pd_index;
		if (NRF_SUCCESS != pd_select(pd_index)) {
			DBG_PRINTF("ERR!!! pd_on Failed! %d\r\n", pd_index);
			return NRF_ERROR_INTERNAL;
		}
	} else {
		DBG_PRINTF("ERR!!! pd_index Failed! %d\r\n", pd_index);
		return NRF_ERROR_INTERNAL;
	}

	return err_code;
}


/**
 * @brief Turn off photodetector
 * - Charles KWON
 *
 * Deactivates all PD channels. Only accepts index 99 for safety.
 *
 * @param[in] pd_index Must be 99 for all-off operation
 * @return NRF_SUCCESS on success, NRF_ERROR_INTERNAL on invalid index
 */
ret_code_t pd_off(uint8_t pd_index)
{
	uint32_t err_code = NRF_SUCCESS;

#if FEATURE_PRINTF
	DBG_PRINTF("pd_off\r\n");
#endif

	if (pd_index == 99) {
		PD_ALLOFF();
		activated_pd = 99;
	} else {
		DBG_PRINTF("ERR!!! PD Off Failed! %d\r\n", pd_index);
		return NRF_ERROR_INTERNAL;
	}

	return err_code;
}

/*============================================================================*/
/* LED Power EEPROM Functions - Charles KWON                                  */
/*============================================================================*/

/**
 * @brief Read all 24 LED power values from ROM
 * - Charles KWON
 *
 * Reads LED power calibration values from DS3930 EEPROM.
 * Each read has 1ms delay for EEPROM access timing.
 *
 * @param[out] data Output buffer for 24 uint16_t values
 */
void led_power_read_48(uint16_t *data)
{
	for (uint8_t i = 0; i < 24; i++) {
		data[i] = LED_READ_ROM(i);
		nrf_delay_ms(1);  /* EEPROM access delay */
	}
}


/**
 * @brief Read single LED power value from ROM
 * - Charles KWON
 *
 * @param[in] led_index LED index (0-47)
 * @return LED power value, or NRF_ERROR_INTERNAL on invalid index
 */
int16_t led_power_read(uint8_t led_index)
{
	int16_t led_power;

	if (led_index <= 47) {
		led_power = LED_READ_ROM(led_index);
	} else {
		DBG_PRINTF("ERR!!! led_index Failed! %d\r\n", led_index);
		return NRF_ERROR_INTERNAL;
	}

	return led_power;
}


/**
 * @brief Save LED power value to ROM
 * - Charles KWON
 *
 * Writes LED power calibration value to DS3930 EEPROM.
 *
 * @param[in] led_index LED index (0-47)
 * @param[in] led_power Power value (0-255)
 * @return NRF_SUCCESS on success, NRF_ERROR_INTERNAL on failure
 */
ret_code_t led_power_save_mem(uint8_t led_index, int16_t led_power)
{
	uint32_t err_code = NRF_SUCCESS;

	if ((led_index <= 47) || (led_power <= 255)) {
		if (NRF_SUCCESS != LED_WRITE_ROM(led_index, led_power)) {
			DBG_PRINTF("ERR!!! DS3930 1\r\n");
			err_code = NRF_ERROR_INTERNAL;
		}
	} else {
		DBG_PRINTF("ERR!!! led_index || pd_index Failed! %d, %d\r\n", led_index, led_power);
		return NRF_ERROR_INTERNAL;
	}

	return err_code;
}


/**
 * @brief Save same LED power value to all 48 LEDs
 * - Charles KWON
 *
 * Writes identical power value to all LED EEPROM locations.
 * Useful for factory reset or uniform calibration.
 *
 * @param[in] led_index Unused parameter
 * @param[in] led_power Power value to set for all LEDs
 * @return NRF_SUCCESS on success, NRF_ERROR_INTERNAL on any write failure
 *
 * @note 10ms delay between writes for EEPROM programming time
 */
ret_code_t led_power_save_mem_6(uint8_t led_index, int16_t led_power)
{
	uint32_t err_code = NRF_SUCCESS;

	for (uint8_t i = 0; i < 48; i++) {
		if (NRF_SUCCESS != LED_WRITE_ROM(i, led_power)) {
			DBG_PRINTF("ERR!!! DS3930 1\r\n");
			err_code = NRF_ERROR_INTERNAL;
		}
		nrf_delay_ms(10);  /* EEPROM programming delay */
	}

	return err_code;
}


/**
 * @brief Save array of LED power values to all 48 LEDs
 * - Charles KWON
 *
 * Writes individual power values for each LED from input array.
 *
 * @param[in] led_power Array of 48 power values (uint8_t)
 * @return NRF_SUCCESS on success, NRF_ERROR_INTERNAL on any write failure
 *
 * @note 10ms delay between writes for EEPROM programming time
 */
ret_code_t led_power_save_mem_48(uint8_t *led_power)
{
	uint32_t err_code = NRF_SUCCESS;

	for (uint8_t i = 0; i < 48; i++) {
		if (NRF_SUCCESS != LED_WRITE_ROM(i, (int16_t)(led_power[i]))) {
			DBG_PRINTF("ERR!!! DS3930 1\r\n");
			err_code = NRF_ERROR_INTERNAL;
		}
		nrf_delay_ms(10);  /* EEPROM programming delay */
	}

	return err_code;
}


/**
 * @brief Set LED power (placeholder function)
 * - Charles KWON
 *
 * Currently not implemented. Reserved for future LED current control.
 *
 * @param[in] led_index LED index
 * @return NRF_SUCCESS always
 */
ret_code_t led_power_set(uint8_t led_index)
{
	uint32_t err_code = NRF_SUCCESS;
	return err_code;
}

/*============================================================================*/
/* DAC Gain Control Functions - Charles KWON                                  */
/*============================================================================*/

/**
 * @brief Set PD gain via MCP4725 DAC based on LED-PD matching table
 * - Charles KWON
 *
 * Looks up the calibrated DAC value for the specified LED from
 * led_pd_dac_v[] array and programs the MCP4725 DAC.
 *
 * @param[in] activated_led LED index for gain lookup (0-47, 99=off)
 * @return NRF_SUCCESS on success, NRF_ERROR_INTERNAL on invalid index
 *
 * @note Uses SW I2C for MCP4725 communication
 * @note LED 99 uses led_off_dac_v as default gain
 */
ret_code_t pd_gain_set(uint8_t activated_led)
{
	uint32_t err_code = NRF_SUCCESS;

#if FEATURE_PRINTF
	DBG_PRINTF("<<pd_gain_set L%d, P%d>>\r\n", activated_led, pd_index);
#endif

	if (activated_led <= 47) {
		sw_i2c_init_once();  /* Ensure I2C is ready */
		mcp4725_writeFastMode(led_pd_dac_v[activated_led]);
	} else if (activated_led == 99) {
		mcp4725_writeFastMode(led_off_dac_v);  /* Use default off-state gain */
	} else {
		DBG_PRINTF("ERR!!! led_index Failed!  %d\r\n", activated_led);
		return NRF_ERROR_INTERNAL;
	}

	return err_code;
}


/**
 * @brief Set immediate DAC gain value
 * - Charles KWON
 *
 * Directly programs the MCP4725 DAC with specified value.
 * Used for manual gain adjustment and testing.
 *
 * @param[in] imm_dac DAC value (0-2000)
 * @return NRF_SUCCESS on success, NRF_ERROR_INTERNAL on out-of-range
 */
ret_code_t imm_gain_set(uint16_t imm_dac)
{
	uint32_t err_code = NRF_SUCCESS;

	if (imm_dac <= 2000) {
		sw_i2c_init_once();
		mcp4725_writeFastMode(imm_dac);
		DBG_PRINTF("dac_v %d\r\n", imm_dac);
	} else {
		DBG_PRINTF("ERR!!! range Failed!  %d\r\n", activated_led);
		return NRF_ERROR_INTERNAL;
	}

	return err_code;
}

/*============================================================================*/
/* Hardware Selection Functions - Charles KWON                                */
/*============================================================================*/

/**
 * @brief Select and activate specific LED via GPIO multiplexer
 * - Charles KWON
 *
 * Programs the CAT9532 LED driver to activate the specified LED.
 * LED selection is done via I2C commands to set the appropriate
 * output pins on the LED driver IC.
 *
 * @param[in] led_index LED index (0-47 for specific LED, 99=clear/off)
 * @return NRF_SUCCESS on success, NRF_ERROR_NOT_FOUND on invalid index
 *
 * @note 1ms delay after selection for GPIO stabilization
 * @note LED macros (LED0, LED1, etc.) are defined in LED_Parse.h
 */
ret_code_t led_select(uint8_t led_index)
{
	uint32_t err_code = NRF_SUCCESS;

	switch (led_index) {
		case 0:  LED0();  break;
		case 1:  LED1();  break;
		case 2:  LED2();  break;
		case 3:  LED3();  break;
		case 4:  LED4();  break;
		case 5:  LED5();  break;
		case 6:  LED6();  break;
		case 7:  LED7();  break;
		case 8:  LED8();  break;
		case 9:  LED9();  break;
		case 10: LED10(); break;
		case 11: LED11(); break;
		case 12: LED12(); break;
		case 13: LED13(); break;
		case 14: LED14(); break;
		case 15: LED15(); break;
		case 16: LED16(); break;
		case 17: LED17(); break;
		case 18: LED18(); break;
		case 19: LED19(); break;
		case 20: LED20(); break;
		case 21: LED21(); break;
		case 22: LED22(); break;
		case 23: LED23(); break;
		case 24: LED24(); break;
		case 25: LED25(); break;
		case 26: LED26(); break;
		case 27: LED27(); break;
		case 28: LED28(); break;
		case 29: LED29(); break;
		case 30: LED30(); break;
		case 31: LED31(); break;
		case 32: LED32(); break;
		case 33: LED33(); break;
		case 34: LED34(); break;
		case 35: LED35(); break;
		case 36: LED36(); break;
		case 37: LED37(); break;
		case 38: LED38(); break;
		case 39: LED39(); break;
		case 40: LED40(); break;
		case 41: LED41(); break;
		case 42: LED42(); break;
		case 43: LED43(); break;
		case 44: LED44(); break;
		case 45: LED45(); break;
		case 46: LED46(); break;
		case 47: LED47(); break;
		case 99: LED99(); break;  /* All LEDs off / clear state */
		default:
			err_code = NRF_ERROR_NOT_FOUND;
			break;
	}

	/* GPIO stabilization delay */
	nrf_delay_ms(1);

	return err_code;
}


/**
 * @brief Select photodetector channel
 * - Charles KWON
 *
 * Programs the FSA5157P6X analog switch to route the selected
 * PD output to the ADC input.
 *
 * PD Channel Mapping:
 *   - 0: PD0  - Primary photodetector (LED 0-23)
 *   - 1: PD1  - Secondary photodetector (LED 24-47)
 *   - 2: MOD  - Lock-in amplifier modulation channel
 *   - 3: MOD2 - Secondary modulation channel
 *
 * @param[in] pd_index Photodetector index (0-3)
 * @return NRF_SUCCESS on success, NRF_ERROR_NOT_FOUND on invalid index
 *
 * @note All PD channels are turned off before selecting new one
 */
ret_code_t pd_select(uint8_t pd_index)
{
	uint32_t err_code = NRF_SUCCESS;

	PD_ALLOFF();  /* Ensure clean state before switching */

	switch (pd_index) {
		case 0: PD0();  break;  /* Photodetector 0 */
		case 1: PD1();  break;  /* Photodetector 1 */
		case 2: MOD();  break;  /* Modulation channel */
		case 3: MOD2(); break;  /* Secondary modulation */
		default:
			err_code = NRF_ERROR_NOT_FOUND;
			break;
	}

	return err_code;
}

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

/**
 * @brief Start measurement send timer
 * - Charles KWON
 *
 * Starts the single-shot timer for sequenced BLE data transmission.
 */
void mea_send_timer_start(void)
{
	APP_ERROR_CHECK(app_timer_start(m_mea_send_loop_timer_id,
	                                APP_TIMER_TICKS(MEA_SEND_LOOP_INTERVAL), NULL));
}


/**
 * @brief Stop measurement send timer
 * - Charles KWON
 */
void mea_send_timer_stop(void)
{
	APP_ERROR_CHECK(app_timer_stop(m_mea_send_loop_timer_id));
}


/**
 * @brief Initialize measurement send timer
 * - Charles KWON
 *
 * Creates the single-shot timer used for sequenced BLE transmission.
 * Must be called during system initialization.
 */
void mea_send_timer_init(void)
{
	APP_ERROR_CHECK(app_timer_create(&m_mea_send_loop_timer_id,
	                                  APP_TIMER_MODE_SINGLE_SHOT, mea_send_loop));
}
