/**
 * @file comms_sband_xlink_sock.c
 *
 * @brief This file contains APIs for S-Band parameters configuration
 *        programming and readback
 *
 * @copyright Copyright 2023 Antaris, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
/*********************************************************************/
#include <stdio.h>



#ifdef LINUX_TEMP_PORT
#include <netdb.h>
#include <netinet/in.h>
#endif

#include <csp/csp_endian.h>
#include <time.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include "comms_csw.h"
#include <sys/types.h>
#include <unistd.h>
#include "stdint.h"
#include "exo_common.h"
#include "exo_osal.h"
#include "comms_sband_xlink_sock.h"
#include "comms_sband_xlink_api.h"
#include "comms_sband_xlink_ss.h"
#include "sdr_app_main.h"
#include "exo_io_al_rtc_common.h"
#include "exo_io_al_sos_timer.h"
#include "exo_tctm_ipc.h"
#include "logger.h"
#include "comms_sband_init.h"
#include "err_handler.h"
#include "obc_main.h"


/** IQS server address */
struct sockaddr_in PLACE_IN_EXT_RAM_MEM iqs_servaddr;

/** IQS socket timeout */
struct timeval PLACE_IN_EXT_RAM_MEM iqs_timeout;

/** IQS connection file descriptor */
int PLACE_IN_EXT_RAM_MEM iqs_connfd;

/** IQS socket file descriptor */
int PLACE_IN_EXT_RAM_MEM iqs_sockfd;

/** S-Band Xlink socket status */ 
uint8_t PLACE_IN_EXT_RAM_MEM sband_xlink_sock_sts = 0;

/** Time structure */
sys_time_s PLACE_IN_EXT_RAM_MEM get_t_iqs;

/** Date-time structure */
sys_time_date_s PLACE_IN_EXT_RAM_MEM tim_get_dev;

/** Scheduler based ground contact flag */
extern uint8_t PLACE_IN_EXT_RAM_MEM uhf_sch_gs_contact;

/** Timer used for transmit ON duration */
extern os_timer_handle_ptr uhf_tx_tm_on;
extern s_sdr_tmr_cfg PLACE_IN_EXT_RAM_MEM uhf_tmr_cfg;

/** S-Band ground station contact flag */
extern volatile uint8_t PLACE_IN_EXT_RAM_MEM sband_sch_gs_contact;
/** S-Band TX timer handle */
extern os_timer_handle_ptr sband_tx_tm_on;
/** S-Band timer configuration */
extern s_sdr_tmr_cfg PLACE_IN_EXT_RAM_MEM sband_tmr_cfg;
/** S-Band last product key */
extern uint32 sband_lastProductKey[4];
/** IQS RX buffer */
uint8_t  PLACE_IN_EXT_RAM_MEM iqs_rx_buff[IQS_MTU_SIZE] = {0};
/** IQS TX buffer */
uint8_t  PLACE_IN_EXT_RAM_MEM iqs_tx_buff[IQS_MTU_SIZE] = {0};
/** IQS receive buffer */
uint8_t  PLACE_IN_EXT_RAM_MEM iqs_rcv_buff[CLTU_SIZE] = {0};
/** UDP RX frame count */
uint32 PLACE_IN_EXT_RAM_MEM rx_udp_frame = 0;
/** UDP TX frame count */
uint32 PLACE_IN_EXT_RAM_MEM tx_udp_frame = 0;

//uint8_t PLACE_IN_EXT_RAM_MEM debug_rx_dump[64][IQS_MTU_SIZE];

/** S-Band service device configuration */
extern SERVICE_DEVICE_CONF_STRUCT   PLACE_IN_EXT_RAM_MEM sband_serv_device_conf;
/** OBC IQS simulator socket info */
s_obc_sock_info obc_iqs_sim_soc_inf;
/** OBC IQS simulator socket info 1*/
s_obc_sock_info obc_iqs_sim_soc1_inf;

extern os_timer_handle_ptr PLACE_IN_EXT_RAM_MEM sband_beacon_enb_timer; ///< S-band beacon enable timer

uint32_t dbg_iqs_fail[10];

/**
 * @brief This API initialize the server socket connection
 * @brief Initializes the S-Band Xlink client.
 *
 * This function performs initialization of the S-Band Xlink client,
 * including socket creation and connection setup.
 */
void sband_xlink_client_init()
{
    iqs_timeout.tv_sec = 1;
    iqs_timeout.tv_usec = 0;

#ifdef COMMS_TIME_DEBUG
    get_crnt_time_date(&tim_get_dev);
    DEBUG_CPRINT(("\nIQS TIME TCP ENT %d:%d:%d",tim_get_dev.hour, tim_get_dev.minute, tim_get_dev.seconds));
    log_id_with_three_arg(LOG_INFO_L2,LOG_COMMS,IQS_TIME_TCP_ENT__u8__u8__u8_,tim_get_dev.hour, tim_get_dev.minute, tim_get_dev.seconds);
#endif
    // socket create and verification
    iqs_sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (iqs_sockfd == -1)
    {
        DEBUG_CPRINT(("\nS-Band IQ Spacecom socket creation failed...\n"));
        dbg_iqs_fail[0] = (dbg_iqs_fail[0] + 1) & 0xFFFFFFFF;
        log_id(LOG_ERROR,LOG_COMMS,SBAND_IQ_SPACECOM_SOCKET_CREATION_FAILED);
    }
    else
    {
        DEBUG_CPRINT(("\nS-Band IQ Spacecom simu Socket successfully created..\n"));
        log_id(LOG_INFO_L1,LOG_COMMS,SBAND_IQ_SPACECOM_SIMU_SOCKET_SUCCESFULLY_CREATED);
    }

    if (setsockopt (iqs_sockfd, SOL_SOCKET, SO_RCVTIMEO, &iqs_timeout,
                sizeof iqs_timeout) < 0)
    {
        DEBUG_CPRINT(("RCV setsockopt failed\n"));
        log_id(LOG_ERROR,LOG_COMMS,SET_SOCKPOT_FAILED);
        dbg_iqs_fail[1] = (dbg_iqs_fail[1] + 1) & 0xFFFFFFFF;
    }

    if (setsockopt (iqs_sockfd, SOL_SOCKET, SO_SNDTIMEO, &iqs_timeout,
                sizeof iqs_timeout) < 0)
    {
        DEBUG_CPRINT(("SND setsockopt failed\n"));
        log_id(LOG_ERROR,LOG_COMMS,SET_SOCKPOT_FAILED);
        dbg_iqs_fail[9] = (dbg_iqs_fail[9] + 1) & 0xFFFFFFFF;
    }

    bzero(&iqs_servaddr, sizeof(struct sockaddr_in));
    // assign IP, PORT

#ifdef LINUX_TEMP_PORT
    iqs_servaddr.sin_family = AF_INET;
    //iqs_servaddr.sin_addr.s_addr = inet_addr("192.168.10.67");
    iqs_servaddr.sin_addr.s_addr = inet_addr(obc_iqs_sim_soc_inf.ip_addr);
    iqs_servaddr.sin_port = htons((obc_iqs_sim_soc_inf.port));
#else
    ip4_addr_t sband_xlink_ipaddr;
    IP4_ADDR(&sband_xlink_ipaddr, 192, 168, 1, 1);
    iqs_servaddr.sin_family = AF_INET;
    iqs_servaddr.sin_addr.s_addr = sband_xlink_ipaddr.addr;
    iqs_servaddr.sin_port = htons(51003);
#endif

}

