/**
 *!
 * \file        b_srv_tcpip.c
 * \version     v0.0.1
 * \date        2023/08/27
 * \author      Bean(notrynohigh@outlook.com)
 *******************************************************************************
 * @attention
 *
 * Copyright (c) 2023 Bean
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *******************************************************************************
 */

/*Includes ----------------------------------------------*/
#include "services/inc/b_srv_tcpip.h"

#if (defined(_TCPIP_SERVICE_ENABLE) && (_TCPIP_SERVICE_ENABLE == 1))

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

#include "core/inc/b_sem.h"
#include "core/inc/b_task.h"
#include "modules/inc/b_mod_ssl.h"
#include "thirdparty/http-parser/http_parser.h"
#include "utils/inc/b_util_log.h"
#include "utils/inc/b_util_memp.h"
#include "utils/inc/b_util_utc.h"

/**
 * \addtogroup BABYOS
 * \{
 */

/**
 * \addtogroup SERVICES
 * \{
 */

/**
 * \addtogroup TCPIP
 * \{
 */

/**
 * \defgroup TCPIP_Private_TypesDefinitions
 * \{
 */

typedef struct
{
    uint32_t seconds;
    uint32_t fraction;
} bNtpTimestamp_t;

typedef struct
{
    uint8_t         li_vn_mode;       // Leap indicator, version number, and mode
    uint8_t         stratum;          // Stratum level of the local clock
    uint8_t         poll;             // Maximum interval between successive messages
    uint8_t         precision;        // Precision of the local clock
    uint32_t        root_delay;       // Total round trip delay time
    uint32_t        root_dispersion;  // Max error allowed from primary clock source
    uint32_t        ref_id;           // Reference clock identifier
    bNtpTimestamp_t ref_time;         // Reference timestamp
    bNtpTimestamp_t orig_time;        // Originate timestamp
    bNtpTimestamp_t recv_time;        // Receive timestamp
    bNtpTimestamp_t trans_time;       // Transmit timestamp
} bNtpPacket_t;

typedef struct
{
    bTaskId_t task_id;
    int       sockfd;
    uint32_t  interval_s;
} bNtpPcb_t;

//----------------------------------------------------------------------------
typedef enum
{
    B_HTTP_STA_INIT,
    B_HTTP_STA_CONNECTING,
    B_HTTP_STA_CONNECTED,
    B_HTTP_STA_RECV_DATA,
    B_HTTP_STA_DISCONNECT,
    B_HTTP_STA_DEINIT,
    B_HTTP_STA_DESTROY,
} bHttpState_t;
typedef struct
{
    uint8_t              is_https;
    bHttpState_t         state;
    char                 host[_HTTP_HOST_LEN_MAX + 1];
    char                 path[_HTTP_PATH_LEN_MAX + 1];
    uint16_t             port;
    pHttpCb_t            callback;
    void                *user_data;
    char                *request;
    int                  sockfd;
    bTaskAttr_t          attr;
    bTaskId_t            task_id;
    http_parser          parse;
    http_parser_settings parse_cb;
    uint8_t             *precv;
    uint16_t             recvbuf_len;
    uint16_t             recvbuf_index;
#if (defined(_SSL_ENABLE) && (_SSL_ENABLE == 1))
    bSSLHandle_t ssl;
#endif
} bHttpStruct_t;

/**
 * \}
 */

/**
 * \defgroup TCPIP_Private_Defines
 * \{
 */
// NTP时间的起始时间
#define B_NTP_TIMESTAMP_DELTA 2208988800ull
#define B_NTP_SERVER_NUM (3)
#define B_NTP_TIMEOUT_S (20)
/**
 * \}
 */

/**
 * \defgroup TCPIP_Private_Macros
 * \{
 */

/**
 * \}
 */

/**
 * \defgroup TCPIP_Private_Variables
 * \{
 */
static bNtpPcb_t bNtpPcb = {
    .task_id = 0,
};
B_TASK_CREATE_ATTR(bNtpTask);
static const char *bNtpServer[B_NTP_SERVER_NUM] = {_NTP_SERVER_1, _NTP_SERVER_2, _NTP_SERVER_3};

//------------------------------------------------------------------------------------------------

/**
 * \}
 */

/**
 * \defgroup TCPIP_Private_FunctionPrototypes
 * \{
 */

