/*
 * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 *
 * This file is part of the lwIP TCP/IP stack.
 *
 * Author: Adam Dunkels <adam@sics.se>
 *
 */

/* ========================================================================== */
/*                             Include Files                                  */
/* ========================================================================== */
#include <string.h>
#include <stdio.h>
#include <kernel/dpl/TaskP.h>
#include <kernel/dpl/ClockP.h>
#include "lwip/opt.h"
#include "lwip/sys.h"
#include "lwip/api.h"
#include "enet_apputils.h"

/* ========================================================================== */
/*                           Macros & Typedefs                                */
/* ========================================================================== */
#define HOST_SERVER_IP6  ("FE80::12:34FF:FE56:78AB")

#define HOST_SERVER_PORT  (8888)

#define APP_MAX_RX_DATA_LEN (1024U)

#define APP_NUM_ITERATIONS (2U)

#define APP_SEND_DATA_NUM_ITERATIONS (5U)

#define MAX_IPV4_STRING_LEN (20U)

char snd_buf[APP_MAX_RX_DATA_LEN];

/* ========================================================================== */
/*                         Structure Declarations                             */
/* ========================================================================== */

struct App_hostInfo_t
{
    ip_addr_t ipAddr1;
    ip_addr_t ipAddr2;
    uint16_t port;
};

/* ========================================================================== */
/*                          Function Declarations                             */
/* ========================================================================== */
static void AppTcp_fillHostSocketInfo(struct App_hostInfo_t* pHostInfo);

/* ========================================================================== */
/*                            Global Variables                                */
/* ========================================================================== */

static struct App_hostInfo_t gHostInfo;
static char   gHostServerIp4_1[MAX_IPV4_STRING_LEN] = "";
static char   gHostServerIp4_2[MAX_IPV4_STRING_LEN] = "";

/* ========================================================================== */
/*                          Function Definitions                              */
/* ========================================================================== */


static void AppTcp_fillHostSocketInfo(struct App_hostInfo_t* pHostInfo)
{
    EnetAppUtils_print("[0] IP eneterd is: %s \r\n", gHostServerIp4_1);
    EnetAppUtils_print("[1] IP eneterd is: %s \r\n", gHostServerIp4_2);
    int32_t addr_ok;
    pHostInfo->port = HOST_SERVER_PORT;
    memset(&pHostInfo->ipAddr1, 0, sizeof(pHostInfo->ipAddr1));
    memset(&pHostInfo->ipAddr2, 0, sizeof(pHostInfo->ipAddr2));
    ip_addr_t*  pAddr1 = &pHostInfo->ipAddr1;
    ip_addr_t*  pAddr2 = &pHostInfo->ipAddr2;
    IP_SET_TYPE_VAL(*pAddr, IPADDR_TYPE_V4);
    addr_ok = ip4addr_aton(gHostServerIp4_1, ip_2_ip4(pAddr1));
    addr_ok = ip4addr_aton(gHostServerIp4_2, ip_2_ip4(pAddr2));
    EnetAppUtils_assert(addr_ok);

    return;
}

static void AppTcp_simpleclient_1(void *pArg)
{
    struct netconn *pConn1 = NULL;
    err_t err1 = ERR_OK, connectError1 = ERR_OK;
    struct App_hostInfo_t* pHostInfo = (struct App_hostInfo_t*) pArg;
    uint32_t buf_len = 0;
    const enum netconn_type connType = NETCONN_TCP;

    /* Create a new connection identifier. */
    for (uint32_t pktIdx = 0; pktIdx < APP_NUM_ITERATIONS; pktIdx++)
    {
        struct netbuf *rxBbuf = NULL;
        pConn1 = netconn_new(connType);
        if (pConn1 != NULL)
        {
            /* Connect to the TCP Server */
            EnetAppUtils_print("[1]<<<< ITERATION %d >>>>\r\n", (pktIdx + 1));
            EnetAppUtils_print("[1] Connecting to: %s:%d \r\n", gHostServerIp4_2, HOST_SERVER_PORT);
            connectError1 = netconn_bind_if(pConn1, 1);
            connectError1 = netconn_connect(pConn1, &pHostInfo->ipAddr2, 8888);
            if (connectError1 != ERR_OK)
            {
                netconn_close(pConn1);
                DebugP_log("[1]Connection with the server isn't established\r\n");
                continue;
            }
           

            DebugP_log("[1]Connection with the server is established\r\n");
            // send the data to the server
            for ( uint32_t i = 0; i < APP_SEND_DATA_NUM_ITERATIONS; i++)
            {
                memset(&snd_buf, 0, sizeof(snd_buf));
                buf_len = snprintf(snd_buf, sizeof(snd_buf), "[1]Hello over TCP %d", i+1);
                err1 = netconn_write(pConn1, snd_buf, buf_len, NETCONN_COPY);
                if (err1 == ERR_OK)
                {
                    printf("[1]\"%s\" was sent to the Server\r\n", snd_buf);
                }
                else
                {
                    DebugP_log("couldn't send packet to server\r\n");
                    continue;
                }

                /* wait until the data is sent by the server */
                if (netconn_recv(pConn1, &rxBbuf) == ERR_OK)
                {
                    DebugP_log("[1]Successfully received the packet %d\r\n", i+1);
                    netbuf_delete(rxBbuf);
                }
                else
                {
                    DebugP_log("No response from server\r\n");
                }
            }
            netconn_close(pConn1);
            netconn_delete(pConn1);
            DebugP_log("[1]Connection closed\r\n");
            ClockP_sleep(1);
        }
    }
}

