#include "sr_netdev_conn.h"
//#include "os_task.h"

#include <stdlib.h>
//#include <os_stddef.h>

//#define MO_LOG_TAG "molink.netconn"
//#define MO_LOG_LVL MO_LOG_INFO
#include "sr_log.h"
#include "sr_osal.h"

#ifdef MOLINK_USING_NETCONN_OPS

#if  0
static mo_netconn_ops_t *get_netconn_ops(SR_NETDEV_T *module)
{
    mo_netconn_ops_t *ops = (mo_netconn_ops_t *)module->ops_table[MODULE_OPS_NETCONN];

    if (OS_NULL == ops)
    {
        SR_LOG(ERR,"Module %s does not support general operates", module->name);
    }

    return ops;
}
#endif

void mo_wifi_netconn_data_recv_notice(mo_netconn_t *netconn, ip_addr_t addr, uint16_t port, char *data, os_size_t size)
{
    OS_ASSERT(OS_NULL != netconn);
    OS_ASSERT(OS_NULL != data);

    if (netconn->stat == NETCONN_STAT_CONNECT)
    {
        mo_netconn_msg_t msg = {0};

        msg.addr     = addr;
        msg.port     = port;
        msg.data     = data;
        msg.data_len = size;

#ifdef MOLINK_USING_SOCKETS_OPS
        if (OS_NULL != netconn->data_func)
        {
            netconn->data_func(OS_NULL, addr, port, data, size);
            SR_OsFree(data);
        }
        else
        {
            //os_msgqueue_send(netconn->mq_id, (void *)&msg, sizeof(mo_netconn_msg_t), OS_WAIT_FOREVER);
            osMessageQueuePut(netconn->mq_id, (void *)&msg, 0, osWaitForever);
            if (OS_NULL != netconn->evt_func)
            {
                netconn->evt_func(netconn, MO_NETCONN_EVT_RCVPLUS, size);
            }
        }
#else
        //os_msgqueue_send(netconn->mq_id, (void *)&msg, sizeof(mo_netconn_msg_t), OS_WAIT_FOREVER);       
	    osMessageQueuePut(netconn->mq_id, (void *)&msg, 0, osWaitForever);
#endif
    }
    else
    {
        SR_OsFree(data);
        SR_LOG(ERR,"Netconn id %d receive state[%d] error", netconn->connect_id, netconn->stat);
    }

    return;
}

void mo_netconn_data_recv_notice(mo_netconn_t *netconn, char *data, os_size_t size)
{
    OS_ASSERT(OS_NULL != netconn);
    OS_ASSERT(OS_NULL != data);
    int result = 0;

    if (netconn->stat == NETCONN_STAT_CONNECT)
    {
        mo_netconn_msg_t msg = {0};

        msg.data     = data;
        msg.data_len = size;

        //result = os_msgqueue_send(netconn->mq_id, (void *)&msg, sizeof(mo_netconn_msg_t), OS_WAIT_FOREVER);        
		result = osMessageQueuePut(netconn->mq_id, (void *)&msg, 0, osWaitForever);
		
        //if (OS_SUCCESS != result)
        if (SR_OK != result)
        {
            SR_LOG(ERR,"%s: Netconn id %d mq send error", __func__, netconn->connect_id);
        }

#ifdef MOLINK_USING_SOCKETS_OPS
        if (OS_NULL != netconn->evt_func)
        {
            //if (OS_SUCCESS == result)
            if (SR_OK == result)
            {
                netconn->evt_func(netconn, MO_NETCONN_EVT_RCVPLUS, size);
            }
            else
            {
                netconn->evt_func(netconn, MO_NETCONN_EVT_ERROR, 0);
            }
        }
#endif
    }
    else
    {
        SR_OsFree(data);
        SR_LOG(ERR,"Netconn id %d receive state error", netconn->connect_id);
    }

    return;
}

/**
 * tcp/udp_close urc recv, which means socket closed by peer or something exception happend
 */
void mo_netconn_pasv_close_notice(mo_netconn_t *netconn)
{
    OS_ASSERT(OS_NULL != netconn);

    if (netconn->stat != NETCONN_STAT_NULL)
    {
        netconn->stat = NETCONN_STAT_CLOSED_WAIT;

#ifdef MOLINK_USING_SOCKETS_OPS
        if (OS_NULL != netconn->evt_func)
        {
            // when socket closed, notice recv & error event
            netconn->evt_func(netconn, MO_NETCONN_EVT_RCVPLUS, 0);
            netconn->evt_func(netconn, MO_NETCONN_EVT_ERROR, 0);
        }
#endif

        /* TODO REVIEW */
        // os_data_queue_reset(&netconn->data_queue);
    }

    return;
}