/**
 * \}
 */

/**
 * \defgroup TCPIP_Private_Functions
 * \{
 */

static void _bTcpipSrvFree(void *addr)
{
    bFree(addr);
}

static void _bNtpConnCallback(bTransEvent_t event, void *param, void *arg)
{
    ;
}

PT_THREAD(_bNtpTaskFunc)(struct pt *pt, void *arg)
{
    static uint8_t ntp_server_index = 0;
    bNtpPacket_t   packet;
    uint16_t       rlen     = 0;
    uint64_t       ntp_time = 0;
    PT_BEGIN(pt);
    while (1)
    {
        bNtpPcb.sockfd = bSocket(B_TRANS_CONN_UDP, _bNtpConnCallback, NULL);
        if (bNtpPcb.sockfd < 0)
        {
            b_log_e("socket fail...\r\n");
            break;
        }
        b_log("sockfd: %x %d\r\n", bNtpPcb.sockfd, ntp_server_index);
        bConnect(bNtpPcb.sockfd, (char *)bNtpServer[ntp_server_index], 123);
        PT_WAIT_UNTIL(pt, bSockIsWriteable(bNtpPcb.sockfd) == 1, B_NTP_TIMEOUT_S * 1000);
        if (pt->retval == PT_RETVAL_TIMEOUT)
        {
            b_log_e("ntp send fail...\r\n");
            PT_WAIT_UNTIL_FOREVER(pt, bShutdown(bNtpPcb.sockfd) >= 0);
            b_log_e("shutdown..\r\n");
            ntp_server_index = (ntp_server_index + 1) % B_NTP_SERVER_NUM;
            break;
        }
        memset(&packet, 0, sizeof(bNtpPacket_t));
        packet.li_vn_mode = 0x1b;
        bSend(bNtpPcb.sockfd, (uint8_t *)&packet, sizeof(bNtpPacket_t), NULL);
        PT_WAIT_UNTIL(pt, bSockIsReadable(bNtpPcb.sockfd) == 1, B_NTP_TIMEOUT_S * 1000);
        if (pt->retval == PT_RETVAL_TIMEOUT)
        {
            b_log_e("ntp recv timeout.. \r\n");
            PT_WAIT_UNTIL_FOREVER(pt, bShutdown(bNtpPcb.sockfd) >= 0);
            b_log_e("shutdown..\r\n");
            ntp_server_index = (ntp_server_index + 1) % B_NTP_SERVER_NUM;
            bTaskDelayMs(pt, 10000);
            break;
        }
        bRecv(bNtpPcb.sockfd, (uint8_t *)&packet, sizeof(bNtpPacket_t), &rlen);
        if (rlen == sizeof(bNtpPacket_t))
        {
            if (packet.recv_time.seconds <= packet.trans_time.seconds &&
                (packet.trans_time.seconds - packet.recv_time.seconds) <= 1)
            {
                ntp_time = B_SWAP_32(packet.trans_time.seconds) - B_NTP_TIMESTAMP_DELTA;
                bUTC_SetTime(ntp_time);
            }
        }
        PT_WAIT_UNTIL_FOREVER(pt, bShutdown(bNtpPcb.sockfd) >= 0);
        b_log_e("shutdown..\r\n");
        bTaskDelayMs(pt, bNtpPcb.interval_s * 1000);
    }
    PT_END(pt);
}

//--------------------------------------------------------http--
//--------------------------------------------------------http--
static int _bHttpParseUrl(const char *url, char *host, char *path, uint16_t *port, uint8_t *ishttps)
{
    // 检查是否是HTTPS
    if (strncmp(url, "https://", 8) == 0)
    {
        *ishttps = 1;
    }
    else if (strncmp(url, "http://", 7) == 0)
    {
        *ishttps = 0;
    }
    else
    {
        return -1;
    }

    // 跳过"http://"或"https://"
    const char *start = url + (*ishttps ? 8 : 7);

    // 查找host的结束位置
    const char *end = strchr(start, '/');
    if (end == NULL)
    {
        end = url + strlen(url);
    }
    if ((end - start) > _HTTP_HOST_LEN_MAX)
    {
        return -1;
    }
    // 复制host到目标字符串
    strncpy(host, start, end - start);
    host[end - start] = '\0';

    // 解析port
    const char *portStart = strchr(host, ':');
    if (portStart != NULL)
    {
        *port = atoi(portStart + 1);
    }
    else
    {
        *port = (*ishttps ? 443 : 80);
    }

    // 解析path
    if (*end != '\0')
    {
        if (strlen(end) > _HTTP_PATH_LEN_MAX)
        {
            return -1;
        }
        strcpy(path, end);
    }
    else
    {
        strcpy(path, "/");
    }
    return 0;
}

