#include "board.h"

#include "adc.h"
#include "main.h"
#include "sma_filter.h"
#include "tim.h"

InfoBlock_t *InfoBlock = (InfoBlock_t *)(VERSION_OFFSET);

uint8_t RELAY_State[RELAY_COUNT];
static volatile uint8_t adc_lock = 0;
static SMAFilter_t conn_temp_adc_filter[2];

void RELAY_Write(relay_t num, uint8_t state){
	switch (num) {
	case RELAY_AUX0:
		HAL_GPIO_WritePin(RELAY1_GPIO_Port, RELAY1_Pin, state);
		break;
	case RELAY_AUX1:
		HAL_GPIO_WritePin(RELAY2_GPIO_Port, RELAY2_Pin, state);
		break;
	case RELAY3:
		HAL_GPIO_WritePin(RELAY3_GPIO_Port, RELAY3_Pin, state);
		break;
	case RELAY_DC:
		HAL_GPIO_WritePin(RELAY4_GPIO_Port, RELAY4_Pin, state);
		break;
	case RELAY_AC:
		HAL_GPIO_WritePin(RELAY5_GPIO_Port, RELAY5_Pin, state);
		break;
	case RELAY_CP:
		HAL_GPIO_WritePin(RELAY_CP_GPIO_Port, RELAY_CP_Pin, state);
		break;
	case RELAY_CC:
		HAL_GPIO_WritePin(RELAY_CC_GPIO_Port, RELAY_CC_Pin, state);
		break;
	case RELAY_DC1:
		HAL_GPIO_WritePin(RELAY_DC_GPIO_Port, RELAY_DC_Pin, state);
		break;
	default:
		break;
	}

	RELAY_State[num] = state;
}

uint8_t RELAY_Read(relay_t num){
	return RELAY_State[num];
}


uint8_t IN_ReadInput(inputNum_t input_n){
	switch(input_n){
	case IN_SW0:
		return HAL_GPIO_ReadPin(IN_SW0_GPIO_Port, IN_SW0_Pin);
	case IN_SW1:
		return HAL_GPIO_ReadPin(IN_SW1_GPIO_Port, IN_SW1_Pin);
	case IN0:
		return HAL_GPIO_ReadPin(IN0_GPIO_Port, IN0_Pin);
	case IN_ESTOP:
		return HAL_GPIO_ReadPin(IN_ESTOP_GPIO_Port, IN_ESTOP_Pin);
	case IN_FB1:
		return HAL_GPIO_ReadPin(IN_FB1_GPIO_Port, IN_FB1_Pin);
	case IN_CONT_FB_DC:
		return HAL_GPIO_ReadPin(IN_FB2_GPIO_Port, IN_FB2_Pin);
	case ISO_IN:
		return HAL_GPIO_ReadPin(ISO_IN_GPIO_Port, ISO_IN_Pin);
	default:
		return 0;
	}
}

/* Заглушка: температура платы не измеряется (нет реализации АЦП для канала). */
uint8_t GetBoardTemp(void){
	return 0;
}

/**
  * @brief  Force PC12 (HEATER) as GPIO output via CRH/ODR, overriding UART5 MspInit.
  *         CubeMX assigns PC12 to UART5_TX; on the board it drives the heater relay.
  */
static void Heater_PinForceOutput(void)
{
	const uint32_t pin_cfg = GPIO_SPEED_FREQ_LOW; /* MODE=10, CNF=00: GP output PP, 2 MHz */

	__HAL_RCC_GPIOC_CLK_ENABLE();

	MODIFY_REG(HEATER_GPIO_Port->CRH,
	           (GPIO_CRH_MODE12 | GPIO_CRH_CNF12),
	           (pin_cfg << GPIO_CRH_MODE12_Pos));

	HEATER_GPIO_Port->BSRR = (uint32_t)HEATER_Pin << 16U;
}

void Init_Peripheral(){
	Heater_PinForceOutput();
    HAL_GPIO_WritePin(HEATER_GPIO_Port, HEATER_Pin, GPIO_PIN_RESET);
    HAL_ADCEx_Calibration_Start(&hadc1);
    ADC_ScanStart();

    RELAY_Write(RELAY_AUX0, 0);
    RELAY_Write(RELAY_AUX1, 0);
    RELAY_Write(RELAY3, 0);
    RELAY_Write(RELAY_DC, 0);
    RELAY_Write(RELAY_AC, 0);
    RELAY_Write(RELAY_CP, 0);
    RELAY_Write(RELAY_CC, 0);
    RELAY_Write(RELAY_DC1, 0);

    SMAFilter_Init(&conn_temp_adc_filter[0]);
    SMAFilter_Init(&conn_temp_adc_filter[1]);
}

float pt1000_to_temperature(float resistance) {
    // Константы для PT1000
    const float R0 = 1000.0;  // Сопротивление при 0 °C
    const float C_A = 3.9083E-3f;
    
    float temperature = (resistance-R0) / ( R0 * C_A);

    return temperature;
}


float calculate_NTC_resistance(int adc_value, float Vref, float Vin, float R) {
    // Преобразуем значение АЦП в выходное напряжение
    float Vout = (adc_value / 4095.0) * Vref;

    // Проверяем, чтобы Vout не было равно Vin
    if (Vout >= Vin) {
        return -1; // Ошибка: Vout не может быть больше или равно Vin
    }

    // Вычисляем сопротивление термистора
    float R_NTC = R * (Vout / (Vin - Vout));

    return R_NTC;
}

int16_t CONN_ReadTemp(uint8_t ch){
    uint32_t adcValue = 0u;
    adcValue = ch ? adc_data.ntc2_raw : adc_data.ntc1_raw;

    int32_t adc_filtered = SMAFilter_Update(&conn_temp_adc_filter[ch ? 1u : 0u], (int32_t)adcValue);
    if((uint32_t)adc_filtered > 4000u) {
    	return 20; //Термодатчик не подключен
    }

    float Vref = 3.3;     // Напряжение опорное
    float Vin = 5.0;      // Входное напряжение
    float R = 1000;      // Сопротивление резистора в Омах

    float temp = pt1000_to_temperature(calculate_NTC_resistance((int)adc_filtered, Vref, Vin, R));

    return (int16_t)temp;

}

int16_t GBT_ReadTemp(uint8_t ch){
    return CONN_ReadTemp(ch);
}

void ADC_Select_Channel(uint32_t ch) {
    ADC_ChannelConfTypeDef conf = {
        .Channel = ch,
        .Rank = 1,
        .SamplingTime = ADC_SAMPLETIME_28CYCLES_5,
    };
    if (HAL_ADC_ConfigChannel(&hadc1, &conf) != HAL_OK) {
        Error_Handler();
    }
}

uint8_t ADC_TryLock(void) {
    uint32_t primask = __get_PRIMASK();
    __disable_irq();
    if (adc_lock != 0u) {
        if (primask == 0u) {
            __enable_irq();
        }
        return 0u;
    }
    adc_lock = 1u;
    if (primask == 0u) {
        __enable_irq();
    }
    return 1u;
}

void ADC_LockBlocking(void) {
    while (ADC_TryLock() == 0u) {
        /* wait in main context until ADC is free */
    }
}

void ADC_Unlock(void) {
    uint32_t primask = __get_PRIMASK();
    __disable_irq();
    adc_lock = 0u;
    if (primask == 0u) {
        __enable_irq();
    }
}
