/**
 * Selects the specified analog input, automatically handling any multiplexing and timing needs.
 *
 * @param input Analog_Input_t* The analog input to select.
 * @retval none
 */
void SelectAnalogInput(Analog_Input_t* input) {
	if (input != NULL ) {
		if (isExternalInput(input->physicalInput)) {
			SelectExternalInput(input->externalInput);
			CurrentInput = input;
		} else if (isInternalInput(input->physicalInput)) {
			SelectInternalInput(input->internalInput);
			CurrentInput = input;
		} else if (input->physicalInput == EXTERNAL_OFFSET_CAL) {
			SelectCalibrationInput();
			CurrentInput = input;
		} else {
			/* ERROR */
			snprintf(TOSTRING_BUFFER, sizeof(TOSTRING_BUFFER),
					"[Analog Input Multiplexer] Attempted to select an input which does not exist.");
			TelnetWriteErrorMessage(TOSTRING_BUFFER);
#ifdef INPUT_MULTIPLEXER_DEBUG
			printf("[Analog Input Multiplexer] Attempted to select an input which does not exist.\n\r");
#endif
		}
	} else {
#ifdef INPUT_MULTIPLEXER_DEBUG
		printf("[Analog Input Multiplexer] Attempted to select a NULL input.\n\r");
#endif
	}
}
/**
 * Retrieve the gain calibration value for the specified sampling parameters.
 *
 * @param rate ADS1256_SPS_t The sample rate to lookup for.
 * @param gain ADS1256_PGA_t The gain to lookup for.
 * @param buffer ADS1256_BUFFER_t The buffer setting to lookup for.
 * @param temperature float The temperature value to lookup for.
 * @retval The determined calibration value.
 */
uint32_t Tekdaqc_GetGainCalibration(ADS1256_SPS_t rate, ADS1256_PGA_t gain, ADS1256_BUFFER_t buffer, float temperature) {
	uint8_t rate_index = 0U;
	uint8_t gain_index = 0U;
	uint8_t buffer_index = 0U;
	ComputeTableIndices(&rate_index, &gain_index, &buffer_index, rate, gain, buffer);
	uint32_t baseGain = baseGainCalibrations[rate_index][gain_index][buffer_index];

	if (CALIBRATION_VALID != TRUE) {
#ifdef CALIBRATION_TABLE_DEBUG
		printf("[Calibration Table] The calibration table is not valid, returning ADC calibration only (0x%" PRIX32 ").\n\r", baseGain);
#endif
		return baseGain;
	}
	if (temperature < CAL_TEMP_LOW || temperature > CAL_TEMP_HIGH) {
		/* The temperature is out of range, we will return the closest */
#ifdef CALIBRATION_TABLE_DEBUG
		printf("[Calibration Table] The requested temperature %f was out of range. Minimum is %f and maximum is %f.\n\r", temperature,
				CAL_TEMP_LOW, CAL_TEMP_HIGH);
#endif
		snprintf(TOSTRING_BUFFER, sizeof(TOSTRING_BUFFER),
				"Error fetching the gain calibration value for temperature: %f Deg C. Temperature out of range. Allowable range is %f to %f Deg C",
				temperature, CAL_TEMP_LOW, CAL_TEMP_HIGH);
		TelnetWriteErrorMessage(TOSTRING_BUFFER);
		if (temperature < CAL_TEMP_LOW) {
			temperature = CAL_TEMP_LOW;
		} else {
			temperature = CAL_TEMP_HIGH;
		}
	}

	uint8_t num_temp_steps = (uint8_t) (temperature / CAL_TEMP_STEP);
	float low_temp = CAL_TEMP_LOW * num_temp_steps;
	float high_temp = CAL_TEMP_HIGH * num_temp_steps;
	float factor = (temperature - CAL_TEMP_LOW) / CAL_TEMP_STEP;

	uint32_t offset = ComputeOffset(rate, gain, buffer, low_temp);                                                                                                                                            //Add one for the move to gain
	uint32_t Address = CAL_DATA_START_ADDR + 4 * offset;                                                                                                                                            //Multiply offset by 4 because entries are 4bytes long

	uint32_t data_low = (*(__IO uint32_t*) Address);

	offset = ComputeOffset(rate, gain, buffer, high_temp);                                                                                                                                            //Add one for the move to gain
	Address = CAL_DATA_START_ADDR + 4 * offset;                                                                                                                                            //Multiply offset by 4 because entries are 4bytes long

	uint32_t data_high = (*(__IO uint32_t*) Address);
	return (baseGain + InterpolateValue(data_low, data_high, factor));
}
/**
 * @internal
 * Computes the address offset for the offset calibration value corresponding to the specified parameters.
 *
 * @param rate ADS1256_SPS_t The sample rate to lookup for.
 * @param gain ADS1256_PGA_t The gain to lookup for.
 * @param bufer ADS1256_BUFFER_t The buffer setting to lookup for.
 * @param temperature float The temperature value to lookup for.
 * @retval The computed address offset.
 */
static uint32_t ComputeOffset(ADS1256_SPS_t rate, ADS1256_PGA_t gain, ADS1256_BUFFER_t buffer, float temperature) {
	uint32_t offset = 0;
	switch (buffer) {
	case ADS1256_BUFFER_ENABLED:
		offset += CALIBRATION_BUFFER_OFFSET;
		break;
	case ADS1256_BUFFER_DISABLED:
		/* No offset, do nothing */
		break;
	default:
		snprintf(TOSTRING_BUFFER, sizeof(TOSTRING_BUFFER),
				"Error computing lookup offset in calibration table for rate: %s, gain: %s, buffer: %s and temperature: %f Deg C.",
				ADS1256_StringFromSPS(rate), ADS1256_StringFromPGA(gain), ADS1256_StringFromBuffer(buffer), temperature);
		TelnetWriteErrorMessage(TOSTRING_BUFFER);
		break;
	}
	return offset;
}