static char *_bHttpGetRequest(bHttpStruct_t *http)
{
    char *request      = NULL;
    int   request_size = 0;
    request_size       = strlen("GET /") + strlen(http->path) + strlen(" HTTP/1.1\r\n") +
                   strlen("Host: ") + strlen(http->host) + strlen("\r\n") +
                   strlen("Connection: close\r\n") + strlen("\r\n") + 1;

    request = (char *)bMalloc(request_size);
    if (request != NULL)
    {
        memset(request, 0, request_size);
        snprintf(request, request_size, "GET /%s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n",
                 http->path, http->host);
    }
    return request;
}

static char *_bHttpPostRequest(bHttpStruct_t *http, const char *head, const char *body)
{
    char *request      = NULL;
    int   request_size = 0;

    request_size = strlen("POST /") + strlen(http->path) + strlen(" HTTP/1.1\r\n") +
                   strlen("Host: ") + strlen(http->host) + strlen("\r\n") +
                   strlen("Content-Length: xxxxxx\r\n") + strlen("\r\n") + strlen(body) +
                   strlen("\r\n") + 1;
    if (head != NULL)
    {
        request_size += strlen(head);
    }
    request = (char *)bMalloc(request_size);
    if (request == NULL)
    {
        return NULL;
    }
    memset(request, 0, request_size);
    // 组装请求字符串
    if (head != NULL)
    {
        snprintf(request, request_size,
                 "POST /%s HTTP/1.1\r\nHost: %s\r\n%sContent-Length: %d\r\n\r\n"
                 "%s\r\n",
                 http->path, http->host, head, strlen(body), body);
    }
    else
    {
        snprintf(request, request_size,
                 "POST /%s HTTP/1.1\r\nHost: %s\r\nContent-Length: %d\r\n\r\n"
                 "%s\r\n",
                 http->path, http->host, strlen(body), body);
    }
    return request;
}

static void _bHttpResult(bHttpStruct_t *http, bHttpEvent_t evt, void *param)
{
    if (evt < 0 || evt == B_HTTP_EVENT_RECV_DATA || evt == B_HTTP_STA_DESTROY)
    {
        if (http->request)
        {
            bFree(http->request);
            http->request = NULL;
        }
        http->sockfd = -1;
#if (defined(_SSL_ENABLE) && (_SSL_ENABLE == 1))
        if (http->ssl != NULL)
        {
            bSSLDeinit(http->ssl);
            http->ssl = NULL;
        }
#endif
        http->state = B_HTTP_STA_DEINIT;
    }
    http->callback(evt, param, http->user_data);
    if (http->precv)
    {
        bFree(http->precv);
        http->precv = NULL;
    }
    http->recvbuf_index = 0;
    http->recvbuf_len   = 0;
}

static void _bHttpTransCb(bTransEvent_t event, void *param, void *arg)
{
}

static int _bHttpParseComplete(http_parser *parser)
{
    bHttpStruct_t *http = (bHttpStruct_t *)parser->data;
    http->state         = B_HTTP_STA_RECV_DATA;
    return 0;
}

