/*
 * kline.c
 *
 *  Created on: Aug 18, 2025
 *      Author: herli
 */

#include "kline.h"
#include "IKW1281Connection.h"
#include "hc_06.h"

#include "stdint.h"
#include "main.h"

uint8_t BitBang = 0;

void KLine_Send5Baud(uint8_t data)
{
    HAL_UART_DeInit(&huart1);

    // Reconfigure TX pin as output
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Pin = KLINE_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(KLINE_GPIO_PORT, &GPIO_InitStruct);

    // 5 baud = 200ms per bit
    const uint32_t bit_delay_ms = 200;

    // Start bit (low)
    HAL_GPIO_WritePin(KLINE_GPIO_PORT, KLINE_PIN, GPIO_PIN_RESET);
    HAL_Delay(bit_delay_ms);

    // Send 8 bits, LSB first
    for (int i = 0; i < 8; i++) {
        if (data & (1 << i)) {
            HAL_GPIO_WritePin(KLINE_GPIO_PORT, KLINE_PIN, GPIO_PIN_SET);
        } else {
            HAL_GPIO_WritePin(KLINE_GPIO_PORT, KLINE_PIN, GPIO_PIN_RESET);
        }
        HAL_Delay(bit_delay_ms);
    }

    // Stop bit (high)
    HAL_GPIO_WritePin(KLINE_GPIO_PORT, KLINE_PIN, GPIO_PIN_SET);
    HAL_Delay(bit_delay_ms);

    HAL_UART_Init(&huart1);   // ensures TX idles high, necessary for g431
    HAL_UART_Receive_IT(&huart1, RxData, 1);
}

int WakeUp(uint8_t controllerAddress, uint8_t evenParity){
	KLine_Send5Baud(controllerAddress);  // ECU address
	connectionAlive = 1;
	uint8_t syncByte = ReadByte();
	if(syncByte != 0x55){return 0;}
	uint8_t keywordLsb = ReadByte();
	uint8_t keywordMsb = ReadAndAckByte();

	int protocolVersion = ((keywordMsb & 0x7F) << 7) + (keywordLsb & 0x7F);
	return protocolVersion;
}

/*void PrintAsciiPayload(const ParsedPacket* packet, UART_HandleTypeDef* huart)
{
    char data[128];  // adjust size as needed
    int len = 0;

    // Start with a label
    len += sprintf(&data[len], "ASCII: ");

    for (int i = 4; i < packet->length - 1; i++) {
        char c = packet->raw[i] & 0x7F;
        if (len < sizeof(data) - 2) {
            data[len++] = c;
        }
    }

    // Null-terminate and add newline
    data[len++] = '\r\n';
    data[len] = '\0';

    HAL_UART_Transmit(huart, (uint8_t*)data, len, 1000);

    // Optional: report more data
    if (packet->raw[3] > 0x7F) {
        const char more[] = "More data available via ReadIdent\r\n";
        HAL_UART_Transmit(huart, (uint8_t*)more, sizeof(more) - 1, 1000);
    }
}*/

int FilterNonAckNak(ParsedPacket* all, int total, ParsedPacket* filtered, int max)
{
    int j = 0;
    for (int i = 0; i < total && j < max; i++) {
        if (!all[i].isAckNak) {
            filtered[j++] = all[i];
        }
    }
    return j;
}
static ControllerInfo info = {0};  // Zero init all strings

ControllerInfo ReadEcuInfo() {
    int packet_count = 0;
    ParsedPacket *packets = ReceivePackets(&packet_count);

    char combined[128] = {0};  // Temporary buffer to build full ASCII text

    if (packets != NULL) {
        for (int i = 0; i < packet_count; i++) {
            ParsedPacket *p = &packets[i];
            if (p->type == PACKET_TYPE_ASCII_DATA && p->length > 4) {
                size_t len = p->length - 4;  // Exclude first 3 and last byte
                strncat(combined, (char*)(p->raw + 3), len);
            }
        }
        //free(packets); // clean up when done
    }

    memset(&info, 0, sizeof(ControllerInfo));

    // Fill ControllerInfo fields from combined text
    strncpy(info.client_ident, combined, 12);
    strncpy(info.unk_ident1, combined + 12, 10);
    strncpy(info.soft_info,  combined + 22, 10);

    if (strlen(combined) > 40)
        strncpy(info.unk_ident2, combined + 32, 10);
    if (strlen(combined) > 50)
        strncpy(info.unk_ident3, combined + 42, 10);

    //free(combined);
    return info;
}

