/**
 * @file    wm_socket_client_demo.c
 *
 * @brief   socket demo function
 *
 * @author  dave
 *
 * Copyright (c) 2015 Winner Microelectronics Co., Ltd.
 */

#include <string.h>
#include "wm_include.h"
#include "wm_demo.h"

#if DEMO_STD_SOCKET_CLIENT
#define     DEMO_SOCK_C_TASK_SIZE      		512
#define     DEMO_SOCK_BUF_SIZE            	512

#define		DEMO_SOCK_MSG_UART_RX			1
#define		DEMO_SOCK_MSG_UART_OFF			2



static OS_STK   sock_c_task_stk[DEMO_SOCK_C_TASK_SIZE];
static OS_STK   sock_c_rcv_task_stk[DEMO_SOCK_C_TASK_SIZE];


/**
 * @typedef struct demo_sock_c
 */
typedef struct demo_sock_c
{
    tls_os_queue_t *sock_c_q;
    char *sock_rx;
    char *sock_tx;
    bool socket_ok;
    int socket_num;
    int remote_port;
    u32 remote_ip;
    u32 rcv_data_len;
    int snd_skt_no;
    int snd_data_len;
    int uart_trans;
    int uart_txlen;

} ST_Demo_Sock_C;

typedef struct demo_sockc_uart
{
    u8 *rxbuf;
    u16 rxlen;
} ST_Demo_Sockc_uart;


ST_Demo_Sock_C *demo_sock_c = NULL;

tls_os_queue_t *demo_sockc_q = NULL;

ST_Demo_Sockc_uart demo_sockc_uart = {0};


static void demo_sock_c_task(void *sdata);


int create_client_socket_demo(void)
{
    struct sockaddr_in pin;

    memset(&pin, 0, sizeof(struct sockaddr));
    pin.sin_family = AF_INET;
    demo_sock_c->socket_num = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    pin.sin_addr.s_addr = demo_sock_c->remote_ip;
    pin.sin_port = htons(demo_sock_c->remote_port);

    printf("\nsocket num=%d\n", demo_sock_c->socket_num);
    if (connect(demo_sock_c->socket_num, (struct sockaddr *)&pin, sizeof(struct sockaddr)) != 0)
    {
        printf("connect failed! socket num=%d\n", demo_sock_c->socket_num);
        closesocket(demo_sock_c->socket_num);
        tls_os_queue_send(demo_sock_c->sock_c_q, (void *)DEMO_MSG_SOCKET_ERR, 0);
        return WM_FAILED;
    }
    else
    {
        demo_sock_c->socket_ok = TRUE;
    }

    return WM_SUCCESS;
}

