/*
 * remoteDisplay.c - TCP 服务器线程实现
 * 实现 RemoDispBus 协议：解析 remo_disp_server.py 的请求，返回 lcd 显存数据等。
 * 帧格式： [TAG][cmd][len_hi][len_lo][data][crc]，CRC = data 区逐字节异或低 8 位。
 * 客户端 TAG=0xAA，设备回复 TAG=0xBB。
 */

#include <stdio.h>
#include <string.h>

#include "Drv/lcd/lcd.h"
#include "TCP/tcp.h"
#include "Drv/key.h"
#include "remoteDisplay.h"

/* RemoDispBus 协议常量（与 remo_disp_server.py 一致） */
#define TAG_CLIENT  0xAAu
#define TAG_DEVICE  0xBBu
#define CMD_KEEPLIVE  0
#define CMD_INIT      1
#define CMD_KEY       2
#define CMD_LCDMEM    3

/* 计算 data 区 CRC：逐字节异或取低 8 位 */
static uint8_t calc_crc(const uint8_t* data, unsigned int len)
{
    uint8_t crc = 0;
    while (len--)
        crc ^= *data++;
    return crc;
}

/* 从 recv 缓冲区解析一帧： [TAG][cmd][len_hi][len_lo][data...][crc]
 * 成功返回 1 并设置 *p_cmd, *p_data, *p_data_len；失败返回 0。
 * *p_consume 返回本帧总字节数（含头尾），调用方从缓冲区移除。 */
static int parse_frame(const uint8_t* buf, unsigned int buf_len,
    uint8_t* p_cmd, const uint8_t** p_data, unsigned int* p_data_len,
    unsigned int* p_consume)
{
    if (buf_len < 5 || buf[0] != TAG_CLIENT)
        return 0;
    unsigned int data_len = ((unsigned int)buf[2] << 8) | buf[3];
    unsigned int frame_len = 4 + data_len + 1;  /* 头4 + data + crc */
    if (buf_len < frame_len)
        return 0;
    uint8_t crc = calc_crc(buf + 4, data_len);
    if (crc != buf[4 + data_len])
        return 0;
    *p_cmd = buf[1];
    *p_data = buf + 4;
    *p_data_len = data_len;
    *p_consume = frame_len;
    return 1;
}

/* 回复帧最大长度：头4 + 最大 payload(4+LCD_DISPLAYMEMORYSIZE) + crc1 */
#define REMO_REPLY_BUF_SIZE  (4 + 4 + LCD_DISPLAYMEMORYSIZE + 1)

/* 构造设备回复帧并发送： [TAG_DEVICE][cmd][len_hi][len_lo][data][crc]，一次性发送 */
static int send_reply(int client, uint8_t cmd, const uint8_t* data, unsigned int data_len)
{
    uint8_t buf[REMO_REPLY_BUF_SIZE];
    unsigned int total = 4 + data_len + 1;

    if (total > REMO_REPLY_BUF_SIZE)
        return -1;
    buf[0] = TAG_DEVICE;
    buf[1] = cmd & 0xFF;
    buf[2] = (data_len >> 8) & 0xFF;
    buf[3] = data_len & 0xFF;
    if (data_len)
        memcpy(buf + 4, data, data_len);
    buf[4 + data_len] = calc_crc(data, data_len);

    return TcpClient_Send(client, (const char*)buf, total) == (int)total ? 0 : -1;
}

/* 处理 CMD_LCDMEM：请求 data 为 4 字节大端起始地址；回复 [4B 地址][显存数据] */
static void handle_cmd_lcdmem(int client, const uint8_t* req_data, unsigned int req_len)
{
    const uint8_t *framebuffer = Lcd_GetFrameBuffer();
    unsigned int start_addr = 0;

    if (req_len >= 4)
        start_addr = ((unsigned int)req_data[0] << 24) | ((unsigned int)req_data[1] << 16)
                   | ((unsigned int)req_data[2] << 8) | req_data[3];

    /* 回复 payload = [4B 地址大端][显存]，Python 取 payload[4:] 为位图 */
    uint8_t payload[4 + LCD_DISPLAYMEMORYSIZE];
    payload[0] = (start_addr >> 24) & 0xFF;
    payload[1] = (start_addr >> 16) & 0xFF;
    payload[2] = (start_addr >> 8) & 0xFF;
    payload[3] = start_addr & 0xFF;

    unsigned int copy_len = LCD_DISPLAYMEMORYSIZE;
    if (start_addr < LCD_DISPLAYMEMORYSIZE) 
    {
        unsigned int offset = start_addr;
        copy_len = LCD_DISPLAYMEMORYSIZE - offset;
        memcpy(payload + 4, framebuffer + offset, copy_len);
    } 
    else 
    {
        copy_len = 0;
    }
    send_reply(client, CMD_LCDMEM, payload, 4 + copy_len);
}

/* 处理 CMD_INIT：回复宽、高、显存大小（与 Python 端约定一致时可扩展） */
static void handle_cmd_init(int client)
{
    uint8_t data[8];
    data[0] = (LCD_SIZE_X >> 8) & 0xFF;
    data[1] = LCD_SIZE_X & 0xFF;
    data[2] = (LCD_SIZE_Y >> 8) & 0xFF;
    data[3] = LCD_SIZE_Y & 0xFF;
    data[4] = (LCD_DISPLAYMEMORYSIZE >> 24) & 0xFF;
    data[5] = (LCD_DISPLAYMEMORYSIZE >> 16) & 0xFF;
    data[6] = (LCD_DISPLAYMEMORYSIZE >> 8) & 0xFF;
    data[7] = LCD_DISPLAYMEMORYSIZE & 0xFF;
    send_reply(client, CMD_INIT, data, 8);
}