/*void PrintEcuInfo(ControllerInfo* info) {
    char data[64];

    sprintf(data, "client_ident: %s\t\r\n", info->client_ident);
    //HAL_UART_Transmit(&huart2, (uint8_t*)data, strlen(data), 1000);
    CDC_Transmit_FS((uint8_t*)data, strlen(data));
    HAL_Delay(10);   // allow USB stack to flush

    sprintf(data, "unk_ident1: %s\t\r\n", info->unk_ident1);
    //HAL_UART_Transmit(&huart2, (uint8_t*)data, strlen(data), 1000);
    CDC_Transmit_FS((uint8_t*)data, strlen(data));
    HAL_Delay(10);   // allow USB stack to flush

    sprintf(data, "soft_info: %s\t\r\n", info->soft_info);
    //HAL_UART_Transmit(&huart2, (uint8_t*)data, strlen(data), 1000);
    CDC_Transmit_FS((uint8_t*)data, strlen(data));
    HAL_Delay(10);   // allow USB stack to flush

    if (strlen(info->unk_ident2) > 0) {
        sprintf(data, "unk_ident2: %s\t\r\n", info->unk_ident2);
        //HAL_UART_Transmit(&huart2, (uint8_t*)data, strlen(data), 1000);
        CDC_Transmit_FS((uint8_t*)data, strlen(data));
        HAL_Delay(10);   // allow USB stack to flush

    }

    if (strlen(info->unk_ident3) > 0) {
        sprintf(data, "unk_ident3: %s\t\r\n", info->unk_ident3);
        CDC_Transmit_FS((uint8_t*)data, strlen(data));
        HAL_Delay(10);   // allow USB stack to flush

//        HAL_UART_Transmit(&huart2, (uint8_t*)data, strlen(data), 1000);
    }
}*/
#define MAX_EEPROM_DATA 256   // adjust to your ECU’s max response
#define MAX_PACKETS 16

static ParsedPacket packet_buffer[MAX_PACKETS];
static uint8_t eeprom_result[MAX_EEPROM_DATA];

uint8_t* ReadRomEeprom(uint16_t address, uint8_t count, int* outLength)
{
    uint8_t request[4] = {
        PACKET_CMD_ReadRomEeprom,  // 0x08 usually
        count,
        (uint8_t)(address >> 8),
        (uint8_t)(address & 0xFF)
    };

    int packetCount = 0;
    ParsedPacket* packets = SendCustom(request, sizeof(request), &packetCount);
    if (!packets || packetCount == 0) {
        *outLength = 0;
        return NULL;
    }

    // Check for NAK
    if (packetCount == 1 && packets[0].type == PACKET_TYPE_NAK) {
        *outLength = 0;
        return NULL;
    }

    // Collect useful packets (non-Ack/Nak)
    int usefulCount = 0;
    for (int i = 0; i < packetCount && usefulCount < MAX_PACKETS; i++) {
        if (!packets[i].isAckNak) {
            packet_buffer[usefulCount++] = packets[i];
        }
    }

    if (usefulCount != 1) {
        *outLength = 0;
        return NULL;  // Expect exactly one useful packet
    }

    int dataLen = packet_buffer[0].length - 4;  // skip 3-byte header + 1-byte end
    if (dataLen > MAX_EEPROM_DATA) {
        dataLen = MAX_EEPROM_DATA;  // clamp to buffer size
    }

    memcpy(eeprom_result, packet_buffer[0].raw + 3, dataLen);
    *outLength = dataLen;

    return eeprom_result;   // static buffer pointer
}
/*
uint8_t* ReadRomEeprom(uint16_t address, uint8_t count, int* outLength) {
    uint8_t request[4] = {
		PACKET_CMD_ReadRomEeprom,  // 0x08 usually
        count,
        (uint8_t)(address >> 8),
        (uint8_t)(address & 0xFF)
    };

    int packetCount = 0;
    ParsedPacket* packets = SendCustom(request, sizeof(request), &packetCount, 0);
    if (!packets || packetCount == 0) {
        *outLength = 0;
        return NULL;
    }

    // Check for NAK
    if (packetCount == 1 && packets[0].type == PACKET_TYPE_NAK) {
        freeParsedPackets(packets, packetCount);
        *outLength = 0;
        return NULL;
    }

    // Filter to exclude Ack/Nak
    ParsedPacket* useful = malloc(packetCount * sizeof(ParsedPacket));
    int usefulCount = 0;
    for (int i = 0; i < packetCount; i++) {
        if (!packets[i].isAckNak) {
            useful[usefulCount++] = packets[i];
        } else {
            // If dynamically allocated, free individual packet.raw
            free(packets[i].raw);
        }
    }
    free(packets);  // Free the original array pointer (but not the reused raw data)

    if (usefulCount != 1) {
        freeParsedPackets(useful, usefulCount);
        *outLength = 0;
        return NULL;  // Or handle as error
    }

    uint8_t* result = malloc(useful[0].length - 4);  // Remove 3-byte header + end (or however yours works)
    if (!result) {
        freeParsedPackets(useful, usefulCount);
        *outLength = 0;
        return NULL;
    }

    memcpy(result, useful[0].raw + 3, useful[0].length - 4);  // Adjust if header is different
    *outLength = useful[0].length - 4;

    freeParsedPackets(useful, usefulCount);
    return result;
}*/