static void AppTcp_simpleclient(void *pArg)
{
    struct netconn *pConn = NULL;
    err_t err = ERR_OK, connectError = ERR_OK;
    struct App_hostInfo_t* pHostInfo = (struct App_hostInfo_t*) pArg;
    uint32_t buf_len = 0;
    const enum netconn_type connType = NETCONN_TCP;

    /* Create a new connection identifier. */
    for (uint32_t pktIdx = 0; pktIdx < APP_NUM_ITERATIONS; pktIdx++)
    {
        struct netbuf *rxBbuf = NULL;
        pConn = netconn_new(connType);
        if (pConn != NULL)
        {
            /* Connect to the TCP Server */
            EnetAppUtils_print("[0]<<<< ITERATION %d >>>>\r\n", (pktIdx + 1));
            EnetAppUtils_print("[0] Connecting to: %s:%d \r\n", gHostServerIp4_1, HOST_SERVER_PORT);
            connectError = netconn_bind_if(pConn, 0);
            connectError = netconn_connect(pConn, &pHostInfo->ipAddr1, 8888);
            if (connectError != ERR_OK)
            {
                netconn_close(pConn);
                DebugP_log("[0]Connection with the server isn't established\r\n");
                continue;
            }
           

            DebugP_log("[0]Connection with the server is established\r\n");
            // send the data to the server
            for ( uint32_t i = 0; i < APP_SEND_DATA_NUM_ITERATIONS; i++)
            {
                memset(&snd_buf, 0, sizeof(snd_buf));
                buf_len = snprintf(snd_buf, sizeof(snd_buf), "[0]Hello over TCP %d", i+1);
                err = netconn_write(pConn, snd_buf, buf_len, NETCONN_COPY);
                if (err == ERR_OK)
                {
                    printf("[0]\"%s\" was sent to the Server\r\n", snd_buf);
                }
                else
                {
                    DebugP_log("couldn't send packet to server\r\n");
                    continue;
                }

                /* wait until the data is sent by the server */
                if (netconn_recv(pConn, &rxBbuf) == ERR_OK)
                {
                    DebugP_log("[0]Successfully received the packet %d\r\n", i+1);
                    netbuf_delete(rxBbuf);
                }
                else
                {
                    DebugP_log("No response from server\r\n");
                }
            }
            netconn_close(pConn);
            netconn_delete(pConn);
            DebugP_log("[0]Connection closed\r\n");
            ClockP_sleep(1);
        }
    }
}

void AppTcp_showMenu(void)
{
    ip_addr_t ipAddr1;
    ip_addr_t ipAddr2;
    int32_t addr_ok = 0;
    EnetAppUtils_print(" TCP Client Menu: \r\n");

    do
    {
        EnetAppUtils_print(" Enter TCP server-1 IPv4 address:(example: 192.168.101.100)\r\n");
        DebugP_scanf("%s", gHostServerIp4_1);
        EnetAppUtils_print(" Enter TCP server-2 IPv4 address:(example: 192.168.101.100)\r\n");
        DebugP_scanf("%s", gHostServerIp4_2);
        addr_ok = ip4addr_aton(gHostServerIp4_1, ip_2_ip4(&ipAddr1));
        addr_ok = ip4addr_aton(gHostServerIp4_2, ip_2_ip4(&ipAddr2));
        TaskP_yield();
    } while (addr_ok != 1);
}

void AppTcp_startClient(void)
{
    AppTcp_showMenu();
    AppTcp_fillHostSocketInfo(&gHostInfo);
    sys_thread_new("tcpinit_thread", AppTcp_simpleclient, &gHostInfo, DEFAULT_THREAD_STACKSIZE, DEFAULT_THREAD_PRIO);
    sys_thread_new("tcpinit_thread1", AppTcp_simpleclient_1, &gHostInfo, DEFAULT_THREAD_STACKSIZE, DEFAULT_THREAD_PRIO);

  /* USER CODE END 5 */
}