/**
 * @brief This API accept the client request
 * @brief Connects the S-Band Xlink client to server.
 *
 * @return ret - Connection status
 *  
 * This function handles connecting the S-Band Xlink client to the
 * Xlink server over TCP/IP.
 */
uint8_t sband_client_connect()
{
    uint8_t ret= 0;
    socklen_t len = sizeof(struct sockaddr_in);
    // Accept the data packet from client and verification
    iqs_connfd = connect(iqs_sockfd, (const struct sockaddr *)&iqs_servaddr,len);

    if (iqs_connfd < 0) {
        sband_xlink_sock_sts = 0;
        ret = 1;
        DEBUG_CPRINT(("\nS-Band IQ Spacecom connect failed...\n"));
        log_id(LOG_ERROR,LOG_COMMS,SBAND_IQ_SPACECOM_CONNECT_FAILED);
    }
    else
    {
        sband_xlink_sock_sts = 1;
        ret = 0;
        DEBUG_CPRINT(("\nS-Band IQ Spacecom client-server connected...\n"));
        log_id(LOG_INFO_L1,LOG_COMMS,SBAND_IQ_SPACECOM_CLIENT_SERVER_CONNECTED);
        //rtc_print_time();

    }

    return ret;
}

/**
 * @brief This API write the data
 * @brief Writes data over S-Band Xlink.
 *
 * @param[in] length Length of data to write in bytes.
 * @param[in] buff Pointer to data buffer to write.
 *
 * @return ret - Execution status
 *
 * This function writes input data buffer over the S-Band Xlink socket.
 */
uint8_t sband_cmd_write(uint16_t length, uint8_t *buff)
{
    int ret=0;
    uint8_t rep_cnt =0;

DO_CONNECT_AGAIN:
    if(PWR_ON == sband_get_state())
    {
#ifdef COMMS_TIME_DEBUG
        get_crnt_time_date(&tim_get_dev);
        DEBUG_CPRINT(("\nIQS TIME CMD WR ENT %d:%d:%d",tim_get_dev.hour, tim_get_dev.minute, tim_get_dev.seconds));
        log_id_with_three_arg(LOG_INFO_L2,LOG_COMMS,IQS_TIME_CMD_WR_ENT__u8__u8__u8_,tim_get_dev.hour, tim_get_dev.minute, tim_get_dev.seconds);
#endif
        if(sband_xlink_sock_sts == 0)
        {
            sband_xlink_client_init();
            ret = sband_client_connect();
            if ((ret == 1) && (rep_cnt < 3)) 
            {
                dbg_iqs_fail[2] = (dbg_iqs_fail[2] + 1) & 0xFFFFFFFF;
                err_detected(ERR_GROUP_COMMS, ERR_COMMS_SBAND_TCP_CONNECTION_FAIL);
                rep_cnt ++;
                close(iqs_sockfd);
                os_delay(2000);
                if(rep_cnt == 3)
                {
                    return 1;
                }
                goto DO_CONNECT_AGAIN;
            }
        }
        rep_cnt = 0;

        DEBUG_CPRINT(("Socket status: %d \nS-Band IQ Spacecom write data: ",sband_xlink_sock_sts));
        log_id_with_one_arg(LOG_INFO_L1,LOG_COMMS,SOCKET_STATUS_SBAND_IQ_SPACECOM_WRITE_DATA__u8_,sband_xlink_sock_sts);
        /*    for (int i=0;i<length;i++)
              {
              DEBUG_CPRINT(("%x\t",buff[i]));
              }*/

        DEBUG_CPRINT(("\n"));

        os_memcpy(iqs_tx_buff,buff,length);
        length = IQS_MTU_SIZE;

#if 0
        for (int i=0;i<length;i++)
        {
            DEBUG_CPRINT(("%x\t",iqs_tx_buff[i]));
            log_id_with_one_arg(LOG_INFO_L2,LOG_COMMS,__u8_,iqs_tx_buff[i]);
        }
        DEBUG_CPRINT(("\n"));
#endif

        if(sband_xlink_sock_sts)
        {
            if(check_socket_disconnected(iqs_sockfd))
            {
                sband_xlink_sock_sts = 0;
                ret = 1;
                close(iqs_sockfd);
            }
            else
            {
repeat_tx:
                ret = send(iqs_sockfd,iqs_tx_buff,length,MSG_DONTWAIT);
                if(ret!=-1)
                {
                    DEBUG_CPRINT(("\nS-Band IQ Spacecom socket write success : %d\n",ret));
                    log_id_with_one_arg(LOG_INFO_L1,LOG_COMMS,SBAND_IQ_SPACECOM_SOCKET_WRITE_SUCCESS__u8_,ret);
                    ret=0;

                    ret = sband_cmd_read(iqs_rx_buff,length);

                    if((ret == SBAND_TM_BUSY) && (rep_cnt < 5))
                    {
                        dbg_iqs_fail[3] = (dbg_iqs_fail[3] + 1) & 0xFFFFFFFF;
                        rep_cnt ++;
                        err_detected(ERR_GROUP_COMMS, ERR_COMMS_SBAND_RSP_CMD);
                        DEBUG_CPRINT(("\nS-Band repeating transmission\n"));
                        log_id(LOG_INFO_L1,LOG_COMMS,SBAND_REPEATING_TRANSMISSION);
                        os_delay(2000);

                        if(rep_cnt == 5)
                        {
                            return 1;
                        }
                        goto repeat_tx;
                    }
                }
                else
                {
                    dbg_iqs_fail[4] = (dbg_iqs_fail[4] + 1) & 0xFFFFFFFF;
                    err_detected(ERR_GROUP_COMMS, ERR_COMMS_SBAND_BUSY_TCP_WRITE);
                    DEBUG_CPRINT(("\nS-Band IQ Spacecom socket write fail\n"));
                    log_id(LOG_ERROR,LOG_COMMS,SBAND_IQ_SPACECOM_SOCKET_WRITE_FAIL);

                    rep_cnt ++;
                    if(rep_cnt < 5)
                    {
                        goto repeat_tx;
                    }

                    ret=1;
                    sband_xlink_sock_sts = 0;
                    //rtc_print_time();
                    close(iqs_sockfd);
                    /** This is added to mitigate the Back to back connect failure */
                    os_delay(500);
                    DEBUG_CPRINT(("\nIQ Spacecom connection closed \n"));
                    log_id(LOG_ERROR,LOG_COMMS,IQ_SPACECOM_CONNECTION_CLOSED);
                }
            }
        }

        if(sband_xlink_sock_sts)
        {
            sband_xlink_sock_sts = 0;
            //rtc_print_time();
            close(iqs_sockfd);
            /** This is added to mitigate the Back to back connect failure */
            os_delay(500);
            DEBUG_CPRINT(("\nIQ Spacecom connection closed \n"));
            log_id(LOG_ERROR,LOG_COMMS,IQ_SPACECOM_CONNECTION_CLOSED);
#ifdef COMMS_TIME_DEBUG
            get_crnt_time_date(&tim_get_dev);
            DEBUG_CPRINT(("\nIQS TIME CMD WR EXT %d:%d:%d",tim_get_dev.hour, tim_get_dev.minute, tim_get_dev.seconds));
            log_id_with_three_arg(LOG_INFO_L2,LOG_COMMS,IQS_TIME_CMD_WR_EXT__u8__u8__u8_,tim_get_dev.hour, tim_get_dev.minute, tim_get_dev.seconds);
#endif
        }
    }
    else
    {
        comms_sband_xlinks_cfg_nvm_write();
        ret = 1;
    }  

    return ret;
}