void ReadSerialNumber(char* serialOut, int maxLen) {
    uint8_t request[] = { 0x19, 0x06, 0x00, 0x80 };

    int packetCount = 0;
    ParsedPacket* packets = SendCustom(request, sizeof(request), &packetCount);

    if (!packets || packetCount == 0) {
        serialOut[0] = '\0';
        return;
    }

    for (int i = 0; i < packetCount; i++) {
        ParsedPacket* p = &packets[i];
        if (!p->isAckNak && p->type == PACKET_TYPE_READ_EEPROM_RESPONSE) {
            int dataLen = p->length - 4;  // Skip length, counter, command, end byte
            if (dataLen > maxLen - 1) {
                dataLen = maxLen - 1;
            }

            memcpy(serialOut, p->raw + 3, dataLen);  // Adjust if needed
            serialOut[dataLen] = '\0';

            //freeParsedPackets(packets, packetCount);
            return;
        }
    }

    serialOut[0] = '\0';
    //freeParsedPackets(packets, packetCount);
}

/*void freeParsedPackets(ParsedPacket* packets, int count) {
    if (!packets) return;

    for (int i = 0; i < count; i++) {
        if (packets[i].raw != NULL) {
            //free(packets[i].raw);
            //packets[i].raw = NULL; // Avoid double free
        }
    }

    //free(packets); // Free the packet array itself
}*/

uint16_t ExtractEepromAddress(ParsedPacket *packets, int packet_count) {
	for (int i = 0; i < packet_count; i++) {
		ParsedPacket *p = &packets[i];
		if (!p->isAckNak && p->length > 4) {
			uint32_t address = (p->raw[3+1] << 8) | p->raw[3];
			return address - 10;
		}
	}
	return 0;
}
float ReadDfi(){
	float Dfi = 0.0;
	if(!KeepAlive()){
		KLINE_THROW_NONALIVE_EXCEPTION(0);
		return 0.0;
	}
	uint8_t request[] = { 0x18, 0x00, 0x03, 0xFF, 0xFF }; 	//TODO ERROR ENVIA AC 7E 00 20 00
	int packet_count = 0;
	//ParsedPacket* packets = SendCustom(request, 5, &packet_count, 1); //this changes with pump version but for now no exceptions
    ParsedPacket* packets = SendCustom((uint8_t[]){ 0x18, 0x00, 0x03, 0xFF, 0xFF }, 5, &packet_count);

	//Dfi = 1.0;
	if(!KeepAlive()){
		KLINE_THROW_NONALIVE_EXCEPTION(0);
		return 0.0;
	}

	uint8_t request2[] = { 0x19, 0x02, 0x00, 0x44};
	packets = SendCustom(request2, 4, &packet_count); //this changes with pump version but for now no exceptions


	// Filter to exclude Ack/Nak
	for (int i = 0; i < packet_count; i++) {
		if (!packets[i].isAckNak) {
			Dfi = ((int8_t)packets[i].raw[3] * 3.0) / 256.0;
		}
	}
	//free(packets);  // Free the original array pointer (but not the reused raw data)
	/*if(Dfi != 0.0){

	}*/
	return Dfi;
}

