/*
 * 模块名称：Time Sync — PC 本地时间推送接收（TCP）
 * 模块功能：在独立任务 timeSyncTask 中以 TCP Server 方式监听端口，接收 PC 推送的本地时间包，
 *          调用 sys_clock_set_unix() 更新系统墙钟并写回 SD2506 RTC
 * 适用平台：STM32F407ZGTx + CH395F
 * 作者：王建锋
 * 创建日期：2026-08-27
 * 修改记录：
 *   v1.0  2026-08-27  王建锋  创建初始版本（UDP 推送，netTask 内轮询）
 *   v1.1  2026-08-28  王建锋  改为 TCP Server，迁入独立任务 timeSyncTask，
 *                           所有 socket 操作经 net_socket 线程安全 API 走消息队列，
 *                           netTask 仅负责 net_poll/net_process_messages，多任务并发安全
 */

#include <string.h>
#include "time_sync.h"
#include "net_socket.h"
#include "sys_clock.h"

#define DBG_TAG "[TIME_SYNC]"
#include "dbg_log.h"

/* 监听端口：PC 推送端（TCP 客户端）需连到本端口 */
#define TIME_SYNC_PORT   8888

/*
 * 函数功能：创建并监听 TCP 时间同步服务端口
 * 入口参数：无
 * 返回值：监听 Socket 描述符（>=0），失败返回 -1
 * 限定条件：net_init() 已成功；可在非 netTask 上下文调用
 * 函数说明：net_socket/net_bind/net_listen 均为线程安全 API，内部经消息队列由 netTask 串行处理。
 *          单连接模式下监听 Socket 在收到连接后会转为数据通道，连接关闭后即被释放，
 *          因此每次调用都会新建监听 Socket（与 lftpd 同模式），失败自动重试。
 */
static int time_sync_open_listener(void)
{
    int retry;
    int s = -1;

    for (retry = 0; retry < 5; retry++) {
        s = net_socket(NET_AF_INET, NET_SOCK_STREAM);
        if (s < 0) {
            DBG_ERROR("time_sync: net_socket FAIL");
            vTaskDelay(pdMS_TO_TICKS(500));
            continue;
        }

        struct net_sockaddr_in addr;
        memset(&addr, 0, sizeof(addr));
        addr.sin_family      = NET_AF_INET;
        addr.sin_port        = net_htons(TIME_SYNC_PORT);
        addr.sin_addr.s_addr = NET_INADDR_ANY;

        if (net_bind(s, (struct net_sockaddr *)&addr, sizeof(addr)) != 0) {
            DBG_ERROR("time_sync: bind port %d FAIL", TIME_SYNC_PORT);
            net_close(s);
            vTaskDelay(pdMS_TO_TICKS(2000));
            continue;
        }

        if (net_listen(s) != 0) {
            DBG_ERROR("time_sync: listen FAIL");
            net_close(s);
            vTaskDelay(pdMS_TO_TICKS(2000));
            continue;
        }

        DBG_INFO("time_sync: TCP listen on :%d (sock=%d)", TIME_SYNC_PORT, s);
        return s;
    }

    return -1;
}

/*
 * 函数功能：处理一个已建立的时间同步连接
 * 入口参数：conn_sock - 已建立的连接 Socket 描述符（单连接模式下即监听 Socket 自身）
 * 返回值：无
 * 函数说明：阻塞读取 PC 发来的时间包（"TIME"+uint32 大端 本地时间），解析后写回 RTC，然后关闭连接
 */
static void time_sync_handle_conn(int conn_sock)
{
    uint8_t buf[16];
    int len;

    len = net_recv(conn_sock, buf, sizeof(buf), 0);
    if (len >= 8) {
        if (buf[0] == 'T' && buf[1] == 'I' && buf[2] == 'M' && buf[3] == 'E') {
            uint32_t epoch = ((uint32_t)buf[4] << 24) |
                             ((uint32_t)buf[5] << 16) |
                             ((uint32_t)buf[6] << 8)  |
                             ((uint32_t)buf[7]);
            sys_clock_set_unix(epoch);
            DBG_INFO("time_sync: wall clock updated (epoch=%lu)", (unsigned long)epoch);
        }
    }

    net_close(conn_sock);
}

/*
 * 函数功能：时间同步任务主体（独立 FreeRTOS 任务）
 * 入口参数：argument - 未使用
 * 返回值：无
 * 限定条件：net_init() 已由 netTask 完成；netTask 正在处理消息队列
 * 函数说明：等待网络栈就绪后，循环：建监听 -> 等连接 -> 处理 -> 关闭 -> 重建监听。
 *           全部 socket 操作经消息队列由 netTask 串行执行，与本任务/其它网络任务并发安全。
 */
void timeSyncTask(void *argument)
{
    (void)argument;

    /* 等网络栈起来（与 StartFtpTask 的 7s 延迟同风格，确保 netTask 已开始处理消息） */
    vTaskDelay(pdMS_TO_TICKS(8000));

    for (;;) {
        int ls = time_sync_open_listener();
        if (ls < 0) {
            DBG_ERROR("time_sync: open listener FAIL, retry later");
            vTaskDelay(pdMS_TO_TICKS(2000));
            continue;
        }

        /* 等待 PC 连接：net_accept 在无连接时立即返回 -1，循环重试同一监听 Socket */
        int conn = -1;
        do {
            conn = net_accept(ls, NULL, NULL);
            if (conn < 0) {
                vTaskDelay(pdMS_TO_TICKS(200));
            }
        } while (conn < 0);

        time_sync_handle_conn(conn);
        /* conn（单连接模式下即 ls）已被 net_close 释放，下次循环重建监听 */

        vTaskDelay(pdMS_TO_TICKS(50));
    }
}
