//***********************************************************************************************************
//! @file        lta_protocol.c
//! @brief       Реализация протокола LTA (CLI Protocol 1.1)
//! @version     v1.1.0
//! @date        31.03.2026
//***********************************************************************************************************

#include "lta_protocol.h"
#include "ring_buffer.h"
#include "lmcal.h"
#include "tcp.h"
#include "fpga_cmn.h"
#include "flash_mem.h"
#include "submodule.h"
#include <string.h>
#include <stdio.h>

#define LTA_CMD_PREFIX          'T'
#define LTA_RESPONSE_PREFIX     't'
#define LTA_ERROR_PREFIX        'e'
#define LTA_MSG_TERMINATOR      '\r'

#define HEX_CHAR_TO_NIBBLE(c)  (((c) >= '0' && (c) <= '9') ? ((c) - '0') :      \
                                 ((c) >= 'A' && (c) <= 'F') ? ((c) - 'A' + 10) : \
                                 ((c) >= 'a' && (c) <= 'f') ? ((c) - 'a' + 10) : 0xFF)

static ringbuf_t fifo_rx;
static u8 fifo_rx_buf[LTA_CMD_BUFFER_SIZE];

static char lta_cmd_buffer[LTA_CMD_BUFFER_SIZE];
static u16 lta_cmd_index = 0;

static bool lta_connected = false;
static u32 lta_last_activity = 0;
static u32 (*lta_get_tick)(void) = NULL;

static lta_device_info_t lta_device_info;
static char lta_password[16] = "1357";

//-----------------------------------------------------------------------------------------------------------
static inline void byte_to_hex(u8 byte, char* hex)
{
    const char* d = "0123456789ABCDEF";
    hex[0] = d[(byte >> 4) & 0x0F];
    hex[1] = d[byte & 0x0F];
}

//-----------------------------------------------------------------------------------------------------------
static u8 hex_to_byte(const char* hex)
{
    u8 h = HEX_CHAR_TO_NIBBLE(hex[0]);
    u8 l = HEX_CHAR_TO_NIBBLE(hex[1]);
    return (h == 0xFF || l == 0xFF) ? 0xFF : (h << 4) | l;
}

//-----------------------------------------------------------------------------------------------------------
void lta_send_string(const char* str)
{
    if (!str) return;
    while (*str) {
        lmcal_uart_write(LTA_UART_CHANNEL, (u16)*str);
        str++;
    }
}

//-----------------------------------------------------------------------------------------------------------
static void send_response(u8 cmd, const u8* data, u8 len)
{
    u8 buf[LTA_CMD_BUFFER_SIZE];
    u16 idx = 0;
    
    buf[idx++] = LTA_RESPONSE_PREFIX;
    byte_to_hex(cmd, (char*)&buf[idx]);
    idx += 2;
    
    for (u8 i = 0; i < len; i++) {
        byte_to_hex(data[i], (char*)&buf[idx]);
        idx += 2;
    }
    
    buf[idx++] = LTA_MSG_TERMINATOR;
    
    for (u16 i = 0; i < idx; i++) {
        lmcal_uart_write(LTA_UART_CHANNEL, buf[i]);
    }
}

//-----------------------------------------------------------------------------------------------------------
static void send_error(u8 cmd, u8 err)
{
    u8 buf[6];
    buf[0] = LTA_ERROR_PREFIX;
    byte_to_hex(cmd, (char*)&buf[1]);
    byte_to_hex(err, (char*)&buf[3]);
    buf[5] = LTA_MSG_TERMINATOR;
    
    for (u16 i = 0; i < 6; i++) {
        lmcal_uart_write(LTA_UART_CHANNEL, buf[i]);
    }
}

//-----------------------------------------------------------------------------------------------------------
// T00 - открыть соединение (пароль "1357")
//-----------------------------------------------------------------------------------------------------------
static void handle_cmd_open(void)
{
    if (lta_connected) {
        send_error(LTA_CMD_OPEN, LTA_ERR_INTERNAL);
        return;
    }
    
    lta_connected = true;
    if (lta_get_tick) lta_last_activity = lta_get_tick();
    send_response(LTA_CMD_OPEN, NULL, 0);
}

//-----------------------------------------------------------------------------------------------------------
// T01 - закрыть соединение
//-----------------------------------------------------------------------------------------------------------
static void handle_cmd_close(void)
{
    lta_connected = false;
    send_response(LTA_CMD_CLOSE, NULL, 0);

    lta_cmd_index = 0;
    memset(lta_cmd_buffer, 0, sizeof(lta_cmd_buffer));
}

//-----------------------------------------------------------------------------------------------------------
// T02 - проверка соединения (ping)
//-----------------------------------------------------------------------------------------------------------
static void handle_cmd_ping(const u8* data, u8 len)
{
    if (!lta_connected) {
        send_error(LTA_CMD_PING, LTA_ERR_NOT_CONNECTED);
        return;
    }
    
    if (len > 0) {
        send_response(LTA_CMD_PING, data, len);
    } else {
        send_response(LTA_CMD_PING, NULL, 0);
    }
}