/**
 * Write DFI parameter (EEPROM 0x44) after password unlock.
 *
 * version mapping (as in your C#):
 *  default: {18 00 03 2F FF ...}
 *  version==1: {18 00 03 2F F2 ...}
 *  version==2 or 3: {18 00 03 FF F2 ...}
 *
 * Returns 1 on success, 0 on failure (non-alive).
 */
int WriteDfi(float dfi, int version)
{
    if (!KeepAlive()) {
        KLINE_THROW_NONALIVE_EXCEPTION(0);
        return 0;
    }

    // Password packets (14 bytes)
    static const uint8_t pass_default[14] = {
        0x18, 0x00, 0x03, 0x2F, 0xFF, 0x4B, 0x48, 0x54, 0x43, 0x41, 0x38, 0x47, 0x30, 0x45
    }; // V1  KHTCA8G0E (per your comment)
    static const uint8_t pass_v2[14] = {
        0x18, 0x00, 0x03, 0x2F, 0xF2, 0x4B, 0x48, 0x54, 0x43, 0x41, 0x38, 0x47, 0x30, 0x45
    }; // V2  KHTCA8G0E
    static const uint8_t pass_v3v4[14] = {
        0x18, 0x00, 0x03, 0xFF, 0xF2, 0x4B, 0x48, 0x54, 0x43, 0x41, 0x38, 0x47, 0x30, 0x45
    }; // V3, V4

    const uint8_t *password_packet = pass_default;
    if (version == 1) {
        password_packet = pass_v2;
    } else if (version == 2 || version == 3) {
        password_packet = pass_v3v4;
    }

    int packet_count = 0;
    (void)SendCustom(password_packet, 14, &packet_count); // throwOnNak=1 (strict)

    if (!KeepAlive()) {
        KLINE_THROW_NONALIVE_EXCEPTION(0);
        return 0;
    }

    // C#:
    // sbyte value = (sbyte)((dfi * 256.0) / 3);
    // if (value == 0) value = 1;
    //
    // NOTE: with dfi up to +1.5, math gives 128.0. That does not fit in int8_t.
    // We clamp to [-128..127] to avoid wrap to -128.
    int vi = (int)((dfi * 256.0f) / 3.0f);   // trunc toward 0 (matches typical C# unchecked cast behavior for in-range)
    vi = vi > 127 ? 127:vi;
    vi = vi < -128 ? -128:vi;
    //vi = clamp_int(vi, -128, 127);
    int8_t value = (int8_t)vi;
    if (value == 0) value = 1;

    // byte value_csum = (byte)(0 - (byte)value);
    uint8_t value_u    = (uint8_t)value;
    uint8_t value_csum = (uint8_t)(0u - value_u);

    // Write command
    uint8_t req[7] = { 0x1A, 0x02, 0x00, 0x44, value_u, value_csum, 0x03 };
    (void)SendCustom(req, 7, &packet_count); // throwOnNak=1 (strict)

    if (!KeepAlive()) {
        KLINE_THROW_NONALIVE_EXCEPTION(0);
        return 0;
    }

    return 1;
}
void ReadCustomerChangeIndex(){
	if(!KeepAlive()){
		KLINE_THROW_NONALIVE_EXCEPTION(0);
		return;
	}
    uint8_t request[] = { 0x18, 0x00, 0x00, 0x82, 0x33 };

    int packet_count = 0;
    ParsedPacket* packets = SendCustom(request, 5, &packet_count); //this changes with pump version but for now no exceptions

    char message[40];
    //sprintf(message, "Reading customer change index\r\n");
	//HAL_UART_Transmit(&huart2, (uint8_t*)message, strlen(message), 1000);

	if(!KeepAlive()){
		KLINE_THROW_NONALIVE_EXCEPTION(12);
		return;
	}

	uint16_t cust_change_address = 0;

    int data_length = 0;

    uint8_t* test_data = ReadRomEeprom(0x9FFE, 2, &data_length);
    if(data_length > 1){
    	cust_change_address = (uint16_t)((test_data[1] << 8) | test_data[0]);
    	cust_change_address += 3;
    }

	test_data = ReadRomEeprom(cust_change_address, 6, &data_length);

	char cust_change_index[12];

	if (test_data && data_length > 0) {
	    for (int i = 0; i < data_length; i++) {
	        cust_change_index[i] = (char)test_data[i];  // Convert byte to char
	    }
	}
    //sprintf(message, "Mod. Index : %s\r\n", cust_change_index);
    //CDC_Transmit_FS((uint8_t*)message, strlen(message));
    /*char cust_change_index[12];

    if (test_data && data_length > 0) {
        int copy_len = (data_length < (sizeof(cust_change_index) - 1))
                       ? data_length
                       : (sizeof(cust_change_index) - 1);

        for (int i = 0; i < copy_len; i++) {
            cust_change_index[i] = (char)test_data[i];
        }
        cust_change_index[copy_len] = '\0';  // Null-terminate
    }

    sprintf(message, "Mod. Index : %s\r\n", cust_change_index);
    CDC_Transmit_FS((uint8_t*)message, strlen(message));*/
    //HAL_UART_Transmit(&huart2, (uint8_t*)cust_change_index, strlen(cust_change_index), 1000);
}
char identStr[11]; //11+1

