/**
 * @file    wm_socket_server_demo.c
 *
 * @brief   socket demo function
 *
 * @Author  dave
 *
 * Copyright (c) 2015 Winner Microelectronics Co., Ltd.
 */
#include "wm_include.h"
#include <string.h>
#include "wm_demo.h"


#if DEMO_STD_SOCKET_SERVER
#define     DEMO_SOCK_S_TASK_SIZE      		512
#define     DEMO_SOCK_BUF_SIZE            	1024
#define     BACKLOG     7       // how many pending connections queue will hold


#define		DEMO_SOCK_MSG_UART_RX			1
#define		DEMO_SOCK_MSG_UART_OFF			2


static OS_STK   sock_s_task_stk[DEMO_SOCK_S_TASK_SIZE];
static OS_STK   sock_s_rcv_task_stk[DEMO_SOCK_S_TASK_SIZE];
static OS_STK   sock_s_snd_task_stk[DEMO_SOCK_S_TASK_SIZE];


/**
 * @typedef struct demo_sock_s
 */
typedef struct demo_sock_s
{
    //    OS_STK *sock_s_task_stk;
    tls_os_queue_t *sock_s_q;
    //    OS_STK *sock_rcv_task_stk;
    //    OS_STK *sock_snd_task_stk;
    char *sock_rx;
    char *sock_tx;
    bool socket_ok;
    int server_fd;
    int my_port;
    int conn_num;
    int client_fd_queue[BACKLOG];
    u32 rcv_data_len[BACKLOG];
    int if_snd[BACKLOG];
    int all_snd;
    int uart_trans;
    int uart_txlen;
    int snd_data_len[BACKLOG];
} ST_Demo_Sock_S;

typedef struct demo_socks_uart
{
    u8 *rxbuf;
    u16 rxlen;
} ST_Demo_Socks_uart;


ST_Demo_Sock_S *demo_sock_s = NULL;

tls_os_queue_t *demo_socks_q = NULL;

ST_Demo_Socks_uart demo_socks_uart = {0};



static void demo_sock_s_task(void *sdata);
static void sock_server_recv_task(void *sdata);
static void sock_server_snd_task(void *sdata);

static void sock_server_recv_task(void *sdata)
{
    ST_Demo_Sock_S *sock_s = (ST_Demo_Sock_S *) sdata;
    int ret = 0;
    fd_set fdsr;
    //fd_set fdexpt;
    int maxsock = 0;
#if 1
    struct timeval tv;
#endif
    int i;

    for (;;)
    {
        tls_os_time_delay(1);
        if (sock_s->conn_num < 1)
        {
            // printf("no socket \n");
            continue;
        }
        /** initialize file descriptor set */
        FD_ZERO(&fdsr);
        //     FD_ZERO(&fdexpt);
#if 1       /**not use timeout*/
        /** timeout setting */
        tv.tv_sec = 1;
        tv.tv_usec = 0;
#endif
        maxsock = 0;
        /** add active connection to fd set */
        for (i = 0; i < BACKLOG; i++)
        {
            if (sock_s->client_fd_queue[i] != -1)
            {
                FD_SET(sock_s->client_fd_queue[i], &fdsr);
                //   FD_SET(sock_s->client_fd_queue[i], &fdexpt);
                if (sock_s->client_fd_queue[i] > maxsock)
                {
                    maxsock = sock_s->client_fd_queue[i];
                }
            }
        }

        ret = select(maxsock + 1, &fdsr, NULL, NULL, &tv);
        //ret = select(maxsock + 1, &fdsr, NULL, &fdexpt, NULL);
        //    printf("recv select ret=%d\n",ret);
        if (0 == ret)
        {
            //            printf("select ret=%d\n",ret);
            continue;
        }
        else if (-1 == ret)
        {
            printf("select error\n");
            continue;
        }

        for (i = 0; i < BACKLOG; i++)
        {
            if (-1 == sock_s->client_fd_queue[i])
            {
                continue;
            }
            if (FD_ISSET(sock_s->client_fd_queue[i], &fdsr))
            {
                memset(sock_s->sock_rx, 0, DEMO_SOCK_BUF_SIZE);
                ret = recv(sock_s->client_fd_queue[i], sock_s->sock_rx, DEMO_SOCK_BUF_SIZE, 0);
                if (ret <= 0)
                {
                    // client close
                    printf("client[%d] close\n", i);
                    closesocket(sock_s->client_fd_queue[i]);
                    FD_CLR(sock_s->client_fd_queue[i], &fdsr);
                    sock_s->conn_num --;
                    if (sock_s->conn_num <= 0)
                    {
                        sock_s->socket_ok = 0;
                    }
                    sock_s->client_fd_queue[i] = -1;
                }
                else
                {
                    // receive data
                    sock_s->rcv_data_len[i] += ret;
                    if(sock_s->uart_trans
                            && sock_s->if_snd[i])
                    {
                        tls_uart_write(TLS_UART_1, sock_s->sock_rx, ret);
                    }
                    else
                    {
                        printf("\nsock[%d] rcv len==%d\n", sock_s->client_fd_queue[i], sock_s->rcv_data_len[i]);
                    }
                }
            }

            //    if (FD_ISSET(sock_s->client_fd_queue[i], &fdexpt))
            //    {
            //        printf("expt error\n");
            //    }
        }

    }
}