/**
 * @brief This API read the data
 * @brief Reads data over S-Band Xlink.
 *
 * @param[out] buff Pointer to buffer for holding read data.
 * @param[in] length Length of data to read in bytes.
 * 
 * @return ret - Execution status
 *
 * This function reads data of specified length over the S-Band Xlink socket.
 */
uint8_t sband_cmd_read(uint8_t *buff, uint16_t length)
{
    int ret=0;
    int rd_sz = 0;
    int rx_repeat_cnt = 0;
#ifdef COMMS_TIME_DEBUG
    get_crnt_time_date(&tim_get_dev);
    DEBUG_CPRINT(("\nIQS TIME CMD RD ENT Len %d %d:%d:%d", length, tim_get_dev.hour, tim_get_dev.minute, tim_get_dev.seconds));
    log_id_with_three_arg(LOG_INFO_L2,LOG_COMMS,IQS_TIME_CMD_RD_ENT_LEN__u8__u8__u8_,tim_get_dev.hour, tim_get_dev.minute, tim_get_dev.seconds);
#endif
    if(sband_xlink_sock_sts)
    {
repeat_rx:
        ret = read(iqs_sockfd,buff,length);
        if(ret<=0)
        {
            dbg_iqs_fail[5] = (dbg_iqs_fail[5] + 1) & 0xFFFFFFFF;
            log_id(LOG_ERROR,LOG_COMMS,SBAND_IQ_SPACECOM_SOCKET_READ_FAIL);
            err_detected(ERR_GROUP_COMMS, ERR_COMMS_SBAND_TCP_READ_FAIL);

            DEBUG_CPRINT(("\nS-Band IQ Spacecom socket Read fail : %d\n",ret));

            rx_repeat_cnt ++;
            if(rx_repeat_cnt < 2)
            {
                goto repeat_rx;
            }

            sband_xlink_sock_sts = 0;
            close(iqs_sockfd);
            DEBUG_CPRINT(("\nIQ Spacecom Read Fail \n"));
            log_id(LOG_ERROR,LOG_COMMS,IQ_SPACECOM_CONNECTION_CLOSED);
            ret=1;
            return ret;
        }
        else
        {
            DEBUG_CPRINT(("\nS-Band IQ Spacecom socket Read success : %d\n",ret));
            log_id_with_one_arg(LOG_INFO_L1,LOG_COMMS,SBAND_IQ_SPACECOM_SOCKET_READ_SUCCESS__u8_,ret);
            rd_sz = ret;
            ret=0;

            //DEBUG_CPRINT(("S-Band IQ Spacecom Response==\n"));
                /*         for (int i=0;i<length;i++)
                           {
                           DEBUG_CPRINT(("%x\t",buff[i]));
                           } */
            ret = sband_proc_rcv_data(buff,rd_sz);
        }

    }
#ifdef COMMS_TIME_DEBUG 
    get_crnt_time_date(&tim_get_dev);
    DEBUG_CPRINT(("\nIQS TIME CMD RD EXT %d:%d:%d",tim_get_dev.hour, tim_get_dev.minute, tim_get_dev.seconds));
    log_id_with_three_arg(LOG_INFO_L2,LOG_COMMS,IQS_TIME_CMD_RD_EXT__u8__u8__u8_,tim_get_dev.hour, tim_get_dev.minute, tim_get_dev.seconds);
#endif

#ifdef COMMS_TIME_DEBUG
    get_crnt_time_date(&tim_get_dev);
    DEBUG_CPRINT(("\nIQS TIME TCP EXT %d:%d:%d",tim_get_dev.hour, tim_get_dev.minute, tim_get_dev.seconds));
    log_id_with_three_arg(LOG_INFO_L2,LOG_COMMS,IQS_TIME_TCP_EXT__u8__u8__u8_,tim_get_dev.hour, tim_get_dev.minute, tim_get_dev.seconds);
#endif
    return ret;
}