void IdentifyEcu() {
    char outputStr[35];
	//float dFi = ReadDfi();
    //sprintf(outputStr, "dFi: %d.%02d\t\t\r\n", (int)dFi, (int)((dFi - (int)dFi) * 100));

	//CDC_Transmit_FS((uint8_t*)outputStr, strlen(outputStr));

    ReadCustomerChangeIndex();//Necessary

    if(!KeepAlive()){
		KLINE_THROW_NONALIVE_EXCEPTION(0);
		return;
	}
    int packet_count = 0;
    ParsedPacket* packets = SendCustom((uint8_t[]){ 0x01, 0x02, 0x00, 0xC6 }, 4, &packet_count);

    //uint16_t address = ExtractEepromAddress(packets, count);

    uint16_t address = ExtractEepromAddress(packets, packet_count);

    if(!KeepAlive()){
		KLINE_THROW_NONALIVE_EXCEPTION(1);
		return;
    }

    if (address != 0) {
        int data_length = 0;
        uint8_t* test_data = ReadRomEeprom(address, 10, &data_length);

        for (int i = 0; i < data_length && i < sizeof(identStr) - 1; i++) {
            identStr[i] = (char)test_data[i];
        }        //identStr[10] = '\0';
        //HAL_UART_Transmit(&huart2, (uint8_t*)identStr, strlen(identStr), 1000);
        //sprintf(outputStr, "Ident. : %s\t\r\n", identStr);

        //CDC_Transmit_FS((uint8_t*)outputStr, strlen(outputStr));

    }

    if(!KeepAlive()){
    		KLINE_THROW_NONALIVE_EXCEPTION(2);
    		return;
        }

    char serial[20];
    ReadSerialNumber(serial, sizeof(serial));
    //sprintf(serial, "serial numb: %s\r\n", serial);
    //HAL_UART_Transmit(&huart2, (uint8_t*)serial, strlen(serial), 1000);
    /*sprintf(outputStr, "Serial N. : %s\t\r\n", serial);

    CDC_Transmit_FS((uint8_t*)outputStr, strlen(outputStr));*/

    //sprintf(serial, "serial numb: %s\r\n", serial);
    //HAL_UART_Transmit(&huart2, (uint8_t*)data, strlen(data), 1000);
}


/*  FAULT CODE READING  */


#define FAULTCODE_NONE_DTC   0x00

/* --- Tuning limits (adapt to your max) --- */
#define MAX_FAULTCODES        20
#define MAX_FC_AGGREGATE_LEN  512  // total concatenated body bytes