static void sock_server_snd_task(void *sdata)
{
    ST_Demo_Sock_S *sock_s = (ST_Demo_Sock_S *) sdata;
    void *msg;
    int i;
    int len = DEMO_SOCK_BUF_SIZE;
    int ret;
    fd_set fdsr;
    int maxsock = 0;
    u8 if_send = 0;
    void *uart_msg;
    u16 offset = 0;
    u16 readlen = 0;

    for (;;)
    {
        tls_os_queue_receive(sock_s->sock_s_q, (void **) &msg, 0, 0);
        //   printf("msg=%d\n",msg);
        switch ((u32) msg)
        {
        case DEMO_MSG_UART_RECEIVE_DATA:
            while(1)
            {
                tls_os_time_delay(1);       /**adjust send speed*/
                FD_ZERO(&fdsr);
                /** add active connection to fd set */
                maxsock = 0;
                if_send = 0;
                for (i = 0; i < BACKLOG; i++)
                {
                    if (sock_s->client_fd_queue[i] != -1 && (sock_s->snd_data_len[i] != 0 || sock_s->uart_trans) &&
                            (1 == sock_s->if_snd[i] || 1 == sock_s->all_snd))
                    {
                        if_send = 1;
                        FD_SET(sock_s->client_fd_queue[i], &fdsr);

                        if (sock_s->client_fd_queue[i] > maxsock)
                        {
                            maxsock = sock_s->client_fd_queue[i];
                        }
                    }
                }
                //printf("maxsock==%d\n",maxsock);
                if (0 == if_send)
                {
                    //      printf("do not send\n");
                    break;
                }

                ret = select(maxsock + 1, NULL, &fdsr, NULL, NULL);
                if (ret <= 0)
                {
                    printf("send select ret=%d\n", ret);
                    continue;
                }

                if(sock_s->uart_trans)
                {
                    tls_os_queue_receive(demo_socks_q, (void **) &uart_msg, 0, 0);

                    if((u32)uart_msg != DEMO_SOCK_MSG_UART_RX)
                    {
                        continue;
                    }

                    readlen = (demo_socks_uart.rxlen > DEMO_SOCK_BUF_SIZE) ?
                              DEMO_SOCK_BUF_SIZE : demo_socks_uart.rxlen;
                    demo_socks_uart.rxbuf = tls_mem_alloc(readlen);
                    if(demo_socks_uart.rxbuf == NULL)
                    {
                        printf("demo_socks_uart->rxbuf malloc err\n");
                        continue;
                    }
                    ret = tls_uart_read(TLS_UART_1, demo_socks_uart.rxbuf, readlen);
                    if(ret < 0)
                    {
                        tls_mem_free(demo_socks_uart.rxbuf);
                        continue;
                    }
                    demo_socks_uart.rxlen -= ret;
                    readlen = ret;
                }

                for (i = 0; i < BACKLOG; i ++)
                {
                    if (-1 == sock_s->client_fd_queue[i] || (0 == sock_s->if_snd[i] &&
                            0 == sock_s->all_snd))
                    {
                        continue;
                    }
                    if (FD_ISSET(sock_s->client_fd_queue[i], &fdsr))
                    {

                        if(sock_s->uart_trans)
                        {
                            //								printf("send data len:%d\n", uart_msg->rxlen);
                            offset = 0;
                            do
                            {
                                ret = send(sock_s->client_fd_queue[i], demo_socks_uart.rxbuf + offset, readlen, 0);
                                offset += ret;
                                readlen -= ret;
                            }
                            while(readlen);
                            tls_mem_free(demo_socks_uart.rxbuf);
                        }
                        else
                        {
                            if (-1 == sock_s->snd_data_len[i])
                            {
                                len = DEMO_SOCK_BUF_SIZE;
                            }
                            else if(sock_s->snd_data_len[i] != 0)
                            {
                                len = (sock_s->snd_data_len[i] > DEMO_SOCK_BUF_SIZE) ?
                                      DEMO_SOCK_BUF_SIZE : sock_s->snd_data_len[i];
                            }

                            //			printf("send skno=%d\n",sock_s->client_fd_queue[i]);
                            memset(sock_s->sock_tx, sock_s->client_fd_queue[i] + 0x30, len);
                            ret = send(sock_s->client_fd_queue[i], sock_s->sock_tx, len, 0);
                            //			 printf("ret=%d\n",ret);
                            if (ret != -1)
                            {
                                if (sock_s->snd_data_len[i] != -1)
                                {
                                    sock_s->snd_data_len[i] -= ret;
                                }
                            }

                        }
                    }
                }
            }
            break;

        case DEMO_MSG_WJOIN_FAILD:
            sock_s->server_fd = -1;
            sock_s->socket_ok = FALSE;
            break;

        default:
            break;
        }
    }
}
int sck_s_send_data_demo(int skt_no, int len, int uart_trans)
{
    int i = 0;

    printf("\nsktno=%d, len=%d, uart_trans=%d\n", skt_no, len, uart_trans);
    if (NULL == demo_sock_s)
    {
        return WM_FAILED;
    }

    if (0 == skt_no || -1 == skt_no)
    {
        demo_sock_s->all_snd = 1;
        for (i = 0; i < BACKLOG; i ++)
        {
            demo_sock_s->snd_data_len[i] = len;
        }
    }
    else
    {
        demo_sock_s->all_snd = 0;
        for (i = 0; i < BACKLOG; i ++)
        {
            if(demo_sock_s->client_fd_queue[i] != -1)
            {
                if (skt_no == demo_sock_s->client_fd_queue[i])
                {
                    demo_sock_s->if_snd[i] = 1;
                    demo_sock_s->snd_data_len[i] = len;
                }
                else
                {
                    demo_sock_s->if_snd[i] = 0;
                }
            }
        }
    }

    if(demo_sock_s->uart_trans && (0 == uart_trans))
    {
        demo_sock_s->uart_trans = uart_trans;
        tls_os_queue_send(demo_socks_q, (void *) DEMO_SOCK_MSG_UART_OFF, 0);
    }

    demo_sock_s->uart_trans = uart_trans;

    tls_os_queue_send(demo_sock_s->sock_s_q,
                      (void *) DEMO_MSG_UART_RECEIVE_DATA, 0);

    return WM_SUCCESS;
}


