#include "serial_control.h"

#include "board.h"
#include "debug.h"
#include "isr_opt.h"
#include "serial.h"
#include "usart.h"

// Приватные функции
ISR_FAST static uint32_t calculate_crc32(const uint8_t* data, uint16_t length);
ISR_FAST static uint16_t encode_packet(const uint8_t* payload, uint16_t payload_len, uint8_t* output, uint8_t response_code);
ISR_FAST static uint8_t parse_packet(const uint8_t* packet_data, uint16_t packet_len, ReceivedCommand_t* out_cmd);
ISR_FAST static uint8_t process_received_packet(SerialControl_t *ctx, const uint8_t* packet_data, uint16_t packet_len);
static void SC_UART2_Watchdog(void);
static void SC_ArmUart2RxDma(void);
static void SC_ArmUart5RxDma(void);
static void SC_LogUartError(const char *tag, UART_HandleTypeDef *huart);

uint8_t test_crc_invalid = 0;

SerialControl_t serial_control;
// Контекст для приема пакетов по UART5 (однонаправленный UART)
static SerialControl_t serial_iso;
volatile SC_Source_t g_sc_command_source = SC_SOURCE_UART2;
static volatile uint8_t sc_uart2_timed_out = 0;
static uint32_t sc_uart2_last_packet_tick = 0;
static uint32_t sc_uart2_last_recover_tick = 0;

#define SC_UART2_RECOVER_GUARD_MS       200u
#define SC_UART2_PACKET_TIMEOUT_MS     5000u

StatusPacket_t statusPacket = {
    .SOC = 0,
    .Energy = 0,
    .RequestedVoltage = 0,
    .RequestedCurrent = 0,
    .MeasuredVoltage = 0,
    .MeasuredCurrent = 0,
    .outputEnabled = 0,
    .chargingError = 0,
    .connState = 0,
    .chargingElapsedTimeMin = 0,
    .chargingElapsedTimeSec = 0,
    .estimatedRemainingChargingTime = 0,
    .relayAC = 0,
    .relayDC = 0,
    .relayAUX = 0,
    .lockState = 0,
    .evInfoAvailable = 0,
    .psuOnline = 0,
    .tempConnector0 = 0,
    .tempConnector1 = 0,
    .tempAmbient = 0,
    .tempBatteryMax = 0,
    .tempBatteryMin = 0,
    .highestVoltageOfBatteryCell = 0,
    .batteryStatus = 0,
    .phaseVoltageAB = 0,
    .phaseVoltageBC = 0,
    .phaseVoltageCA = 0,
};

InfoPacket_t infoPacket = {
    .serialNumber = 0,
    .boardVersion = 0,
    .stationType = 0,
    .fw_version_major = 0,
    .fw_version_minor = 0,
    .fw_version_patch = 0,
};

void ReadVersion(){
	infoPacket.serialNumber = InfoBlock->serialNumber;
    infoPacket.boardVersion = InfoBlock->boardVersion;
    infoPacket.stationType = InfoBlock->stationType;
	infoPacket.fw_version_major = FW_VERSION_MAJOR;
	infoPacket.fw_version_minor = FW_VERSION_MINOR;
	infoPacket.fw_version_patch = FW_VERSION_PATCH;
}

// Внешняя функция обработки команд (определена в serial_handler.c)
extern void SC_CommandHandler(ReceivedCommand_t* cmd);

void SC_Init() {
    // Обнуляем структуру
    memset(&serial_control, 0, sizeof(SerialControl_t));
    memset(&serial_iso, 0, sizeof(serial_iso));
    sc_uart2_timed_out = 0;
    sc_uart2_last_packet_tick = HAL_GetTick();
    sc_uart2_last_recover_tick = sc_uart2_last_packet_tick;
    SC_ArmUart2RxDma();
    SC_ArmUart5RxDma();
}

void SC_Task() {
    static uint32_t tick;
    if ((int32_t)(HAL_GetTick() - tick) < 1) return;
    tick = HAL_GetTick();
    SC_UART2_Watchdog();

	// Запуск приема в режиме DMA + idle
    SC_ArmUart2RxDma();
    SC_ArmUart5RxDma();
    
    // Проверка таймаута отправки пакета (больше 100 мс)
    if (huart2.gState == HAL_UART_STATE_BUSY_TX && serial_control.tx_tick != 0) {
        if ((int32_t)(HAL_GetTick() - serial_control.tx_tick) > 100) {
            // Таймаут: принудительно сбрасываем передачу
            (void)HAL_UART_AbortTransmit(&huart2);
            serial_control.tx_tick = 0; // Сбрасываем tick
        }
    }

    // Проверка наличия принятой команды для обработки
    if (serial_control.command_ready && (huart2.gState != HAL_UART_STATE_BUSY_TX)) {
//        HAL_Delay(2);
        SC_CommandHandler((ReceivedCommand_t*)&serial_control.received_command);
        serial_control.command_ready = 0; // Сбрасываем флаг
        SC_ArmUart2RxDma();
    }
}