/* 处理 CMD_KEY：可选转交菜单，此处仅回 ACK */
static void handle_cmd_key(int client, const uint8_t* data, unsigned int len)
{
#ifdef DEBUG
    printf("handle_cmd_key: 0x%02X\n", data[0]);
#endif
    g_tRemoteKey.byKeyValid = EN_KEY_FLAG_NEW;
    g_tRemoteKey.byKeyValue = data[0];
}

/* 处理 CMD_KEEPLIVE：保活，回空 */
static void handle_cmd_keeplive(int client)
{
    send_reply(client, CMD_KEEPLIVE, (const uint8_t*)NULL, 0);
}

void tcp_server_thread_fn(void* arg)
{
    server_thread_arg_t* ctx = (server_thread_arg_t*)arg;
    int server_sock = TcpServer_Listen((uint16_t)ctx->port);
    if (server_sock == TCP_INVALID_SOCKET) {
        fprintf(stderr, "[TCP Server] TcpServer_Listen(%d) failed\n", ctx->port);
        return;
    }
    *ctx->p_server_sock = server_sock;
    printf("[TCP Server] RemoDispBus 监听端口 %d，等待客户端连接...\n", ctx->port);

#define REMO_BUF_SIZE 4096
    uint8_t recv_buf[4096];
    unsigned int recv_len = 0;

    /* ========== 外层循环：主线程未请求退出时，持续等待并接受新客户端 ========== */
    while (!*ctx->p_quit) {
        /* 阻塞等待一个客户端连接；主线程关闭 server_sock 时 Accept 会失败并返回 INVALID */
        int client = TcpServer_Accept(server_sock);
        if (client == TCP_INVALID_SOCKET) 
        {
            if (*ctx->p_quit)
                break;
            continue;
        }
        printf("[TCP Server] 客户端连接成功\n");
        recv_len = 0;   /* 新连接对应新的接收缓冲区，避免混用上一连接的残留数据 */

        /* ========== 内层循环：处理当前连接上的 RemoDispBus 请求，直到断开或退出 ========== */
        while (!*ctx->p_quit) 
        {
            /* ----- 1. 若缓冲区中已有一条完整且校验通过的帧，则解析并分发处理 ----- */
            uint8_t cmd;
            const uint8_t* pdata;
            unsigned int data_len, consume;
            if (parse_frame(recv_buf, recv_len, &cmd, &pdata, &data_len, &consume)) 
            {
                switch (cmd) 
                {
                    case CMD_KEEPLIVE:
                        handle_cmd_keeplive(client);
                        break;
                    case CMD_INIT:
                        handle_cmd_init(client);
                        break;
                    case CMD_KEY:
                        handle_cmd_key(client, pdata, data_len);
                        break;
                    case CMD_LCDMEM:
                        handle_cmd_lcdmem(client, pdata, data_len);
                        break;
                    default:
                        send_reply(client, cmd, (const uint8_t*)NULL, 0);
                        break;
                }
                /* 从缓冲区头部移除已消费的 consume 字节，避免重复解析同一帧 */
                memmove(recv_buf, recv_buf + consume, recv_len - consume);
                recv_len -= consume;
                continue;   /* 继续检查是否还有完整帧，避免不必要的 recv */
            }

            /* ----- 2. 缓冲区中尚无完整帧：先防止溢出，再收一批数据 ----- */
            if (recv_len >= REMO_BUF_SIZE - 256) 
            {
                recv_len = 0;   /* 异常情况：数据过多且始终不成帧，清空缓冲区防止越界 */
            }
            int n = TcpClient_Recv(client, (char*)(recv_buf + recv_len), REMO_BUF_SIZE - recv_len - 1);
            if (n > 0) 
            {
                recv_len += (unsigned int)n;
            } 
            else 
            {
                /* n==0 表示对方正常关闭连接，n<0 表示读取出错；均退出本连接循环 */
                printf("[TCP Server] 客户端关闭连接\n");
                break;
            }
        }
        /* 当前客户端处理结束，关闭该连接；外层循环继续 Accept 下一个客户端 */
        TcpClient_Close(client);
    }

    TcpServer_Close(server_sock);
    *ctx->p_server_sock = TCP_INVALID_SOCKET;
    printf("[TCP Server] 已退出\n");
}
/* ----------------------------------------------------------------------------
 * 启动 TCP 服务与服务器线程（Tcp_Init + 创建线程 + 短暂等待就绪）
 * port: 监听端口（如 7070）
 * out_server_sock: 输出监听 socket，供主线程退出时 TcpServer_Close
 * out_server_quit: 输出退出标志，主线程置 1 通知服务器线程退出
 * out_server_th:   输出线程句柄，供主线程 Thread_Join
 * 返回：0 成功，1 失败（已调用 Tcp_Cleanup）
 * ---------------------------------------------------------------------------- */
int StartTcpServerThread(thread_handle_t *out_server_th, server_thread_arg_t *io_server_arg)
 {
     if (Tcp_Init() != 0) {
         fprintf(stderr, "Tcp_Init failed\n");
         return 1;
     }
 
     if (Thread_Create(tcp_server_thread_fn, io_server_arg, out_server_th) != 0) {
         fprintf(stderr, "Thread_Create(server) failed\n");
         Tcp_Cleanup();
         return 1;
     }
     Sleep(200);
 
     return 0;
 }