//-----------------------------------------------------------------------------------------------------------
// T0A - информация об устройстве
//-----------------------------------------------------------------------------------------------------------
static void handle_cmd_get_info(void)
{
    if (!lta_connected) {
        send_error(LTA_CMD_GET_INFO, LTA_ERR_NOT_CONNECTED);
        return;
    }

    u8 buf[LTA_DATA_MAX_SIZE];
    u16 idx = 0;
    u8 len;
    
    // Board ID
    buf[idx++] = lta_device_info.board_id;
    
    // Имя
    len = strlen(lta_device_info.name);
    if (len > 31) len = 31;
    buf[idx++] = len;
    memcpy(&buf[idx], lta_device_info.name, len);
    idx += len;
    
    // Серийный номер
    len = strlen(lta_device_info.serial);
    if (len > 15) len = 15;
    buf[idx++] = len;
    memcpy(&buf[idx], lta_device_info.serial, len);
    idx += len;
    
    // Версия ПО
    char ver[16];
    snprintf(ver, sizeof(ver), "%lu", tsp_dev_info.mcu_ver);
    len = strlen(ver);
    if (len > 15) len = 15;
    buf[idx++] = len;
    memcpy(&buf[idx], ver, len);
    idx += len;
    
    send_response(LTA_CMD_GET_INFO, buf, idx);
}

//-----------------------------------------------------------------------------------------------------------
// T14 - чтение памяти
//-----------------------------------------------------------------------------------------------------------
static void handle_cmd_read_mem(const u8* data, u8 len)
{
    if (!lta_connected) {
        send_error(LTA_CMD_READ_MEM, LTA_ERR_NOT_CONNECTED);
        return;
    }
    
    if (len != 5) {
        send_error(LTA_CMD_READ_MEM, LTA_ERR_INVALID_ARG);
        return;
    }
    
    u32 addr = 0;
    for (u8 i = 0; i < 4; i++) {
        addr = (addr << 8) | data[i];
    }
    u8 size = data[4];
    
    if (size > LTA_DATA_MAX_SIZE) {
        send_error(LTA_CMD_READ_MEM, LTA_ERR_INVALID_ARG);
        return;
    }
    
    if ((addr >= 0x08000000 && addr < 0x08200000) ||
        (addr >= 0x20000000 && addr < 0x20020000)) {
        u8 buf[LTA_DATA_MAX_SIZE];
        for (u8 i = 0; i < size; i++) {
            buf[i] = *(volatile u8*)(addr + i);
        }
        send_response(LTA_CMD_READ_MEM, buf, size);
    } else {
        send_error(LTA_CMD_READ_MEM, LTA_ERR_INVALID_ADDR);
    }
}

//-----------------------------------------------------------------------------------------------------------
// T15 - запись памяти
//-----------------------------------------------------------------------------------------------------------
static void handle_cmd_write_mem(const u8* data, u8 len)
{
    if (!lta_connected) {
        send_error(LTA_CMD_WRITE_MEM, LTA_ERR_NOT_CONNECTED);
        return;
    }
    
    if (len < 5) {
        send_error(LTA_CMD_WRITE_MEM, LTA_ERR_INVALID_ARG);
        return;
    }
    
    u32 addr = 0;
    for (u8 i = 0; i < 4; i++) {
        addr = (addr << 8) | data[i];
    }
    u8 size = len - 4;
    
    if ((addr >= 0x20000000 && addr < 0x20020000)) {
        for (u8 i = 0; i < size; i++) {
            *(volatile u8*)(addr + i) = data[4 + i];
        }
        send_response(LTA_CMD_WRITE_MEM, NULL, 0);
    } else {
        send_error(LTA_CMD_WRITE_MEM, LTA_ERR_INVALID_ADDR);
    }
}

//-----------------------------------------------------------------------------------------------------------
static void parse_and_execute(void)
{
    char* buf = lta_cmd_buffer;
    u16 len = lta_cmd_index;
    
    if (len < 3) {
        lta_cmd_index = 0;
        return;
    }
    
    if (buf[0] != LTA_CMD_PREFIX) {
        lta_cmd_index = 0;
        return;
    }
    
    if (buf[len - 1] != LTA_MSG_TERMINATOR) {
        return;
    }
    
    // Парсим код команды
    u8 cmd = hex_to_byte(&buf[1]);
    if (cmd == 0xFF) {
        lta_cmd_index = 0;
        return;
    }
    
    // Парсим данные (HEX)
    u8 data[LTA_DATA_MAX_SIZE + 4];
    u8 data_len = 0;
    
    if (len > 4) {
        u16 chars = len - 4;
        if ((chars % 2) != 0) {
            send_error(cmd, LTA_ERR_INVALID_ARG);
            lta_cmd_index = 0;
            return;
        }
        
        data_len = chars / 2;
        if (data_len > (LTA_DATA_MAX_SIZE + 4)) {
            send_error(cmd, LTA_ERR_INVALID_ARG);
            lta_cmd_index = 0;
            return;
        }
        
        for (u8 i = 0; i < data_len; i++) {
            data[i] = hex_to_byte(&buf[3 + i * 2]);
            if (data[i] == 0xFF) {
                send_error(cmd, LTA_ERR_INVALID_ARG);
                lta_cmd_index = 0;
                return;
            }
        }
    }
    
    if (lta_get_tick) lta_last_activity = lta_get_tick();
    
    // Обработка команд
    switch (cmd) {
        case LTA_CMD_OPEN:
            handle_cmd_open();
            break;
            
        case LTA_CMD_CLOSE:
            handle_cmd_close();
            break;
            
        case LTA_CMD_PING:
            handle_cmd_ping(data, data_len);
            break;
            
        case LTA_CMD_GET_INFO:
            handle_cmd_get_info();
            break;
            
        case LTA_CMD_READ_MEM:
            handle_cmd_read_mem(data, data_len);
            break;
            
        case LTA_CMD_WRITE_MEM:
            handle_cmd_write_mem(data, data_len);
            break;
            
        default:
            send_error(cmd, LTA_ERR_UNKNOWN_CMD);
            break;
    }
    
    lta_cmd_index = 0;
}