ISR_FAST void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) {
    if (huart->Instance == huart2.Instance) {
        if (Size == 0u) {
            log_printf(LOG_WARN, "UART2 RX idle event with zero size\n");
        }
        sc_uart2_last_packet_tick = HAL_GetTick();
        sc_uart2_last_recover_tick = sc_uart2_last_packet_tick;
        sc_uart2_timed_out = 0;
        if(!process_received_packet(&serial_control, serial_control.rx_buffer, Size)){
            log_printf(LOG_WARN, "UART2 RX invalid packet len=%u\n", (unsigned)Size);
            SC_SendPacket(NULL, 0, RESP_INVALID);
        }
        g_sc_command_source = SC_SOURCE_UART2;
        SC_ArmUart2RxDma();
    } else if (huart->Instance == huart5.Instance) {
        if (Size == 0u) {
            log_printf(LOG_WARN, "UART5 RX idle event with zero size\n");
        }
        if (process_received_packet(&serial_iso, serial_iso.rx_buffer, Size)) {
            g_sc_command_source = SC_SOURCE_UART5;
            SC_CommandHandler((ReceivedCommand_t*)&serial_iso.received_command);
        } else {
            log_printf(LOG_WARN, "UART5 RX invalid packet len=%u\n", (unsigned)Size);
        }
        SC_ArmUart5RxDma();
    } else if (huart->Instance == huart3.Instance) {
        CCS_RxEventCallback(huart, Size);
    }
}

ISR_FAST void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {
    if (huart->Instance == huart2.Instance) {
        serial_control.tx_tick = 0;
    } else if (huart->Instance == huart3.Instance) {
        CCS_TxCpltCallback(huart);
    }
}

// Приватные функции реализации

// Полностью программная реализация CRC-32 (полином CRC32_POLYNOMIAL, порядок little-endian)
ISR_FAST static uint32_t calculate_crc32(const uint8_t* data, uint16_t length) {
    uint32_t crc = 0xFFFFFFFFu;

    for (uint16_t i = 0; i < length; i++) {
        crc ^= data[i];
        for (uint8_t bit = 0; bit < 8; bit++) {
            if (crc & 0x1u) {
                crc = (crc >> 1) ^ CRC32_POLYNOMIAL;
            } else {
                crc >>= 1;
            }
        }
    }

    return crc ^ 0xFFFFFFFFu;
}

ISR_FAST static uint16_t encode_packet(const uint8_t* payload, uint16_t payload_len, uint8_t* output, uint8_t response_code) {
    uint16_t out_index = 0;

    output[out_index++] = response_code;

    if (payload != NULL) {
        // Просто копируем полезную нагрузку без какого‑либо экранирования
        for (uint16_t i = 0; i < payload_len; i++) {
            output[out_index++] = payload[i];

            // Проверка переполнения
            if (out_index >= MAX_TX_BUFFER_SIZE - 5) { // 4 байта CRC + END_BYTE
                return 0;
            }
        }
    }

    // Вычисляем CRC для всего содержимого (код ответа + полезная нагрузка)
    uint32_t crc = calculate_crc32(output, out_index);
    uint8_t* crc_bytes = (uint8_t*)&crc;

    // Добавляем CRC без экранирования
    for (int i = 0; i < 4; i++) {
        output[out_index++] = crc_bytes[i];

        if (out_index >= MAX_TX_BUFFER_SIZE - 1) { // место для END_BYTE
            return 0;
        }
    }

    return out_index;
}

ISR_FAST void SC_SendPacket(const uint8_t* payload, uint16_t payload_len, uint8_t response_code) {
    uint16_t packet_len = encode_packet(payload, payload_len, serial_control.tx_buffer, response_code);

    if (packet_len > 0) {
        if (huart2.gState != HAL_UART_STATE_READY) {
            (void)HAL_UART_AbortTransmit(&huart2);
            log_printf(LOG_WARN, "UART2 TX busy, abort transmit before resend\n");
        }
        if (HAL_UART_Transmit_DMA(&huart2, serial_control.tx_buffer, packet_len) != HAL_OK) {
            SC_LogUartError("UART2 TX DMA start failed", &huart2);
            return;
        }

        serial_control.tx_tick = HAL_GetTick();
    }
}