os_err_t mo_netconn_get_info(SR_NETDEV_T *module, mo_netconn_info_t *info)
{
    OS_ASSERT(OS_NULL != module);
    OS_ASSERT(OS_NULL != info);

   // mo_netconn_ops_t *ops = get_netconn_ops(module);
   
	mo_netconn_ops_t *ops = (mo_netconn_ops_t *)module->netconn;

    if (OS_NULL == ops)
    {
        return OS_SUCCESS;
    }

    if (OS_NULL == ops->get_info)
    {
        SR_LOG(ERR,"Module %s does not support get netconn infomation operate", module->name);
        return OS_FAILURE;
    }

    return ops->get_info(module, info);
}

/**
 ***********************************************************************************************************************
 * @brief           Releases legacy data items in the queue and deinit the queue
 *
 * @param[in]       mq_id       The descriptor of message queue
 ***********************************************************************************************************************
 */
//void mo_netconn_mq_destroy(os_msgqueue_id mq_id)
void mo_netconn_mq_destroy(void *mq_id)
{
    OS_ASSERT(0 != mq_id);

    //os_err_t  result    = OS_SUCCESS;
    int  result    = SR_OK;
    os_size_t data_size = 0;

    mo_netconn_msg_t msg = {0};
    do
    {
        //result = os_msgqueue_recv(mq_id, (void *)&msg, sizeof(mo_netconn_msg_t), OS_NO_WAIT, &data_size);
		result = osMessageQueueGet(mq_id, (void *)&msg, 0, 0);
		
        //if (result != OS_SUCCESS)
		if (result != SR_OK)
        {
            /* All items in the queue have been released */
            break;
        }

        if (OS_NULL != msg.data)
        {
            SR_OsFree(msg.data);
            msg.data = OS_NULL;
        }

    } while (1);

    //os_msgqueue_destroy(mq_id);
    osMessageQueueDelete(mq_id);
}

/**
 ***********************************************************************************************************************
 * @brief           Create an instance of a molink module netconn object
 *
 * @param[in]       module          The descriptor of molink module instance
 * @param[in]       type            The type of molink module netconn object. @ref mo_netconn_type_t
 *
 * @return          On success, return a molink module instance netconn descriptor;
 *                  On error, OS_NULL is returned.
 ***********************************************************************************************************************
 */
mo_netconn_t *mo_netconn_create(SR_NETDEV_T *module, mo_netconn_type_t type)
{
    OS_ASSERT(OS_NULL != module);
    OS_ASSERT(NETCONN_TYPE_NULL != type);

    //mo_netconn_ops_t *ops = get_netconn_ops(module);  
	mo_netconn_ops_t *ops = (mo_netconn_ops_t *)module->netconn;

    if (OS_NULL == ops)
    {
        return OS_NULL;
    }

    if (OS_NULL == ops->create)
    {
        SR_LOG(ERR,"Module %s does not support create netconn operate", module->name);
        return OS_NULL;
    }

    return ops->create(module, type);
}

/**
 ***********************************************************************************************************************
 * @brief           Destroy an instance of a molink module netconn object
 *
 * @param[in]       module          The descriptor of molink module instance
 * @param[in]       netconn         The descriptor of molink module netconn instance
 *
 * @return          Returns the result of the operation
 * @retval          OS_FAILURE      Destroy failed
 * @retval          OS_SUCCESS      Destroy successfully
 ***********************************************************************************************************************
 */
os_err_t mo_netconn_destroy(SR_NETDEV_T *module, mo_netconn_t *netconn)
{
    OS_ASSERT(OS_NULL != module);
    OS_ASSERT(OS_NULL != netconn);

    if (NETCONN_STAT_NULL == netconn->stat)
    {
        SR_LOG(ERR,"Module %s netconn id %d connect state %d error!", module->name, netconn->connect_id, netconn->stat);
        return OS_FAILURE;
    }

    //mo_netconn_ops_t *ops = get_netconn_ops(module); 
	mo_netconn_ops_t *ops = (mo_netconn_ops_t *)module->netconn;

    if (OS_NULL == ops)
    {
        return OS_FAILURE;
    }

    if (OS_NULL == ops->destroy)
    {
        SR_LOG(ERR,"Module %s does not support destroy netconn operate", module->name);
        return OS_FAILURE;
    }

    return ops->destroy(module, netconn);
}