static s16 demo_socks_rx(u16 len)
{
    demo_socks_uart.rxlen += len;
    tls_os_queue_send(demo_socks_q, (void *) DEMO_SOCK_MSG_UART_RX, 0);

    return 0;
}


int socket_server_demo(int port)
{
    int i;
    if (demo_sock_s)
    {
        printf("run\n");
        return WM_FAILED;
    }
    if (-1 == port)
    {
        port = 1020;
    }
    printf("server demo port=%d\n", port);
    if (NULL == demo_sock_s)
    {
        demo_sock_s = tls_mem_alloc(sizeof(ST_Demo_Sock_S));
        if (NULL == demo_sock_s)
        {
            goto _error;
        }
        memset(demo_sock_s, 0, sizeof(ST_Demo_Sock_S));
        for (i = 0; i < BACKLOG; i++)
        {
            demo_sock_s->client_fd_queue[i] = -1;
        }

        demo_sock_s->sock_rx = tls_mem_alloc(DEMO_SOCK_BUF_SIZE + 1);
        if (NULL == demo_sock_s->sock_rx)
        {
            goto _error3;
        }

        demo_sock_s->sock_tx = tls_mem_alloc(DEMO_SOCK_BUF_SIZE + 1);
        if (NULL == demo_sock_s->sock_tx)
        {
            goto _error4;
        }

        tls_os_queue_create(&demo_sock_s->sock_s_q, DEMO_QUEUE_SIZE);

        tls_os_queue_create(&demo_socks_q, DEMO_QUEUE_SIZE);

        tls_uart_set_baud_rate(TLS_UART_1, 115200);
        tls_uart_rx_callback_register(TLS_UART_1,(s16(*)(u16, void*))demo_socks_rx, NULL);


        //deal with user's socket message
        tls_os_task_create(NULL, NULL,
                           demo_sock_s_task,
                           (void *) demo_sock_s,
                           (void *) sock_s_task_stk, /* task's stack start address */
                           DEMO_SOCK_S_TASK_SIZE*sizeof(u32),    /* task's stack size, unit:byte */
                           DEMO_SOCKET_S_TASK_PRIO,
                           0);

        //deal with socket's rx data
        tls_os_task_create(NULL, "recv",
                           sock_server_recv_task,
                           (void *) demo_sock_s,
                           (void *) sock_s_rcv_task_stk,    /* task's stack start address */
                           DEMO_SOCK_S_TASK_SIZE*sizeof(u32),           /* task's stack size, unit:byte */
                           DEMO_SOCKET_S_RECEIVE_TASK_PRIO,
                           0);
        // deal with socket's tx data
        tls_os_task_create(NULL, "send",
                           sock_server_snd_task,
                           (void *) demo_sock_s,
                           (void *) sock_s_snd_task_stk,    /* task's stack start address */
                           DEMO_SOCK_S_TASK_SIZE*sizeof(u32),           /* task's stack size, unit:byte */
                           DEMO_SOCKET_S_SEND_TASK_PRIO,
                           0);
    }

    demo_sock_s->my_port = port;

    return WM_SUCCESS;


    tls_mem_free(demo_sock_s->sock_tx);
_error4:
    tls_mem_free(demo_sock_s->sock_rx);
_error3:
    tls_mem_free(demo_sock_s);
    demo_sock_s = NULL;
_error:
    printf("mem err\n");
    return WM_FAILED;
}