int ReadFaultCodes(FaultCode* outCodes, int maxCodes)
{
    char logmsg[64];

    if (!outCodes || maxCodes <= 0) return -1;

    // Keep the link alive as you do elsewhere TODO
    if (!KeepAlive()) {
        KLINE_THROW_NONALIVE_EXCEPTION(0);
        return -1;
    }

    // "Sending ReadFaultCodes packet"
    /*sprintf(logmsg, "Sending ReadFaultCodes packet\r\n");
    CDC_Transmit_FS((uint8_t*)logmsg, (uint16_t)strlen(logmsg));*/

    // Send single-byte payload with command 0x07
    uint8_t cmd = (uint8_t)PACKET_CMD_FaultCodesRead;  // already defined in your enum
    SendPacket(&cmd, 1);

    // Receive all response packets
    int packet_count = 0;
    ParsedPacket* packets = ReceivePackets(&packet_count);
    if (!packets || packet_count <= 0) {
        return 0; // no codes
    }

    // Concatenate all bodies from non-ACK/NAK packets
    // Body = raw[3 .. (3 + body_len - 1)], where body_len = (length - 3).
    // Full raw frame is: [len][counter][command][body...][end]
    // Your receiver code writes the end byte separately (0x03).
    // NOTE: guard against malformed lengths.
    uint8_t fc_buf[64];
    int fc_len = 0;

    for (int i = 0; i < packet_count; i++) {
        if (packets[i].isAckNak) continue;

        // If you classify types and want to be strict, enforce here:
         if (packets[i].title != PACKET_CMD_FaultCodesResponse) {
             /*sprintf(logmsg, "Expected FaultCodesPacket but got type=0x%02X\r\n", packets[i].type);
             CDC_Transmit_FS((uint8_t*)logmsg, (uint16_t)strlen(logmsg));*/
             //free(packets);
             return -1;
         }

        if (!packets[i].raw || packets[i].length < 3) continue;

        int body_len = (int)packets[i].length - 4;  // subtract [len, counter, command]
        if (body_len <= 0) continue;

        // Defensive: make sure the raw buffer actually has those bytes (+1 end is read separately)
        // We assume your parser filled .raw accordingly.

        if ((fc_len + body_len) > (int)sizeof(fc_buf)) {
            /*sprintf(logmsg, "Fault code buffer overflow\r\n");
            CDC_Transmit_FS((uint8_t*)logmsg, (uint16_t)strlen(logmsg));*/
            //free(packets);
            return -1;
        }

        memcpy(&fc_buf[fc_len], &packets[i].raw[3], (size_t)body_len);
        fc_len += body_len;
    }

    // Done with the packet list (your comment: free only the array, not reused raw)
    //free(packets);

    // Parse aggregated data:
    // C# does: take first 3 bytes (dtc,status,extra), then Skip(8) per record.
    // That means record stride = 8 bytes, but only first 3 matter here.
    // Stop when fewer than 3 bytes remain.
    int produced = 0;
    int off = 0;

    while (off + 3 <= fc_len) {
        uint8_t dtc    = fc_buf[off + 0];
        uint8_t status = fc_buf[off + 1];
        uint8_t extra  = fc_buf[off + 2];

        if (dtc != FAULTCODE_NONE_DTC) {
            if (produced < maxCodes) {
                outCodes[produced].dtc    = dtc;
                outCodes[produced].status = status;
                outCodes[produced].extra  = extra;
                produced++;
            } else {
                /*sprintf(logmsg, "Fault code output full (%d)\r\n", maxCodes);
                CDC_Transmit_FS((uint8_t*)logmsg, (uint16_t)strlen(logmsg));*/
                break;
            }
        }

        // Advance to next 8-byte record
        off += 8;
    }

    return produced;  // number of valid fault codes in outCodes[]
}
int ClearFaultCodes(void)
{
    int packet_count = 0;
    uint8_t req[1] = { PACKET_CMD_FaultCodesDelete };

    ParsedPacket *packets = SendCustom(req, 1, &packet_count);

    // C# expects exactly 1 packet
    if (packet_count != 1) {
        // in embedded: return error code (or assert/log)
        return 0;
    }

    ParsedPacket *p = &packets[0];

    // Must be ACK or NAK
    if (!p->isAckNak) {
        return 0;
    }

    if (p->type == PACKET_TYPE_NAK) return 0;
    if (p->type == PACKET_TYPE_ACK) return 1;

    return 0;
}