//-----------------------------------------------------------------------------------------------------------
static void uart_rx_cb(void)
{
    u16 rx = lmcal_uart_read(LTA_UART_CHANNEL);
    u8 ch = (u8)(rx & 0xFF);
    rb_add_item(&fifo_rx, &ch);
}

//************************************ Публичные функции ****************************************************

fun_res_t lta_protocol_init(void)
{
    fifo_rx.item_size = sizeof(u8);
    rb_init(&fifo_rx, fifo_rx_buf, sizeof(fifo_rx_buf));
    rb_clear(&fifo_rx);
    
    lmcal_uart_set_callback(LTA_UART_CHANNEL, CB_INT_RX_CALLBACK, uart_rx_cb);
    
    lta_cmd_index = 0;
    lta_connected = false;
    lta_last_activity = 0;
    lta_get_tick = NULL;
    memset(lta_cmd_buffer, 0, sizeof(lta_cmd_buffer));
    
    strcpy(lta_device_info.name, "LTA Device");
    strcpy(lta_device_info.serial, "00000000");
    strcpy(lta_device_info.version, "1.0.0");
    lta_device_info.board_id = 0;
    
    lta_send_string("\r\n========================================\r\n");
    lta_send_string("LTA Protocol v1.1 Ready\r\n");
    lta_send_string("========================================\r\n");
    lta_send_string("Commands:\r\n");
    lta_send_string("  T00 - Open connection (password: 1357)\r\n");
    lta_send_string("  T01 - Close connection\r\n");
    lta_send_string("  T02 - Ping\r\n");
    lta_send_string("  T0A - Get device info\r\n");
    lta_send_string("  T14[addr][size] - Read memory\r\n");
    lta_send_string("  T15[addr][data] - Write memory\r\n");
    lta_send_string("========================================\r\n");
    lta_send_string("> ");
    
    return ERR_OK;
}

fun_res_t lta_protocol_deinit(void)
{
    lmcal_uart_set_callback(LTA_UART_CHANNEL, CB_INT_RX_CALLBACK, NULL);
    lta_connected = false;
    return ERR_OK;
}

void lta_protocol_process(void)
{
    while (rb_get_items_qty(&fifo_rx) > 0) {
        u8 ch;
        if (rb_get_item(&fifo_rx, &ch) != RB_RES_OK) continue;
        
        // Игнорируем пустые символы в начале
        if (lta_cmd_index == 0 && (ch == '\r' || ch == '\n')) {
            continue;
        }
        
        if (ch == '\r' || ch == '\n') {
            if (lta_cmd_index > 0) {
                lta_cmd_buffer[lta_cmd_index] = LTA_MSG_TERMINATOR;
                lta_cmd_index++;
                parse_and_execute();
                lta_send_string("> ");
            }
            // Сбрасываем индекс после Enter
            lta_cmd_index = 0;
        } else if (lta_cmd_index < LTA_CMD_BUFFER_SIZE - 1) {
            lta_cmd_buffer[lta_cmd_index++] = ch;
        } else {
            lta_cmd_index = 0;
            memset(lta_cmd_buffer, 0, sizeof(lta_cmd_buffer));
            lta_send_string("\r\nERROR: Buffer overflow\r\n> ");
        }
    }
    
    if (lta_connected && lta_get_tick) {
        if ((lta_get_tick() - lta_last_activity) > LTA_CONNECTION_TIMEOUT_MS) {
            lta_connected = false;
        }
    }
}

fun_res_t lta_protocol_set_device_info(const char* name, const char* serial, const char* ver, u8 board_id)
{
    if (name) strncpy(lta_device_info.name, name, sizeof(lta_device_info.name) - 1);
    if (serial) strncpy(lta_device_info.serial, serial, sizeof(lta_device_info.serial) - 1);
    if (ver) strncpy(lta_device_info.version, ver, sizeof(lta_device_info.version) - 1);
    lta_device_info.board_id = board_id;
    return ERR_OK;
}




