/*
 * Copyright (C) 2019      Kai Ludwig, DG4KLU
 * Copyright (C) 2019-2025 Roger Clark, VK3KYY / G4KYF
 *
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
 *    in the documentation and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * 4. Use of this source code or binary releases for commercial purposes is strictly forbidden. This includes, without limitation,
 *    incorporation in a commercial product or incorporation into a product or project which allows commercial use.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include "interfaces/adc.h"
#include "functions/settings.h"
#include "interfaces/batteryAndPowerManagement.h"
#include "functions/ticks.h"


const int TEMPERATURE_DECIMAL_RESOLUTION = 1000000;
const int CUTOFF_VOLTAGE_UPPER_HYST = 64;
const int CUTOFF_VOLTAGE_LOWER_HYST = 62;
const int BATTERY_MAX_VOLTAGE = 82;
const int POWEROFF_VOLTAGE_THRESHOLD = 55;

#if defined(PLATFORM_MDUV380)
#define BATTERY_ADC_COEFF 40.3f
#else
#define BATTERY_ADC_COEFF 37.5f
#endif


//lookup table to convert linear Voltage values between 1 and 255 to dBs relative to 1 with a resolution of 0.2dB.  Values in tabble are dbs * 5 to use all of the available byte range.
//calculated using dBs= (20 * Log10(index)) * 5
const uint8_t PointTwodBs[] =
{
		0,   0,   30,  48,  60,  70,  78,  85,  90,  95,  100, 104, 108, 111, 115, 118,
		120, 123, 126, 128, 130, 132, 134, 136, 138, 140, 141, 143, 145, 146, 148, 149,
		151, 152, 153, 154, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167,
		168, 169, 170, 171, 172, 172, 173, 174, 175, 176, 176, 177, 178, 179, 179, 180,
		181, 181, 182, 183, 183, 184, 185, 185, 186, 186, 187, 188, 188, 189, 189, 190,
		190, 191, 191, 192, 192, 193, 193, 194, 194, 195, 195, 196, 196, 197, 197, 198,
		198, 199, 199, 200, 200, 200, 201, 201, 202, 202, 203, 203, 203, 204, 204, 205,
		205, 205, 206, 206, 206, 207, 207, 208, 208, 208, 209, 209, 209, 210, 210, 210,
		211, 211, 211, 212, 212, 212, 213, 213, 213, 214, 214, 214, 215, 215, 215, 216,
		216, 216, 216, 217, 217, 217, 218, 218, 218, 218, 219, 219, 219, 220, 220, 220,
		220, 221, 221, 221, 221, 222, 222, 222, 223, 223, 223, 223, 224, 224, 224, 224,
		225, 225, 225, 225, 226, 226, 226, 226, 226, 227, 227, 227, 227, 228, 228, 228,
		228, 229, 229, 229, 229, 229, 230, 230, 230, 230, 231, 231, 231, 231, 231, 232,
		232, 232, 232, 232, 233, 233, 233, 233, 233, 234, 234, 234, 234, 234, 235, 235,
		235, 235, 235, 236, 236, 236, 236, 236, 237, 237, 237, 237, 237, 237, 238, 238,
		238, 238, 238, 239, 239, 239, 239, 239, 239, 240, 240, 240, 240, 240, 240, 241
};

volatile uint16_t adcVal[NUM_ADC_CHANNELS];
static const int AVERAGE_BATTERY_VOLTAGE_SAMPLE_WINDOW = 1000.0f;
static int16_t averagedVolume = 0; // value is (* 16) the real value.

void adcStartDMA(void)
{
	HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&adcVal, NUM_ADC_CHANNELS);
	HAL_TIM_Base_Start_IT(&htim3);
}

//void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc){} // Don't need to use the half full callback at the moment

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
	potLevel = adcVal[0];
	batteryVoltage = ((int)adcVal[1] / BATTERY_ADC_COEFF) + ((nonVolatileSettings.batteryCalibration & 0x0F) - 5);
	micLevel = adcVal[2];
	temperatureLevel = adcVal[3];

	// Handle switching the power off, using the rotary control.
	if (batteryVoltage > POWEROFF_VOLTAGE_THRESHOLD)
	{
		lastValidBatteryVoltage = batteryVoltage;
	}

	if (ticksGetMillis() < (BATTERY_VOLTAGE_STABILISATION_TIME + resumeTicks))
	{
		averageBatteryVoltage = batteryVoltage;
	}
	else
	{
		averageBatteryVoltage = (averageBatteryVoltage * (AVERAGE_BATTERY_VOLTAGE_SAMPLE_WINDOW - 1) + batteryVoltage) / AVERAGE_BATTERY_VOLTAGE_SAMPLE_WINDOW;
	}
}

int adcGetBatteryVoltage(void)
{
	return batteryVoltage;			//doesn't need averaging so just use the most recent conversion
}

int adcGetVOX(void)
{
	return ((micLevel > 255) ? 255 : PointTwodBs[micLevel]);
}

int getTemperature(void)
{
	const int tV25 = 943;					// ADC Value at 25 degrees from data sheet (0.76V with 12 Bit ADC ref=3V3)
    const int tslope = 31;	                //Slope from datasheet ADC Counts *10

    return (((temperatureLevel - tV25) * 100) / tslope) + 250 + (nonVolatileSettings.temperatureCalibration * 5);
}

#if defined(PLATFORM_RT84_DM1701)
#define MIN_VOL_ADC_LOW 34 // 29 + 5
#define MAX_VOL_ADC_VAL 2070 // 2075 - 5
#define VOL_POT_ADC_MID_POINT 318
#define MIN_VOL_LOW_TO_HIGH_OFFSET 5
#elif defined(PLATFORM_VARIANT_UV380_PLUS_10W)
#define MIN_VOL_ADC_LOW 24 // 19 + 5
#define MAX_VOL_ADC_VAL 2049 // 2054 - 5
#define VOL_POT_ADC_MID_POINT 308
#define MIN_VOL_LOW_TO_HIGH_OFFSET 5
#elif defined(PLATFORM_MD2017)
#define MIN_VOL_ADC_LOW 37 // 32 + 5
#define MAX_VOL_ADC_VAL 2043 // 2048 - 5
#define VOL_POT_ADC_MID_POINT 321
#define MIN_VOL_LOW_TO_HIGH_OFFSET 5
#else
// COMMENTED: MD-UV380 values
//#define MIN_VOL_ADC_LOW 25 // 22 + 3
//#define MAX_VOL_ADC_VAL 2065 // 2070 - 5
//#define VOL_POT_ADC_MID_POINT 309
//#define MIN_VOL_LOW_TO_HIGH_OFFSET 5
// RT3S
#define MIN_VOL_ADC_LOW 30 // 23 + 7 // RT3S ADC reading is not really stable at lowest level
#define MAX_VOL_ADC_VAL 2015 // 2020 - 5
#define VOL_POT_ADC_MID_POINT 317
#define MIN_VOL_LOW_TO_HIGH_OFFSET 8
#endif

#define MIN_VOL_ADC_HIGH (MIN_VOL_ADC_LOW + MIN_VOL_LOW_TO_HIGH_OFFSET)
#define VOL_LOW_SIDE_DIV ((VOL_POT_ADC_MID_POINT - MIN_VOL_ADC_HIGH) / 31)
#define VOL_HIGH_SIDE_DIV ((MAX_VOL_ADC_VAL - VOL_POT_ADC_MID_POINT) / 31)

static int8_t getVolumeControlRaw(void)
{
	// volume control is adc[0]. Max value seems to be:
	//   - RT3S:             2020, min value seems to be 23
	//   - MD-UV380:         2070, min value seems to be 22
	//   - MD-UV380Plus 10W: 2054, min value seems to be 19
	//   - DM-1701:          2075, min value seems to be 29
	//   - MD-2017:          2048, min value seems to be 32
	// Pot is Log law which needs converting back to linear law
	// Log Law is approximated by two straight lines with breakpoint at 300
	int vol = potLevel;
	int pos;
	static bool minvol;

	if (vol < VOL_POT_ADC_MID_POINT)
	{
		pos = (vol - MIN_VOL_ADC_HIGH) / VOL_LOW_SIDE_DIV; // convert to 0-31
	}
	else
	{
		pos = 31 + ((vol - VOL_POT_ADC_MID_POINT) / VOL_HIGH_SIDE_DIV); // convert to 31-62
	}

	if (minvol ? (vol < MIN_VOL_ADC_HIGH) : (vol < MIN_VOL_ADC_LOW))
	{
		minvol = true;
		return -99; // -99 = volume min (muted)
	}

	minvol = false;
	return (int8_t)CLAMP(pos - 31, -31, 31);
}

int8_t getVolumeControl(void)
{
	int8_t v = getVolumeControlRaw();

	if ((v == -99) || ((v != -99) && (averagedVolume == (-99 << 4))))
	{
		averagedVolume = (v << 4);
		return v;
	}

	averagedVolume = (v + averagedVolume - ((averagedVolume - 8) >> 4));

	return (int8_t)CLAMP((averagedVolume >> 4), -31, 31);
}