/**
 ***********************************************************************************************************************
 * @brief           Get IP address by domain name
 *
 * @param[in]       module          The descriptor of molink module instance
 * @param[in]       domain_name     The domain name
 * @param[out]      addr            The to store ip address
 *
 * @return          Returns the result of the operation
 * @retval          OS_FAILURE      Get IP address failed
 * @retval          OS_TIMEOUT      Get IP address timeout
 * @retval          OS_SUCCESS      Get IP address successfully
 ***********************************************************************************************************************
 */
os_err_t mo_netconn_gethostbyname(SR_NETDEV_T *module, const char *domain_name, ip_addr_t *addr)
{
    OS_ASSERT(OS_NULL != module);
    OS_ASSERT(OS_NULL != addr);
    OS_ASSERT(OS_NULL != domain_name);

    //mo_netconn_ops_t *ops = get_netconn_ops(module);   
	mo_netconn_ops_t *ops = (mo_netconn_ops_t *)module->netconn;

    if (OS_NULL == ops)
    {
        return OS_FAILURE;
    }

    if (inet_addr(domain_name) != IPADDR_NONE)
    {
        SR_LOG(DEBUG,"Resolve input is valid IP address, no need to resolve.\n");
        inet_aton(domain_name, addr);
        return OS_SUCCESS;
    }

    if (OS_NULL == ops->gethostbyname)
    {
        SR_LOG(ERR,"Module %s does not support gethostbyname operate", module->name);
        return OS_FAILURE;
    }

    return ops->gethostbyname(module, domain_name, addr);
}

/**
 ***********************************************************************************************************************
 * @brief           bind a molink module netconn to a specific remote IP address and port
 *
 * @param[in]       module          The descriptor of molink module instance
 * @param[in]       netconn         The descriptor of molink module netconn instance
 * @param[in]       addr            The remote IP address to bind to
 * @param[in]       port            The remote port to bind to
 *
 * @return          Returns the result of the operation
 * @retval          OS_FAILURE      bind failed
 * @retval          OS_TIMEOUT      bind timeout
 * @retval          OS_SUCCESS      bind successfully
 ***********************************************************************************************************************
 */
os_err_t mo_netconn_bind(SR_NETDEV_T *module, mo_netconn_t *netconn, ip_addr_t addr, uint16_t port)
{
    OS_ASSERT(OS_NULL != module);
    OS_ASSERT(OS_NULL != netconn);

    if (netconn->stat != NETCONN_STAT_INIT)
    {
        SR_LOG(ERR,"Module %s netconn id %d bind state %d error!", module->name, netconn->connect_id, netconn->stat);
        return OS_FAILURE;
    }

    //mo_netconn_ops_t *ops = get_netconn_ops(module);  
	mo_netconn_ops_t *ops = (mo_netconn_ops_t *)module->netconn;

    if (OS_NULL == ops)
    {
        return OS_FAILURE;
    }

    if (OS_NULL == ops->bind)
    {
        SR_LOG(ERR,"Module %s does not support bind operate", module->name);
        return OS_FAILURE;
    }

    return ops->bind(module, netconn, addr, port);
}

/**
 ***********************************************************************************************************************
 * @brief           Connect a molink module netconn to a specific remote IP address and port
 *
 * @param[in]       module          The descriptor of molink module instance
 * @param[in]       netconn         The descriptor of molink module netconn instance
 * @param[in]       addr            The remote IP address to connect to
 * @param[in]       port            The remote port to connect to
 *
 * @return          Returns the result of the operation
 * @retval          OS_FAILURE      Connect failed
 * @retval          OS_TIMEOUT      Connect timeout
 * @retval          OS_SUCCESS      Connect successfully
 ***********************************************************************************************************************
 */
os_err_t mo_netconn_connect(SR_NETDEV_T *module, mo_netconn_t *netconn, ip_addr_t addr, uint16_t port)
{
    OS_ASSERT(OS_NULL != module);
    OS_ASSERT(OS_NULL != netconn);

    if (netconn->stat != NETCONN_STAT_INIT)
    {
        SR_LOG(ERR,"Module %s netconn id %d connect state %d error!", module->name, netconn->connect_id, netconn->stat);
        return OS_FAILURE;
    }

    //mo_netconn_ops_t *ops = get_netconn_ops(module);   
	mo_netconn_ops_t *ops = (mo_netconn_ops_t *)module->netconn;

    if (OS_NULL == ops)
    {
        return OS_FAILURE;
    }

    if (OS_NULL == ops->connect)
    {
        SR_LOG(ERR,"Module %s does not support connect operate", module->name);
        return OS_FAILURE;
    }

    return ops->connect(module, netconn, addr, port);
}