static void sock_s_net_status_changed_event(u8 status)
{
    switch (status)
    {
    case NETIF_WIFI_JOIN_FAILED:
        tls_os_queue_send(demo_sock_s->sock_s_q,
                          (void *) DEMO_MSG_WJOIN_FAILD, 0);
        break;
    case NETIF_WIFI_JOIN_SUCCESS:
        tls_os_queue_send(demo_sock_s->sock_s_q,
                          (void *) DEMO_MSG_WJOIN_SUCCESS, 0);
        break;
    case NETIF_IP_NET_UP:
        tls_os_queue_send(demo_sock_s->sock_s_q,
                          (void *) DEMO_MSG_SOCKET_CREATE, 0);
        break;
    default:
        break;
    }
}

static void showclient(void)
{
    int i;

    printf("client count: %d\n", demo_sock_s->conn_num);
    for (i = 0; i < BACKLOG; i++)
    {
        printf("[%d]:%d  ", i, demo_sock_s->client_fd_queue[i]);
    }
    printf("\n");
}

static int socket_server(void)
{
    int new_fd;                 // listen on sock_fd, new connection on new_fd
    struct sockaddr_in server_addr; // server address information
    struct sockaddr_in client_addr; // connector's address information
    socklen_t sin_size;
    int yes = 1;
    int i;
    fd_set fdsr;
    int ret;

    if ((demo_sock_s->server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        printf("create socket err\n");
        return WM_FAILED;
    }

    if (setsockopt(demo_sock_s->server_fd, SOL_SOCKET, SO_REUSEADDR, &yes,
                   sizeof(int)) == -1)
    {
        printf("set sockopt err\n");
        return WM_FAILED;
    }

    /** host byte order */
    server_addr.sin_family = AF_INET;
    /** short, network byte order */
    server_addr.sin_port = htons(demo_sock_s->my_port);
    /** automatically fill with my IP */
    server_addr.sin_addr.s_addr = ((u32) 0x00000000UL);
    memset(server_addr.sin_zero, '\0', sizeof(server_addr.sin_zero));

    if (bind(demo_sock_s->server_fd, (struct sockaddr *) &server_addr,
             sizeof(server_addr)) == -1)
    {
        printf("bind err\n");
        return 1;
    }

    if (listen(demo_sock_s->server_fd, BACKLOG) == -1)
    {
        printf("listen err\n");
        return 1;
    }

    printf("listen port %d\n", demo_sock_s->my_port);

    demo_sock_s->conn_num = 0;

    sin_size = sizeof(client_addr);

    while (1)
    {
        // initialize file descriptor set
        FD_ZERO(&fdsr);
        FD_SET(demo_sock_s->server_fd, &fdsr);

        ret = select(demo_sock_s->server_fd + 1, &fdsr, NULL, NULL, NULL);
        if(ret <= 0)
        {
            continue;
        }
        if (FD_ISSET(demo_sock_s->server_fd, &fdsr))
        {
            new_fd = accept(demo_sock_s->server_fd, (struct sockaddr *) &client_addr,
                            &sin_size);
            printf("accept newfd=%d\n", new_fd);
            if (new_fd <= 0)
            {
                printf("accept err\n");
                continue;
            }
            // add to fd queue
            if (demo_sock_s->conn_num < BACKLOG)
            {
                for (i = 0; i < BACKLOG; i++)
                {
                    if (demo_sock_s->client_fd_queue[i] == -1)
                    {
                        demo_sock_s->client_fd_queue[i] = new_fd;
                        demo_sock_s->conn_num++;
                        if (demo_sock_s->conn_num)
                        {
                            demo_sock_s->socket_ok = 1;
                            tls_os_queue_send(demo_sock_s->sock_s_q,
                                              (void *) DEMO_MSG_UART_RECEIVE_DATA, 0);
                        }
                        break;
                    }
                }
                printf("new connection client[%d] %s:%d\n",
                       demo_sock_s->conn_num, inet_ntoa(client_addr.sin_addr),
                       ntohs(client_addr.sin_port));
            }
            else
            {
                printf("max connections arrive, exit\n");
                closesocket(new_fd);
            }

            showclient();
        }
    }
#if 0
    // close other connections
    for (i = 0; i < BACKLOG; i++)
    {
        if (demo_sock_s->client_fd_queue[i] != -1)
        {
            closesocket(demo_sock_s->client_fd_queue[i]);
        }
    }
    closesocket(demo_sock_s->server_fd);
    return 0;
#endif
}


static void demo_sock_s_task(void *sdata)
{
    ST_Demo_Sock_S *sock_s = (ST_Demo_Sock_S *) sdata;
    void *msg;
    struct tls_ethif *ethif = tls_netif_get_ethif();

    printf("\nsock s task\n");

    if (ethif->status)          /*connected to ap and get IP*/
    {
        tls_os_queue_send(sock_s->sock_s_q, (void *) DEMO_MSG_SOCKET_CREATE, 0);
    }
    else
    {
        struct tls_param_ip ip_param;

        tls_param_get(TLS_PARAM_ID_IP, &ip_param, TRUE);
        ip_param.dhcp_enable = TRUE;
        tls_param_set(TLS_PARAM_ID_IP, &ip_param, TRUE);

        tls_wifi_set_oneshot_flag(1);   /*Enable oneshot configuration*/
        printf("\nwait one shot......\n");
    }
    tls_netif_add_status_event(sock_s_net_status_changed_event);

    for (;;)
    {
        tls_os_queue_receive(sock_s->sock_s_q, (void **) &msg, 0, 0);
        switch ((u32) msg)
        {
        case DEMO_MSG_WJOIN_SUCCESS:
            break;

        case DEMO_MSG_SOCKET_CREATE:
            ethif = tls_netif_get_ethif();
            printf("\nserver ip=%d.%d.%d.%d\n",  ip4_addr1(ip_2_ip4(&ethif->ip_addr)), ip4_addr2(ip_2_ip4(&ethif->ip_addr)),

                   ip4_addr3(ip_2_ip4(&ethif->ip_addr)), ip4_addr4(ip_2_ip4(&ethif->ip_addr)));
            socket_server();
            break;

        default:
            break;
        }
    }
}

#endif