int socket_client_demo(void)
{
    struct tls_ethif *ethif;

    ethif = tls_netif_get_ethif();
    printf("\nip=%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)));


    return create_client_socket_demo();
}

static void sock_client_recv_task(void *sdata)
{
    ST_Demo_Sock_C *sock_c = (ST_Demo_Sock_C *)sdata;
    int ret = 0;

    for(;;)
    {
        if (sock_c->socket_ok)
        {
            ret = 0;
            ret = recv(sock_c->socket_num, sock_c->sock_rx, DEMO_SOCK_BUF_SIZE, 0);

            if(ret > 0)
            {
                sock_c->rcv_data_len += ret;

                if(sock_c->uart_trans)
                {
                    tls_uart_write(TLS_UART_1, sock_c->sock_rx, ret);
                }
                else
                {
                    printf("rcv: %d\n", sock_c->rcv_data_len);
                }
            }
            else
            {
                sock_c->socket_ok = FALSE;
                closesocket(sock_c->socket_num);
                printf("closesocket: %d\n", sock_c->socket_num);
                sock_c->socket_num = 0;
                tls_os_queue_send(sock_c->sock_c_q, (void *)DEMO_MSG_SOCKET_ERR, 0);
            }
            continue;
        }
        tls_os_time_delay(1);
    }
}

int sck_c_send_data_demo(int len, int uart_trans)
{
    printf("\nlen=%d, uart_trans=%d\n", len, uart_trans);
    if (NULL == demo_sock_c)
    {
        return WM_FAILED;
    }
    if (!demo_sock_c->socket_ok)
    {
        printf("skt not created\n");
        return WM_FAILED;
    }

    demo_sock_c->snd_data_len = len;

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

    demo_sock_c->uart_trans = uart_trans;

    tls_os_queue_send(demo_sock_c->sock_c_q, (void *)DEMO_MSG_UART_RECEIVE_DATA, 0);

    return WM_SUCCESS;
}

static s16 demo_sockc_rx(u16 len)
{
    demo_sockc_uart.rxlen += len;
    tls_os_queue_send(demo_sockc_q, (void *) DEMO_SOCK_MSG_UART_RX, 0);

    return 0;
}


int Socket_Client_Demo(int port, char *ip)
{
    if (demo_sock_c)
    {
        printf("run\n");
        return WM_FAILED;
    }
    if (-1 == port || NULL == ip)
    {
        printf("please input remote port and ip\n");
        return WM_FAILED;
    }
    printf("\nremote ip: %s, remote port: %d\n", ip, port);

    if (NULL == demo_sock_c)
    {
        demo_sock_c = tls_mem_alloc(sizeof(ST_Demo_Sock_C));
        if (NULL == demo_sock_c)
        {
            goto _error;
        }
        memset(demo_sock_c, 0, sizeof(ST_Demo_Sock_C));

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

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

        tls_os_queue_create(&(demo_sock_c->sock_c_q), DEMO_QUEUE_SIZE);

        tls_os_queue_create(&demo_sockc_q, DEMO_QUEUE_SIZE);

        tls_uart_set_baud_rate(TLS_UART_1, 115200);
        tls_uart_rx_callback_register(TLS_UART_1, demo_sockc_rx);

        //deal with socket's message
        tls_os_task_create(NULL, NULL,
                           demo_sock_c_task,
                           (void *)demo_sock_c,
                           (void *)sock_c_task_stk, /* task's stack start address */
                           DEMO_SOCK_C_TASK_SIZE*sizeof(u32),   /* task's stack size, unit:byte */
                           DEMO_SOCKET_C_TASK_PRIO,
                           0);

        //deal with socket's rx data
        tls_os_task_create(NULL, NULL,
                           sock_client_recv_task,
                           (void *)demo_sock_c,
                           (void *)sock_c_rcv_task_stk, /* task's stack start address */
                           DEMO_SOCK_C_TASK_SIZE*sizeof(u32),       /* task's stack size, unit:byte */
                           DEMO_SOCKET_RECEIVE_TASK_PRIO,
                           0);
    }

    demo_sock_c->remote_port = port;
    demo_sock_c->remote_ip = ipaddr_addr(ip);

    return WM_SUCCESS;

_error4:
    tls_mem_free(demo_sock_c->sock_rx);
_error3:
    tls_mem_free(demo_sock_c);
    demo_sock_c = NULL;
_error:
    return WM_FAILED;
}


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

static void demo_sock_c_task(void *sdata)
{
    ST_Demo_Sock_C *sock_c = (ST_Demo_Sock_C *)sdata;
    void *msg;
    struct tls_ethif *ethif = tls_netif_get_ethif();
    int len;
    int ret;
    void *uart_msg;
    u16 offset = 0;
    u16 readlen = 0;


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

    if(ethif->status)	/*connected to ap and get IP*/
    {
        tls_os_queue_send(sock_c->sock_c_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_c_net_status_changed_event);

    for (;;)
    {
        tls_os_queue_receive(sock_c->sock_c_q, (void **)&msg, 0, 0);
        //printf("\n msg =%d\n",msg);
        switch((u32)msg)
        {
        case DEMO_MSG_WJOIN_SUCCESS:
            break;

        case DEMO_MSG_SOCKET_CREATE:
            socket_client_demo();
            break;

        case DEMO_MSG_WJOIN_FAILD:
            if(sock_c->socket_num > 0)
            {
                sock_c->socket_num = 0;
                sock_c->socket_ok = FALSE;
            }
            break;

        case DEMO_MSG_SOCKET_RECEIVE_DATA:
            break;

        case DEMO_MSG_UART_RECEIVE_DATA:

            while(sock_c->uart_trans)
            {
                tls_os_queue_receive(demo_sockc_q, (void **) &uart_msg, 0, 0);

                if(uart_msg != DEMO_SOCK_MSG_UART_RX)
                {
                    continue;
                }

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

                offset = 0;
                do
                {
                    ret = send(sock_c->socket_num, demo_sockc_uart.rxbuf + offset, readlen, 0);
                    offset += ret;
                    readlen -= ret;
                }
                while(readlen);
                tls_mem_free(demo_sockc_uart.rxbuf);
            }

            if (-1 == sock_c->snd_data_len)
            {
                len = DEMO_SOCK_BUF_SIZE;
            }
            else if(sock_c->snd_data_len != 0)
            {
                len = (sock_c->snd_data_len > DEMO_SOCK_BUF_SIZE) ?
                      DEMO_SOCK_BUF_SIZE : sock_c->snd_data_len;
            }
            else
            {
                break;
            }

            memset(sock_c->sock_tx, 0x35, len);
            ret = send(sock_c->socket_num, sock_c->sock_tx, len, 0);
            if (ret != -1)
            {
                if (sock_c->snd_data_len != -1)
                {
                    sock_c->snd_data_len -= ret;
                }
            }
            if (sock_c->socket_ok && sock_c->snd_data_len != 0)
            {
                tls_os_time_delay(1);
                tls_os_queue_send(sock_c->sock_c_q, (void *)DEMO_MSG_UART_RECEIVE_DATA, 0);
            }
            break;

        case DEMO_MSG_SOCKET_ERR:
            tls_os_time_delay(200);
            printf("\nsocket err\n");
            create_client_socket_demo( );
            break;

        default:
            break;
        }
    }

}

#endif