PT_THREAD(_bHttpTaskFunc)(struct pt *pt, void *arg)
{
    int             ret   = 0;
    void           *param = NULL;
    bHttpEvent_t    event = B_HTTP_EVENT_ERROR;
    bHttpStruct_t  *http  = (bHttpStruct_t *)arg;
    bHttpRecvData_t dat;
    PT_BEGIN(pt);
    while (1)
    {
        if (http->state == B_HTTP_STA_DEINIT)
        {
            return 0;
        }
        if (http->state == B_HTTP_STA_DESTROY)
        {
            if (http->sockfd > 0)
            {
                SOCKET_SHUTDOWN(pt, http->sockfd);
                _bHttpResult(http, B_HTTP_EVENT_DESTROY, NULL);
            }
            bTaskRemove(http->task_id);
            bFree(http);
            break;  // task end
        }
        http->sockfd = bSocket(B_TRANS_CONN_TCP, _bHttpTransCb, http);
        if (http->sockfd < 0)
        {
            event = B_HTTP_EVENT_ERROR;
            param = NULL;
            goto http_restart;
        }
        b_log("sockfd: %x %s %d %s\r\n", http->sockfd, http->host, http->port, http->path);
        bConnect(http->sockfd, http->host, http->port);
        PT_WAIT_UNTIL(pt, bSockIsWriteable(http->sockfd) == 1, 5000);
        if (pt->retval == PT_RETVAL_TIMEOUT)
        {
            event = B_HTTP_EVENT_CONN_FAIL;
            param = NULL;
            goto http_restart;
        }
        ///////////////////////////////////////////////////////////////
#if (defined(_SSL_ENABLE) && (_SSL_ENABLE == 1))
        static uint16_t ssl_timeout = 0;
        ssl_timeout                 = 0;
        while (http->is_https)
        {
            ret = bSSLHandshake(http->ssl, http->sockfd);
            if (ret < 0)
            {
                event = B_HTTP_EVENT_SSL_FAIL;
                param = &ret;
                goto http_restart;
            }
            else if (ret == 0)
            {
                break;  // ssl handshake ok ...
            }
            else
            {
                bTaskDelayMs(pt, 10);
                ssl_timeout++;
                if (ssl_timeout >= 200)
                {
                    ssl_timeout = 0;
                    event       = B_HTTP_EVENT_SSL_FAIL;
                    param       = &ret;
                    goto http_restart;
                }
            }
        }
#endif
        //////////////////////////////////////////////////////////////
        _bHttpResult(http, B_HTTP_EVENT_CONNECTED, NULL);
        b_log("http req:%s\r\n", http->request);
#if (defined(_SSL_ENABLE) && (_SSL_ENABLE == 1))
        if (http->is_https)
        {
            bSSLSend(http->ssl, (uint8_t *)http->request, strlen(http->request), NULL);
        }
        else
#endif
        {
            bSend(http->sockfd, (uint8_t *)http->request, strlen(http->request), NULL);
        }
        http->parse.data = http;
        http_parser_init(&http->parse, HTTP_RESPONSE);
        http_parser_settings_init(&http->parse_cb);
        http->parse_cb.on_message_complete = _bHttpParseComplete;
        int      parse_len                 = 0;
        uint16_t readlen                   = 0;
        for (;;)
        {
            PT_WAIT_UNTIL(pt, bSockIsReadable(http->sockfd) == 1, 5000);
            if (pt->retval == PT_RETVAL_TIMEOUT)
            {
                event = B_HTTP_EVENT_RECV_TIMEOUT;
                param = NULL;
                goto http_restart;
            }
            if ((http->recvbuf_len - http->recvbuf_index) <= 128)
            {
                http->recvbuf_len += 1024;
                http->precv = bRealloc(http->precv, http->recvbuf_len);
                if (http->precv == NULL)
                {
                    event = B_HTTP_EVENT_ERROR;
                    param = NULL;
                    goto http_restart;
                }
            }
#if (defined(_SSL_ENABLE) && (_SSL_ENABLE == 1))
            if (http->is_https)
            {
                bSSLRecv(http->ssl, http->precv + http->recvbuf_index,
                         http->recvbuf_len - http->recvbuf_index, &readlen);
            }
            else
#endif
            {
                bRecv(http->sockfd, http->precv + http->recvbuf_index,
                      http->recvbuf_len - http->recvbuf_index, &readlen);
            }
            if (readlen > 0)
            {
                parse_len =
                    http_parser_execute(&http->parse, &http->parse_cb,
                                        (const char *)(http->precv + http->recvbuf_index), readlen);
                b_log("parse_len %d readlen %d index:%d\r\n", parse_len, readlen,
                      http->recvbuf_index);
                http->recvbuf_index += readlen;
                if (http->state == B_HTTP_STA_RECV_DATA)
                {
                    dat.pdat    = http->precv;
                    dat.len     = http->recvbuf_index;
                    dat.release = NULL;

                    event = B_HTTP_EVENT_RECV_DATA;
                    param = &dat;
                    goto http_restart;
                }
                else if (parse_len < 0)
                {
                    event = B_HTTP_EVENT_ERROR;
                    param = NULL;
                    goto http_restart;
                }
            }
        }
    http_restart:
        SOCKET_SHUTDOWN(pt, http->sockfd);
        _bHttpResult(http, event, param);
        bTaskRestart(pt);
    }
    PT_END(pt);
}