ISR_FAST static uint8_t parse_packet(const uint8_t* packet_data, uint16_t packet_len, ReceivedCommand_t* out_cmd) {
    // if (test_crc_invalid && (packet_data[1] != CMD_GET_STATUS)) {
    //     test_crc_invalid--;
    //     return 0;
    // }else{
    //     test_crc_invalid = 5;
    // }

    // Минимальный размер: 1 байт команды + 4 байта CRC
    if (packet_len < 5) return 0;
    if (packet_len > MAX_RX_BUFFER_SIZE) return 0;

    uint16_t payload_length = packet_len - 4;

    // Извлекаем принятую CRC (последние 4 байта, little-endian)
    uint32_t received_checksum =
        ((uint32_t)packet_data[payload_length] << 0)   |
        ((uint32_t)packet_data[payload_length + 1] << 8)  |
        ((uint32_t)packet_data[payload_length + 2] << 16) |
        ((uint32_t)packet_data[payload_length + 3] << 24);

    // Вычисляем CRC для полезной нагрузки
    uint32_t calculated_checksum = calculate_crc32(packet_data, payload_length);

    if (received_checksum != calculated_checksum) return 0; // CRC не совпадает

    out_cmd->argument = (void *)&packet_data[1];
    out_cmd->command = packet_data[0];
    out_cmd->argument_length = (uint8_t)(payload_length - 1);

    return 1;
}

ISR_FAST static uint8_t process_received_packet(SerialControl_t *ctx, const uint8_t* packet_data, uint16_t packet_len) {
    if (!parse_packet(packet_data, packet_len, (ReceivedCommand_t *)&ctx->received_command)) {
        return 0;
    }

    ctx->command_ready = 1;
    return 1;
}

static void SC_UART2_Watchdog(void) {
    const uint32_t now = HAL_GetTick();
    const int32_t since_last_packet = (int32_t)(now - sc_uart2_last_packet_tick);

    if (since_last_packet >= (int32_t)SC_UART2_PACKET_TIMEOUT_MS) {
        if (sc_uart2_timed_out == 0u) {
            serial_control.command_ready = 0;
            log_printf(LOG_WARN, "UART2 RX packet timeout (%u ms)\n", (unsigned)SC_UART2_PACKET_TIMEOUT_MS);
        }
        sc_uart2_timed_out = 1;
    } else {
        sc_uart2_timed_out = 0;
    }

    if ((huart2.RxState == HAL_UART_STATE_READY) &&
        ((int32_t)(now - sc_uart2_last_recover_tick) >= (int32_t)SC_UART2_RECOVER_GUARD_MS)) {
        SC_ArmUart2RxDma();
        sc_uart2_last_recover_tick = now;
    }
}

static void SC_ArmUart2RxDma(void) {
    if ((huart2.RxState == HAL_UART_STATE_READY) && (serial_control.command_ready == 0)) {
        if (HAL_UARTEx_ReceiveToIdle_DMA(&huart2, serial_control.rx_buffer, MAX_RX_BUFFER_SIZE - 1) != HAL_OK) {
            SC_LogUartError("UART2 RX DMA arm failed", &huart2);
        }
    }
}

static void SC_ArmUart5RxDma(void) {
    if (huart5.RxState == HAL_UART_STATE_READY) {
        if (HAL_UARTEx_ReceiveToIdle_IT(&huart5, serial_iso.rx_buffer, MAX_RX_BUFFER_SIZE - 1) == HAL_OK) {
            return;
        }
        SC_LogUartError("UART5 RX IT arm failed", &huart5);
    }
}

void SC_RecoverUartDma(UART_HandleTypeDef *huart) {
    if (huart == &huart2) {
        SC_LogUartError("UART2 recover start", &huart2);
        (void)HAL_UART_AbortReceive(&huart2);
        (void)HAL_UART_AbortTransmit(&huart2);
        serial_control.tx_tick = 0;
        SC_ArmUart2RxDma();
        sc_uart2_last_recover_tick = HAL_GetTick();
    } else if (huart == &huart5) {
        SC_LogUartError("UART5 recover start", &huart5);
        (void)HAL_UART_AbortReceive(&huart5);
        SC_ArmUart5RxDma();
    }
}

static void SC_LogUartError(const char *tag, UART_HandleTypeDef *huart) {
    if (tag == NULL || huart == NULL) {
        return;
    }
    log_printf(LOG_ERR, "%s: instance=0x%08lx err=0x%08lx g=%lu rx=%lu\n",
               tag,
               (unsigned long)huart->Instance,
               (unsigned long)HAL_UART_GetError(huart),
               (unsigned long)huart->gState,
               (unsigned long)huart->RxState);
}