/**
 ***********************************************************************************************************************
 * @brief           Send data over a TCP or UDP molink module netconn
 *
 * @param[in]       module          The descriptor of molink module instance
 * @param[in]       netconn         The descriptor of molink module netconn instance
 * @param[in]       data            The buffer containing the data to send
 * @param[in]       size            The size of the data to send
 *
 * @return          The length of data successfully sent
 ***********************************************************************************************************************
 */
os_size_t mo_netconn_sendto(SR_NETDEV_T       *module, mo_netconn_t *netconn,ip_addr_t remote_ip,uint16_t remote_port,const char *data,os_size_t size)
{
    OS_ASSERT(OS_NULL != module);
    OS_ASSERT(OS_NULL != netconn);
    OS_ASSERT(OS_NULL != data);

    if (netconn->stat != NETCONN_STAT_CONNECT)
    {
        SR_LOG(ERR,"Module %s netconn %d does not connect to server, send failed!", module->name, netconn->connect_id);
        return 0;
    }

    //mo_netconn_ops_t *ops = get_netconn_ops(module);
	mo_netconn_ops_t *ops = (mo_netconn_ops_t *)module->netconn;

    if (OS_NULL == ops)
    {
        return 0;
    }

    switch (netconn->type)
    {
    case NETCONN_TYPE_TCP:
    case NETCONN_TYPE_TCP_V6:
    {
        if (OS_NULL == ops->send)
        {
            SR_LOG(ERR,"Module %s does not support send operate", module->name);
            break;
        }

        return ops->send(module, netconn, (char *)data, size);
    }
    case NETCONN_TYPE_UDP:
    case NETCONN_TYPE_UDP_V6:
    {
        if (OS_NULL != ops->sendto)
        {
            return ops->sendto(module, netconn, remote_ip, remote_port, (char *)data, size);
        }

        /* For modules that dosen't support sendto. */
        if (OS_NULL != ops->send)
        {
            return ops->send(module, netconn, (char *)data, size);
        }

        SR_LOG(ERR,"Module %s does not support send/sendto operate", module->name);
        break;
    }
    default:
    {
        SR_LOG(ERR,"Module %s does not support netconn type:%d", module->name, netconn->type);
        break;
    }
    }

    return 0;
}

/**
 ***********************************************************************************************************************
 * @brief           Send data over a TCP or UDP molink module netconn (that is already connected)
 *
 * @param[in]       module          The descriptor of molink module instance
 * @param[in]       netconn         The descriptor of molink module netconn instance
 * @param[in]       data            The buffer containing the data to send
 * @param[in]       size            The size of the data to send
 *
 * @return          The length of data successfully sent
 ***********************************************************************************************************************
 */
os_size_t mo_netconn_send(SR_NETDEV_T *module, mo_netconn_t *netconn, const char *data, os_size_t size)
{
    OS_ASSERT(OS_NULL != module);
    OS_ASSERT(OS_NULL != netconn);
    OS_ASSERT(OS_NULL != data);

    if (netconn->stat != NETCONN_STAT_CONNECT)
    {
        SR_LOG(ERR,"Module %s netconn %d does not connect to server, send failed!", module->name, netconn->connect_id);
        return 0;
    }

    //mo_netconn_ops_t *ops = get_netconn_ops(module); 
	mo_netconn_ops_t *ops = (mo_netconn_ops_t *)module->netconn;

    if (OS_NULL == ops)
    {
        return 0;
    }

    if (OS_NULL == ops->send)
    {
        SR_LOG(ERR,"Module %s does not support send operate", module->name);
        return 0;
    }

    return ops->send(module, netconn, (char *)data, size);
}

/**
 ***********************************************************************************************************************
 * @brief           Receive data from a molink module netconn
 *
 * @param[in]       module          The descriptor of molink module instance
 * @param[in]       netconn         The descriptor of molink module netconn instance
 * @param[out]      data            The secondary pointer to data item to store data
 * @param[out]      size            The size of recved data
 * @param[in]       timeout         The timeout to receive data
 *
 * @return          Returns the result of the operation
 * @retval          OS_FAILURE      Receive data failed
 * @retval          OS_TIMEOUT      Receive data timeout
 * @retval          OS_EMPTY        When the timeout is 0 and no data
 * @retval          OS_SUCCESS      Receive data successfully
 ***********************************************************************************************************************
 */