/**
 * \}
 */

/**
 * \addtogroup TCPIP_Exported_Functions
 * \{
 */

int bTcpipSrvInit()
{
    return 0;
}

int bSntpStart(uint32_t interval_s)
{
    if (bNtpPcb.task_id == NULL)
    {
        memset(&bNtpPcb, 0, sizeof(bNtpPcb));
        bNtpPcb.sockfd     = -1;
        bNtpPcb.interval_s = 60 * 60;
        bNtpPcb.task_id    = bTaskCreate("ntp", _bNtpTaskFunc, NULL, &bNtpTask);
    }
    if (interval_s == 0)
    {
        return -1;
    }
    bNtpPcb.interval_s = interval_s;
    return 0;
}

//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------

int bHttpInit(pHttpCb_t cb, void *user_data)
{
    bHttpStruct_t *http = NULL;
    if (cb == NULL)
    {
        return -1;
    }
    http = (bHttpStruct_t *)bMalloc(sizeof(bHttpStruct_t));
    if (http == NULL)
    {
        return -2;
    }
    memset(http, 0, sizeof(bHttpStruct_t));
    http->callback      = cb;
    http->request       = NULL;
    http->sockfd        = -1;
    http->state         = B_HTTP_STA_DEINIT;
    http->user_data     = user_data;
    http->precv         = NULL;
    http->recvbuf_index = 0;
    http->recvbuf_len   = 0;
    if ((http->task_id = bTaskCreate(NULL, _bHttpTaskFunc, http, &http->attr)) == NULL)
    {
        b_log_e("task create fail..\r\n");
        bFree(http);
        return -3;
    }
    return ((int)http);
}

int bHttpRequest(int httpfd, bHttpReqType_t type, const char *url, const char *head,
                 const char *body)
{
    int            ret     = -1;
    char          *request = NULL;
    bHttpStruct_t *http    = (bHttpStruct_t *)httpfd;
    if (httpfd <= 0 || !HTTPREQ_TYPE_IS_VALID(type) || url == NULL || http->callback == NULL)
    {
        b_log_e("http param errror..%p %d %p\r\n", http, type, url);
        return -1;
    }

    if (http->state != B_HTTP_STA_DEINIT)
    {
        b_log_e("http busy...\r\n");
        return -2;
    }
    memset(http->host, 0, sizeof(http->host));
    memset(http->path, 0, sizeof(http->path));
    ret = _bHttpParseUrl(url, http->host, http->path, &http->port, &http->is_https);
    if (ret < 0)
    {
        b_log_e("parse url fail..%d \r\n", ret);
        return -3;
    }
    b_log("host:%s port %d\n", http->host, http->port);
    b_log("path:%s \n", http->path);
    if (type == B_HTTP_GET)
    {
        request = _bHttpGetRequest(http);
    }
    else if (type == B_HTTP_POST)
    {
        request = _bHttpPostRequest(http, head, body);
    }
    if (request == NULL)
    {
        b_log_e("http create request fail...\r\n");
        return -4;
    }
    http->request = request;
    if (http->is_https)
    {
#if (defined(_SSL_ENABLE) && (_SSL_ENABLE == 1))
        http->ssl = bSSLInit(http->host, NULL);
        if (SSLHANDLE_IS_INVALID(http->ssl))
        {
            bFree(http->request);
            http->request = NULL;
            return -5;
        }
#endif
    }
    http->state = B_HTTP_STA_INIT;
    return 0;
}

int bHttpDeInit(int httpfd)
{
    if (httpfd <= 0)
    {
        return -1;
    }
    bHttpStruct_t *http = (bHttpStruct_t *)httpfd;
    http->state         = B_HTTP_STA_DESTROY;
    return 0;
}

/**
 * \}
 */

/**
 * \}
 */

/**
 * \}
 */

/**
 * \}
 */
#endif

/************************ Copyright (c) 2023 Bean *****END OF FILE****/