/*void PrintFaultCodes(const FaultCode* list, int n)
{
    if (!list || n <= 0) {
        const char* msg = "No fault codes\r\n";
        CDC_Transmit_FS((uint8_t*)msg, (uint16_t)strlen(msg));
        return;
    }
    for (int i = 0; i < n; i++) {
        PrintFaultCode(&list[i]);
        HAL_Delay(10);
    }
}*/

typedef struct { uint8_t code; const char* text; } DtcMapEntry;

static const DtcMapEntry DTC_MAP[] = {
    {0x50, "Fuel quantity solenoid valve Output stage error"},
    {0x51, "Fuel quantity solenoid valve."},
    {0x52, "Angle sensor/ IWZ system."},
    {0x53, "Angle sensor/ IWZ system"},
    {0x54, "Control unit temperature sensor, temperature too high"},
    {0x55, "Control unit temperature sensor"},
    {0x56, "Battery voltage out of range"},
    {0x57, "Timing device control. Permanent control deviation"},
    {0x58, "Fuel quantity / timing solenoid valve"},
    {0x59, "BIP Fault (Begin of Injection Point)"},
    {0x5A, "Engine speed signal"},
    {0x5B, "Engine speed signal"},
    {0x5C, "CAN-Bus (sporadic)"},
    {0x5D, "CAN bus error"},
    {0x5E, "Self-test error"},
};

static const char* GetDtcText(uint8_t code)
{
    for (unsigned i = 0; i < (sizeof(DTC_MAP)/sizeof(DTC_MAP[0])); i++) {
        if (DTC_MAP[i].code == code) return DTC_MAP[i].text;
    }
    return "Unknown Error Code";
}
/*int FaultCode_ToString(const FaultCode* fc, char* dst, int dst_len)
{
    if (!fc || !dst || dst_len <= 0) return 0;

    uint8_t status1 = (uint8_t)(fc->status & 0x7F);
    const char* txt = GetDtcText(fc->dtc);

    // Format like: "5A Engine speed signal (03)\r\n"
    // (use %02u to mimic ":d2" from your C# example)
    int n = snprintf(dst, (size_t)dst_len, "%02X %s (%02u)\r\n",
                     fc->dtc, txt, status1);
    if (n < 0) n = 0;
    if (n >= dst_len) n = dst_len - 1;
    return n;
}*/
/*void PrintFaultCode(const FaultCode* fc)
{
    char line[128];
    int n = FaultCode_ToString(fc, line, sizeof(line));
    if (n > 0) {
        CDC_Transmit_FS((uint8_t*)line, (uint16_t)n);
    }
}*/

void KLINE_THROW_NONALIVE_EXCEPTION(uint8_t id){
	char erroralive[20];
	//sprintf(erroralive, "isnt alive %u\r\r\n", id);
	BT_KLINE_ERROR();
	//HAL_UART_Transmit(&huart2, (uint8_t*)erroralive, strlen(erroralive), 1000);
    //CDC_Transmit_FS((uint8_t*)erroralive, strlen(erroralive));

}

uint8_t ReadAudiPin(uint16_t* pin) {
    int data_length = 0;
    uint8_t* data = ReadRomEeprom(0x04FA, 2, &data_length);
    if(data_length < 1){return 0;}
    *pin = (uint16_t)((data[1] << 8) | data[0]);
    return 1;
}
uint8_t WriteAudiPin(uint16_t pin) {
    /*uint8_t* data = ReadRomEeprom(0x04FA, 2, &data_length);
    if(data_length < 1){return 0;}
    *pin = (uint16_t)((data[1] << 8) | data[0]);*/
    return 1;
}

uint8_t ReadVoltage(uint16_t* volt) {
    int data_length = 0;
    uint8_t* data = ReadRomEeprom(0x0142, 2, &data_length);
    if(data_length < 1){return 0;}
    *volt = (uint16_t)((data[1] << 8) | data[0]);
    return 1;
}