/**
 * @brief S-Band Xlink receive thread.
 * 
 * @param[in] args Thread arguments (unused).
 *
 * This thread handles receiving data over the S-Band Xlink socket.
*/
void* sband_xlink_rcv_thrd(void* args)
{
    os_itc_msg_handle_t trans_msg;
    int32 ret=0;
    //uint32 PktCnt =0;
    int32 fdReady = -1;
    uint8 rcvbuff[BUFFERSIZE];
    uint32 sock1;
    //uint32 rcv_cnt = 0;
    uint32 ByteRcv = 0;

    struct sockaddr_in serv_addr;
    fd_set readfds;                                          // the flag sets to be used

    memset(&serv_addr, 0, sizeof(serv_addr));
#ifdef COMMS_TIME_DEBUG 
    get_crnt_time_date(&tim_get_dev);
    DEBUG_CPRINT(("\nIQS TIME UDP SER_CON %d:%d:%d",tim_get_dev.hour, tim_get_dev.minute, tim_get_dev.seconds));
    log_id_with_three_arg(LOG_INFO_L2,LOG_COMMS,IQS_TIME_UDP_SER_CON__u8__u8__u8_,tim_get_dev.hour, tim_get_dev.minute, tim_get_dev.seconds);
#endif
    //create a socket
    if ((sock1 = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat"
        DEBUG_CPRINT(("\nsocket creation failed: %d", sock1));
        log_id(LOG_ERROR,LOG_COMMS,SOCKET_CREATION_FAILED);
#pragma GCC diagnostic pop

    }

#ifdef LINUX_TEMP_PORT
    serv_addr.sin_family = AF_INET;
    //serv_addr.sin_addr.s_addr = inet_addr("192.168.10.67");
    serv_addr.sin_addr.s_addr = inet_addr(obc_iqs_sim_soc1_inf.ip_addr);
    serv_addr.sin_port = htons((obc_iqs_sim_soc1_inf.port));
#else

    ip4_addr_t eth_ipaddr;
    serv_addr.sin_family = AF_INET;
    IP4_ADDR(&eth_ipaddr, ETH_IP_ADDR0, ETH_IP_ADDR1, ETH_IP_ADDR2, ETH_IP_ADDR3);
    serv_addr.sin_addr.s_addr = eth_ipaddr.addr; //inet_addr("192.168.1.111"); 
    serv_addr.sin_port = htons(51004);
#endif

    const int opt = 1;
    if (setsockopt(sock1, SOL_SOCKET, SO_RCVBUF, (char*)&opt, sizeof(opt)) == -1) {
        DEBUG_CPRINT(("\nerror setting socket opts"));
        log_id(LOG_INFO_L2,LOG_COMMS,ERROR_SETTING_SOCKET_OPTS);
    }

    if( bind(sock1, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0 )
    {
        DEBUG_CPRINT(("\nIQS socket bind failed"));
        log_id(LOG_ERROR,LOG_COMMS,IQ_SOCKET_BIND_FAILED);
        close(sock1);
    }

    while(1)
    {

        DEBUG_CPRINT(("waiting for New Client Connections\n"));
        log_id(LOG_INFO_L3,LOG_COMMS,WAITING_FOR_NEW_CLIENT_CONNECTIONS);
        FD_ZERO(&readfds);
        FD_SET(sock1, &readfds);
        fdReady = select(sock1 + 1, &readfds, NULL, NULL, NULL);

        if ((fdReady < 0))// && (errno!=EINTR))
        {
            perror("FD select error");
            log_id(LOG_ERROR,LOG_COMMS,FD_SELECT_ERROR);
        }
        else{
            //If something happened on the master socket , then its an incoming connection
            if(FD_ISSET(sock1, &readfds))
            {
                bzero(rcvbuff, sizeof(rcvbuff));
                DEBUG_CPRINT(("\nMessage from GS via iqs client: "));
                log_id(LOG_INFO_L3,LOG_COMMS,MESSAGE_FROM_GS_VIA_IQS_CLIENT);
                ret = recvfrom(sock1, rcvbuff, RX_BUFF_SIZE, MSG_DONTWAIT, NULL,NULL); //to do MSG_DONTWAIT //MSG_WAITALL
                if( ret <= 0)
                {
                    err_detected(ERR_GROUP_COMMS, ERR_COMMS_SBAND_READ_UDP_RX_FAIL);
                    DEBUG_CPRINT(("\n\n socket recv error"));
                    log_id(LOG_ERROR,LOG_COMMS,SOCKET_RECV_ERROR);
                    break;
                }
                else
                {
                    ByteRcv = ret;
                    rx_udp_frame++;

                    //os_memcpy(debug_rx_dump[(rx_udp_frame&0x3F)],&rcvbuff[0],sizeof(rcvbuff));

                    DEBUG_CPRINT(("Rx Frame Count is %d, pkt_size %d", rx_udp_frame, ret));
                    log_id_with_two_arg(LOG_INFO_L1,LOG_COMMS,RX_FRAME_COUNT__u8__PKT_SIZE__u8_,rx_udp_frame, ret);
#ifdef COMMS_TIME_DEBUG
                    get_crnt_time_date(&tim_get_dev);
                    DEBUG_CPRINT(("\nIQS TIME RX ENT %d:%d:%d",tim_get_dev.hour, tim_get_dev.minute, tim_get_dev.seconds));
                    log_id_with_three_arg(LOG_INFO_L2,LOG_COMMS,IQS_TIME_RX_ENT__u8__u8__u8_,tim_get_dev.hour, tim_get_dev.minute, tim_get_dev.seconds);
#endif
                    DEBUG_CPRINT(("\n\rIQS RX data: "));
                    for (int i=0;i<ret;i++)
                    {
                        DEBUG_CPRINT(("%x ",(uint8_t)rcvbuff[i]));
                    }
                    DEBUG_CPRINT(("\n"));

                    trans_msg.src_entity= COMMS_RX;
                    trans_msg.pld.pld_ptr = (uint8_t *)os_malloc(ByteRcv);
                    os_memcpy(trans_msg.pld.pld_ptr,&rcvbuff[9], ByteRcv);
                    trans_msg.Msg_len = ByteRcv- 9;
                    os_itc_msg_send(&trans_msg, TC_TM_MANAGER_IPC_RECEIVE, os_send_itc_wait);

                    if((!sband_sch_gs_contact)&&(!uhf_sch_gs_contact))
                    {
                        sdr_gs_contact_status_upd_itc();
                        os_timer_start(uhf_tx_tm_on,uhf_tmr_cfg.sdr_tm_tx_tmr);
                        os_timer_start(sband_tx_tm_on,sband_tmr_cfg.sdr_tm_tx_tmr);
                        DEBUG_CPRINT(("sband_tx_tm_on timer at csp comms %d", sband_tmr_cfg.sdr_tm_tx_tmr));
                        log_id_with_one_arg(LOG_INFO_L1,LOG_COMMS,SBAND_TX_TM_ON_TIMER_AT_CSP_COMMS__u8_,sband_tmr_cfg.sdr_tm_tx_tmr);
                    }
                    os_timer_start(sband_beacon_enb_timer, sband_tmr_cfg.beacon_enb_tmr);
                }

            }
        }

#ifdef COMMS_TIME_DEBUG
        get_crnt_time_date(&tim_get_dev);
        DEBUG_CPRINT(("\nIQS TIME RX EXT %d:%d:%d",tim_get_dev.hour, tim_get_dev.minute, tim_get_dev.seconds));
        log_id_with_three_arg(LOG_INFO_L2,LOG_COMMS,IQS_TIME_RX_EXT__u8__u8__u8_,tim_get_dev.hour, tim_get_dev.minute, tim_get_dev.seconds);
#endif
    }// end while

    close(sock1);
    DEBUG_CPRINT(("\nDone."));

#ifdef LINUX_TEMP_PORT
    sleep(1);
#else
    os_delay(1000);
#endif

    return 0;
}

/**
 * @brief Receives data over S-Band Xlink.
 * 
 * @param[out] rcv_pkt Pointer to buffer for received data.
 * @param[in] ret_byt_cnt Number of bytes to receive.
 * 
 * @return  rcv_cnt - Number of bytes received.
 * 
 * This function receives specified number of bytes from the S-Band Xlink socket.
*/
uint32_t sband_xlink_rx(uint8_t* rcv_pkt, uint32 ret_byt_cnt)
{
    uint32 tail_seq_data1	= 0x1F1F1F1F;
    uint32 tail_seq_data2	= 0x1F1F1F1F;
    uint16 start_seq		= 0xAFAE;
    uint8 	err_cnt_byt		= 0x24;
    uint32 pkt_len		= ret_byt_cnt;
    int i 					= 1;
    int j 					= 0;
    uint32 data_len 		= 0;
    uint8 act_data[RX_BUFF_SIZE] = {0};
    uint32 rcv_flag 		= 0;
    uint32 rcv_cnt		= 0;

    RX_UDP_DATA_STRUCT *rcv_bch_frame = (RX_UDP_DATA_STRUCT*) rcv_pkt;


    DEBUG_CPRINT(("\n\rIQS RX_RCV_DATA: "));
    for (int i=0;i<ret_byt_cnt;i++)
    {
        DEBUG_CPRINT(("%x\t",(uint8_t)rcv_pkt[i]));
        log_id_with_one_arg(LOG_INFO_L2,LOG_COMMS,RCV_DATA__hex_,(uint8_t)rcv_pkt[i]);
    }

    DEBUG_CPRINT(("\n"));

    if ((rcv_bch_frame->start_seq == start_seq) && (rcv_bch_frame->tail_seq[0] == tail_seq_data1) && (rcv_bch_frame->tail_seq[1] == tail_seq_data2))
    {
        if (pkt_len  == RX_BUFF_SIZE)
        {
            data_len  = RX_BUFF_SIZE;
        }
        else
        {
            data_len = pkt_len;
        }

        while(i < data_len)
        {
            if ((i && j) && ((i % 8) == 0))
            {
                i++;
            }
            else
            {
                act_data[j] = rcv_bch_frame->data[i-1];
                i++;
                j++;
            }
        }
        memcpy(&iqs_rcv_buff[0], act_data, data_len);
        rcv_cnt = 0x80000000 | data_len;
    }
    else if((rcv_bch_frame->start_seq != start_seq) && (pkt_len == RX_BUFF_SIZE))
    {
        if((rcv_bch_frame->tail_seq[0] == tail_seq_data1) && (rcv_bch_frame->tail_seq[1] == tail_seq_data2))
        {
            data_len = pkt_len - 8;
            memmove(rcv_bch_frame->data, rcv_pkt, data_len-1);
            rcv_flag = 1;
        }
        else if(rcv_bch_frame->tail_seq[1] == tail_seq_data1)
        {
            data_len = pkt_len - 4;
            memmove(rcv_bch_frame->data, rcv_pkt, data_len);
            rcv_flag = 0;
        }
        while(i < data_len)
        {
            if (rcv_bch_frame->data[i-1] == err_cnt_byt)
            {
                i++;
            }
            else
            {
                act_data[j] = rcv_bch_frame->data[i-1];
                i++;
                j++;
            }

        }
        os_memcpy(&iqs_rcv_buff[RX_BUFF_SIZE], &act_data[0], data_len);

        if (rcv_flag == 1)
        {
            rcv_cnt = 0x80000000 | data_len;
        }
        else
        {
            rcv_cnt = data_len;
        }
    }
    else if((rcv_bch_frame->start_seq == start_seq) && (pkt_len == RX_BUFF_SIZE))
    {
        if((rcv_bch_frame->tail_seq[0] != tail_seq_data1) && (rcv_bch_frame->tail_seq[1] != tail_seq_data2))
        {
            data_len = pkt_len - 2;
            memmove(rcv_bch_frame->data, &rcv_pkt[2], data_len);
            rcv_flag = 0;
        }
        else if(rcv_bch_frame->tail_seq[1] != tail_seq_data1)
        {
            data_len = pkt_len - 6;
            memmove(rcv_bch_frame->data, &rcv_pkt[2], data_len);
            rcv_flag = 1;
        }

        while(i < data_len)
        {
            if (rcv_bch_frame->data[i-1] == err_cnt_byt)
            {
                i++;
            }
            else
            {
                act_data[j] = rcv_bch_frame->data[i-1];
                i++;
                j++;
            }
        }
        memcpy(&iqs_rcv_buff[0], act_data, data_len);
        if (rcv_flag == 1)
        {
            rcv_cnt = 0x80000000 | data_len;
        }
        else
        {
            rcv_cnt = data_len;
        }
    }
    else if((rcv_bch_frame->start_seq != start_seq) && (pkt_len < RX_BUFF_SIZE))
    {
        data_len = pkt_len - 8;

        while(i < data_len)
        {
            if (rcv_bch_frame->data[i-1] == err_cnt_byt)
            {
                i++;
            }
            else
            {
                act_data[j] = rcv_bch_frame->data[i-1];
                i++;
                j++;
            }
        }
        memcpy(&iqs_rcv_buff[RX_BUFF_SIZE], act_data, data_len);
        rcv_cnt = 0x80000000 | data_len;
    }
    else if((rcv_bch_frame->start_seq == start_seq) && (pkt_len < RX_BUFF_SIZE))
    {
        data_len = pkt_len - 8;
        while(i < data_len)
        {
            if ((i & j) && ((i % 8) == 0))
            {
                i++;
            }
            else
            {
                act_data[j] = rcv_bch_frame->data[i-1];
                i++;
                j++;
            }
        }
        memcpy(&iqs_rcv_buff[0], act_data, data_len);

        rcv_cnt = 0x80000000 | data_len;
    }

    // Need to discuss
#ifdef IQS_NORMAL
    data_len = pkt_len;
    memmove(rcv_bch_frame->data, rcv_pkt, pkt_len);
    rcv_flag = 2;
#endif

    DEBUG_CPRINT(("\nRX after process data: "));

    for (int i=0;i<data_len;i++)
    {
        DEBUG_CPRINT(("%x\t",(uint8_t)iqs_rcv_buff[i]));
        log_id_with_one_arg(LOG_INFO_L2,LOG_COMMS,RX_AFTER_PROCESS_DATA__hex_,(uint8_t)iqs_rcv_buff[i]);
    }

    return rcv_cnt;
}


/**
 * @brief Transmits data over S-Band Xlink.
 *
 * @param[in] data Pointer to data buffer to transmit. 
 * @param[in] TrfLen Length of data to transmit in bytes.
 * @param[in] PktCnt Number of packets to transmit.
 *
 * This function transmits the specified data buffer over the S-Band Xlink
 * interface, segmented into the given number of packets.
*/  
void sband_xlink_tx(uint8_t *data, uint32 TrfLen, uint32 PktCnt)
{
    uint32 sock;
    volatile uint32 PktSent = PktCnt;
    uint32 PktMiss = 0;
    uint64_t ByteXmt = 0;
    uint64_t ByteRcv = 0;
    uint8_t rcvbuf[BUFFERSIZE];
    uint32 Pkt_Len = 1460;
    uint32 data_sent = 0;
    uint32 data_len_offset = 0;
    uint8 loop_break = 0;
    uint32 iqs_pkt_sz = sband_serv_device_conf.XmtCtr3.TrFrmSz;
    uint8 sband_soc_conn_fail = 0;
    int ret =0;

    if(!iqs_pkt_sz)
    {
        sband_serv_device_conf.XmtCtr3.TrFrmSz = 1432;
        iqs_pkt_sz = sband_serv_device_conf.XmtCtr3.TrFrmSz;
        DEBUG_CPRINT(("Packet size is %d",sband_serv_device_conf.XmtCtr3.TrFrmSz));
        log_id_with_one_arg(LOG_INFO_L1,LOG_COMMS,PACKET_SIZE__u32_,sband_serv_device_conf.XmtCtr3.TrFrmSz);
    }
#ifdef COMMS_TIME_DEBUG
    get_crnt_time_date(&tim_get_dev);
    DEBUG_CPRINT(("\nIQS TIME UDP CLI_CON %d:%d:%d",tim_get_dev.hour, tim_get_dev.minute, tim_get_dev.seconds));
    log_id_with_three_arg(LOG_INFO_L2,LOG_COMMS,IQS_TIME_UDP_CLI_CON__u8__u8__u8_,tim_get_dev.hour, tim_get_dev.minute, tim_get_dev.seconds);
#endif
    sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (sock < 0) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat"
        DEBUG_CPRINT(("\nsocket creation failed: %ld", sock));
        log_id_with_one_arg(LOG_INFO_L1,LOG_COMMS,SOCKET_CREATION_FAILED__u8_,sock);
#pragma GCC diagnostic pop
    }

    struct sockaddr_in sad;
    memset(&sad, 0, sizeof(sad));

#ifdef LINUX_TEMP_PORT
    sad.sin_family = AF_INET;
    sad.sin_addr.s_addr = inet_addr(obc_iqs_sim_soc_inf.ip_addr);
    sad.sin_port = htons((obc_iqs_sim_soc_inf.port));
#else
    ip4_addr_t sband_xlink_ipaddr;
    IP4_ADDR(&sband_xlink_ipaddr, 192, 168, 1, 1);
    sad.sin_family = AF_INET;
    sad.sin_addr.s_addr = sband_xlink_ipaddr.addr;
    sad.sin_port = htons(51003);
#endif
DO_AGAIN:
    if (connect(sock, (struct sockaddr *) &sad, sizeof(sad)) < 0) {
        err_detected(ERR_GROUP_COMMS, ERR_COMMS_SBAND_UDP_TX_CONNECTION_FAIL);
        sband_soc_conn_fail++;
        DEBUG_CPRINT(("\nfailed to connect"));
        dbg_iqs_fail[6] = (dbg_iqs_fail[6] + 1) & 0xFFFFFFFF;
        log_id(LOG_ERROR,LOG_COMMS,FAILD_TO_CONNECT);
        close(sock);
        os_delay(1000);
        if(sband_soc_conn_fail>2)
        {
            return;
        }
        goto DO_AGAIN;
    }

    SERVICE_RF_XMT_DATA_STRUCT *rcv_frame = (SERVICE_RF_XMT_DATA_STRUCT*)rcvbuf;
    SERVICE_RF_XMT_DATA_STRUCT frame;
    bzero(&frame,sizeof(SERVICE_RF_XMT_DATA_STRUCT));
    memcpy(&frame, sband_lastProductKey, 16);
    frame.service_event = SERVICE_SET | SERVICE_RF_XMT_DATA;
    frame.PktType = PKT_TRANSFER_FRM;
    frame.PktLen = iqs_pkt_sz; //IQS_MTU_SIZE;
    frame.MoreInfo = 0;
    uint32_t csp_hdr =0x96c57c00;
    frame.csp_header = csp_ntoh32(csp_hdr);

    //*** Updated for loop to be continue for more data when received***/
    //do{
    while((data_sent < TrfLen) && (loop_break == 0))
    {
        if ((TrfLen - data_sent) > (iqs_pkt_sz - sizeof(frame.csp_header))) 
        {
            data_len_offset = iqs_pkt_sz - sizeof(frame.csp_header);
        }
        else {
            data_len_offset = TrfLen - data_sent;
        }
        memcpy(frame.Payload, &data[data_sent], data_len_offset);


        DEBUG_CPRINT(("The Packet_Len of XMT data pktLen is %d, trfLen is %d, Data_Off_SET is %d & %d ", Pkt_Len, TrfLen, data_len_offset, PktSent));
        log_id_with_four_arg(LOG_INFO_L1,LOG_COMMS,XMT_DATA_PKTLEN__u8__TRF_LEN__u8__DATA__OFFSET__u8__PKT_SENT_u8__,Pkt_Len, TrfLen, data_len_offset, PktSent);

        get_rtc_time(&get_t_iqs);
        DEBUG_CPRINT(("\nIQS RTC Time-%d:%d:%d\n",get_t_iqs.hour, get_t_iqs.min, get_t_iqs.sec));
        log_id_with_three_arg(LOG_INFO_L2,LOG_COMMS,IQS_RTC_TIME__u8__u8__u8_,get_t_iqs.hour, get_t_iqs.min, get_t_iqs.sec);
        DEBUG_CPRINT(("\n\rIQS TX Data: "));
        /*		uint8_t *tx_d = (uint8_t*)&frame;
                        for (int i=0; i<(data_len_offset + 28); i++)
                        {
        //data[i] = i;
        DEBUG_CPRINT(("%x ;", tx_d[i]));
        } */
        DEBUG_CPRINT(("\n"));

        DEBUG_CPRINT(("\nThe Packet_Len of XMT data pktLen is %d, trfLen is %d, Pkt sent %d datasent %d", Pkt_Len, TrfLen, PktSent,  data_sent));
        log_id_with_four_arg(LOG_INFO_L1,LOG_COMMS,XMT_DATA_PKTLEN__u8__TRF_LEN__u8__DATA__OFFSET__u8__PKT_SENT_u8__,Pkt_Len, TrfLen, data_len_offset, PktSent);

        frame.MoreInfo = PktCnt - PktSent; // SERVICE_XMT_MAGIC on tcp  - packet couter to identify missing packet

        os_delay(100);

        if (send(sock, (uint8_t*)&frame, Pkt_Len, 0) != Pkt_Len) {
            err_detected(ERR_GROUP_COMMS, ERR_COMMS_SBAND_SEND_UDP_TX_FAIL);
            DEBUG_CPRINT(("\n SBAND socket send error"));
            dbg_iqs_fail[7] = (dbg_iqs_fail[7] + 1) & 0xFFFFFFFF;
            loop_break = 1;
            break;
        } else {
            tx_udp_frame++;
            DEBUG_CPRINT(("\n SBAND Tx Frame Count is %d", tx_udp_frame));
            ByteXmt += data_len_offset;
        }

#ifdef COMMS_TIME_DEBUG 
        get_crnt_time_date(&tim_get_dev);
        DEBUG_CPRINT(("\nIQS TIME TX SENT %d:%d:%d",tim_get_dev.hour, tim_get_dev.minute, tim_get_dev.seconds));
        log_id_with_three_arg(LOG_INFO_L2,LOG_COMMS,IQS_TIME_TX_SENT__u8__u8__u8_,tim_get_dev.hour, tim_get_dev.minute, tim_get_dev.seconds);
#endif

        DEBUG_CPRINT(("\n\rIQS TX_SND_: "));
        log_id(LOG_INFO_L2,LOG_COMMS,IQS_TX_SEND);

        bzero(rcvbuf,sizeof(rcvbuf));
        ret = 0;
#if 0
        while(start_tm == end_tm)
        {
            start_tm = time(NULL);
            os_delay(400);

        }
#endif
        ret = recvfrom(sock, rcvbuf, 28, MSG_DONTWAIT, NULL, NULL); //for testing purpose MSG_WAITALL


        DEBUG_CPRINT(("\n\rIQS TX_ACK_: "));
        if( ret <= 0) {

            err_detected(ERR_GROUP_COMMS, ERR_COMMS_SBAND_READ_UDP_TX_FAIL);
            DEBUG_CPRINT(("\n\n socket recv error"));
            log_id(LOG_ERROR,LOG_COMMS,SOCKET_RECV_ERROR);
            dbg_iqs_fail[8] = (dbg_iqs_fail[8] + 1) & 0xFFFFFFFF;
            break;
        }
        else
        {

            DEBUG_CPRINT(("\nRX response data: "));
            for (int i=0;i<ret;i++)
            {
                DEBUG_CPRINT(("%x\t",(uint8_t)rcvbuf[i]));
                log_id_with_one_arg(LOG_INFO_L2,LOG_COMMS,RX_RESPONSE_DATA__u8_,(uint8_t)rcvbuf[i]);
            }
        }
        DEBUG_CPRINT(("\n"));

        switch(rcv_frame->service_event) {
            case (SERVICE_RF_XMT_DATA | SERVICE_SET | SERVICE_ACK):
                ByteRcv += rcv_frame->PktLen;
                data_sent += data_len_offset;

                if (data_sent == TrfLen)
                {
                    loop_break = 1;
                }
                DEBUG_CPRINT(("The Recv PktLen is %x\n",rcv_frame->PktLen));
                log_id_with_one_arg(LOG_INFO_L2,LOG_COMMS,RECV_PKTLEN__u8_,rcv_frame->PktLen);
                break;
            case (SERVICE_RF_XMT_DATA | SERVICE_SET | SERVICE_NAK):
                {
                    if(rcv_frame->ErrInf == RF_XMT_PKT_DROP) { //ERR_NOTI_PKT_DROP
                        err_detected(ERR_GROUP_COMMS, ERR_COMMS_PKT_DROP);
                        if ((loop_break == 0) && (data_sent != TrfLen))
                        {
                            PktSent++;
                            DEBUG_CPRINT(("The Recv error is %x\n",rcv_frame->ErrInf));
                            log_id_with_one_arg(LOG_INFO_L1,LOG_COMMS,RECV_ERROR__u8_,rcv_frame->ErrInf);
                            DEBUG_CPRINT(("The Recv error cnt is %x\n",rcv_frame->MoreInfo));
                            log_id_with_one_arg(LOG_INFO_L1,LOG_COMMS,RECV_ERROR_CNT__u8_,rcv_frame->MoreInfo);
                        }
                        else if(PktSent == 10)
                        {
                            PktSent = 0;
                        }

                        if(PktMiss != rcv_frame->MoreInfo) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat"
                            DEBUG_CPRINT(("\nPktDrop error: %ld", PktMiss));
                            log_id_with_one_arg(LOG_INFO_L1,LOG_COMMS,PKT_DROP_ERROR__u8_,PktMiss);
#pragma GCC diagnostic pop
                        }
                    } else if (rcv_frame->ErrInf == RF_XMT_FAIL_LEN)//ERR_NOTI_PKTLEN_NOT_MATCH
                    {
                        err_detected(ERR_GROUP_COMMS, ERR_COMMS_PKT_LEN_MISMATCH);
                        DEBUG_CPRINT(("\nXMT_DATA NAK received, please check PktLen not match with TrFrmSz"));
                        log_id(LOG_INFO_L1,LOG_COMMS,XMT_DATA_NAK_RECEIVED_CHECK_PKTLEN_NOT_MATCH_WITH_TRFRMSZ);

                        loop_break = 1;
                    }
                    else //RF_RCV_CRC_ERROR
                    {
                        DEBUG_CPRINT(("The Recv error is %x\n",rcv_frame->ErrInf));
                        log_id_with_one_arg(LOG_INFO_L2,LOG_COMMS,RECV_ERROR__hex_,rcv_frame->ErrInf);
                        DEBUG_CPRINT(("\nXMT_DATA NAK received, please check prod-key"));
                        log_id(LOG_INFO_L1,LOG_COMMS,XMT_DATA_NAK_RECEIVED_CHECK_PROD_KEY);
                        loop_break = 1;
                    }
                }
                break;
            case (SERVICE_RF_XMT_DATA | SERVICE_SET |SERVICE_XMT_DATA_STOP|SERVICE_BUSY)://ERR_NOTI_SBAND_BUSY
                {
                    err_detected(ERR_GROUP_COMMS, ERR_COMMS_SBAND_BUSY);
                    DEBUG_CPRINT(("The Recv error is %x\n",rcv_frame->ErrInf));
                    log_id_with_one_arg(LOG_INFO_L2,LOG_COMMS,RECV_ERROR__hex_,rcv_frame->ErrInf);
                }
                loop_break = 1;
                break;

        }

        if (rcv_frame->ErrInf == RF_XMT_FIFO_FULL)
        {
            loop_break = 1;
        }
    }

    shutdown(sock, 1); //commented to check
    close(sock);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat"
    DEBUG_CPRINT(("\nPktSent: %i Xmt/Rcv: %ld/%ld", PktSent, ByteXmt, ByteRcv));
    log_id_with_three_arg(LOG_INFO_L2,LOG_COMMS,PKT_SENT__u8__BYTEXMT__u8__BYTERCV__u8_,PktSent, ByteXmt, ByteRcv);
#pragma GCC diagnostic pop
    DEBUG_CPRINT(("\nDone."));
}

uint32_t xlink_get_udp_ip_addr(void)
{
#ifdef LINUX_TEMP_PORT
    return (inet_addr(obc_iqs_sim_soc1_inf.ip_addr));
#else
    ip4_addr_t eth_ipaddr;
    IP4_ADDR(&eth_ipaddr, ETH_IP_ADDR3, ETH_IP_ADDR2, ETH_IP_ADDR1, ETH_IP_ADDR0);

    return (uint32_t)eth_ipaddr.addr; 
#endif
}

uint32_t xlink_get_udp_temp_ip_addr(uint8_t *ip)
{
    char tmp[16];

    os_memcpy(&tmp,ip,sizeof(tmp));

    return (inet_addr(tmp));
}