os_err_t mo_netconn_recvfrom(SR_NETDEV_T  *module,
                             mo_netconn_t *netconn,
                             void        **data,
                             os_size_t    *size,
                             ip_addr_t    *addr,
                             uint16_t     *port,
                             os_tick_t     timeout)
{
    OS_ASSERT(OS_NULL != module);
    OS_ASSERT(OS_NULL != netconn);
    OS_ASSERT(OS_NULL != data);
    OS_ASSERT(OS_NULL != size);

    mo_netconn_msg_t msg      = {0};
    os_size_t        msg_size = 0;

   // os_err_t result = os_msgqueue_recv(netconn->mq_id, (void *)&msg, sizeof(mo_netconn_msg_t), timeout, &msg_size);
	int result =  osMessageQueueGet(netconn->mq_id,(void *)&msg, 0, timeout);

#if  0
    if (netconn->stat != NETCONN_STAT_CONNECT && result == OS_EMPTY)
    {
        SR_LOG(DEBUG,"Module %s netconn %d does not connect to server, recv failed!", module->name, netconn->connect_id);
        return OS_FAILURE;
    }

    if (OS_TIMEOUT == result || OS_EMPTY == result)
    {
        SR_LOG(DEBUG,"Module %s netconn %d receive timeout", module->name, netconn->connect_id);
        return result;
    }

    if (OS_FAILURE == result)
    {
        SR_LOG(ERR,"Module %s netconn %d receive error", module->name, netconn->connect_id);
        return result;
    }
#else
	if (SR_OK != result)
	{
		SR_LOG(DEBUG,"Module %s netconn %d receive error", module->name, netconn->connect_id);

		return SR_EKERNEL;
	}

#endif


    if (OS_NULL == msg.data || 0 == msg.data_len)
    {
        SR_LOG(ERR,"Module %s netconn %d receive data invalid, msg.data[0x%08x], data_len[%d]",
              module->name,
              netconn->connect_id,
              msg.data,
              msg.data_len);
        return OS_FAILURE;
    }

    //if (OS_SUCCESS == result)
    if (SR_OK == result)
    {
        *data = msg.data;
        *size = msg.data_len;
        if (OS_NULL != addr)
        {
            *addr = msg.addr;
        }
        if (OS_NULL != port)
        {
            *port = msg.port;
        }

#ifdef MOLINK_USING_SOCKETS_OPS
        if (OS_NULL != netconn->evt_func)
        {
            netconn->evt_func(netconn, MO_NETCONN_EVT_RCVMINUS, *size);
        }
#endif
    }

    return result;
}

#endif /* MOLINK_USING_NETCONN_OPS */



#ifdef SR_USE_OS
/**************OS系统环境*******************/




#else

/**************裸机环境*******************/

//CTWING相关API
#ifdef SR_NETDEV_USE_CTWING


int SR_HalNetCtwSetEvtCb(SR_NETDEV_T *dev,CtwEvtCb cb,void *args)
{
 
	if(SR_NULL == dev || SR_NULL == cb) return SR_INVALID;
	dev->ctwEvtCb = (void *)cb;
	dev->ctwEvtArgs = args;
	
	return SR_OK;
}



//ctwing平台连接
int _HalNetCtwConnect(SR_NETDEV_T *dev,CTW_CONN_PARAM_T *param)
{

    int ret = 0;
	SR_GET_CT();
		
	if((SR_NULL == dev)) return SR_INVALID;
		
	SR_NETDEV_CTW_OPS_T *ops = (SR_NETDEV_CTW_OPS_T *)dev->ctwOps;

	if ((SR_NULL == ops) || (SR_NULL == ops->ctw_connect)) return SR_NOIMPT;

	SR_CT_BEGIN();

	SR_CT_CHILD_RET(ret,ops->ctw_connect(dev,param));

	return ret;

	SR_CT_END();
}

//ctwing平台发送数据
int _HalNetCtwSend(SR_NETDEV_T *dev,uint8_t *data,uint16_t dLen)
{

    int ret = 0;
	SR_GET_CT();
		
	if((SR_NULL == dev)) return SR_INVALID;
		
	SR_NETDEV_CTW_OPS_T *ops = (SR_NETDEV_CTW_OPS_T *)dev->ctwOps;

	if ((SR_NULL == ops) || (SR_NULL == ops->ctw_send)) return SR_NOIMPT;

	SR_CT_BEGIN();

	SR_CT_CHILD_RET(ret,ops->ctw_send(dev,data,dLen));

	return ret;

	SR_CT_END();
}



#endif /* SR_NETDEV_USE_CTWING */



#endif /* SR_USE_OS */

