/*!
 * \brief Pass WebSocket data into pjsip transport manager.
 */
static int transport_read(void *data)
{
	struct transport_read_data *read_data = data;
	struct ws_transport *newtransport = read_data->transport;
	struct ast_websocket *session = newtransport->ws_session;

	pjsip_rx_data *rdata = &newtransport->rdata;
	int recvd;
	pj_str_t buf;
	int pjsip_pkt_len;

	pj_gettimeofday(&rdata->pkt_info.timestamp);

	pjsip_pkt_len = PJSIP_MAX_PKT_LEN < read_data->payload_len ? PJSIP_MAX_PKT_LEN : read_data->payload_len;
	pj_memcpy(rdata->pkt_info.packet, read_data->payload, pjsip_pkt_len);
	rdata->pkt_info.len = pjsip_pkt_len;
	rdata->pkt_info.zero = 0;

	pj_sockaddr_parse(pj_AF_UNSPEC(), 0, pj_cstr(&buf, ast_sockaddr_stringify(ast_websocket_remote_address(session))), &rdata->pkt_info.src_addr);
	rdata->pkt_info.src_addr.addr.sa_family = pj_AF_INET();

	rdata->pkt_info.src_addr_len = sizeof(rdata->pkt_info.src_addr);

	pj_ansi_strcpy(rdata->pkt_info.src_name, ast_sockaddr_stringify_host(ast_websocket_remote_address(session)));
	rdata->pkt_info.src_port = ast_sockaddr_port(ast_websocket_remote_address(session));

	recvd = pjsip_tpmgr_receive_packet(rdata->tp_info.transport->tpmgr, rdata);

	pj_pool_reset(rdata->tp_info.pool);

	return (read_data->payload_len == recvd) ? 0 : -1;
}
void
SipIceTransport::onRecv()
{
    rdata_.pkt_info.len += ice_->recv(comp_id_,
        (uint8_t*)rdata_.pkt_info.packet+rdata_.pkt_info.len,
        sizeof(rdata_.pkt_info.packet)-rdata_.pkt_info.len);
    rdata_.pkt_info.zero = 0;
    pj_gettimeofday(&rdata_.pkt_info.timestamp);

    auto eaten = pjsip_tpmgr_receive_packet(rdata_.tp_info.transport->tpmgr, &rdata_);

    /* Move unprocessed data to the front of the buffer */
    auto rem = rdata_.pkt_info.len - eaten;
    if (rem > 0 && rem != rdata_.pkt_info.len) {
        std::move(rdata_.pkt_info.packet + eaten,
                  rdata_.pkt_info.packet + eaten + rem,
                  rdata_.pkt_info.packet);
    }
    rdata_.pkt_info.len = rem;

    /* Reset pool */
    pj_pool_reset(rdata_.tp_info.pool);
}
/*
 * udp_on_read_complete()
 *
 * This is callback notification from ioqueue that a pending recvfrom()
 * operation has completed.
 */
static void udp_on_read_complete( pj_ioqueue_key_t *key, 
				  pj_ioqueue_op_key_t *op_key, 
				  pj_ssize_t bytes_read)
{
    /* See https://trac.pjsip.org/repos/ticket/1197 */
    enum { MAX_IMMEDIATE_PACKET = 50 };
    pjsip_rx_data_op_key *rdata_op_key = (pjsip_rx_data_op_key*) op_key;
    pjsip_rx_data *rdata = rdata_op_key->rdata;
    struct udp_transport *tp = (struct udp_transport*)rdata->tp_info.transport;
    int i;
    pj_status_t status;

    /* Don't do anything if transport is closing. */
    if (tp->is_closing) {
	tp->is_closing++;
	return;
    }

    /* Don't do anything if transport is being paused. */
    if (tp->is_paused)
	return;

    /*
     * The idea of the loop is to process immediate data received by
     * pj_ioqueue_recvfrom(), as long as i < MAX_IMMEDIATE_PACKET. When
     * i is >= MAX_IMMEDIATE_PACKET, we force the recvfrom() operation to
     * complete asynchronously, to allow other sockets to get their data.
     */
    for (i=0;; ++i) {
    	enum { MIN_SIZE = 32 };
	pj_uint32_t flags;

	/* Report the packet to transport manager. Only do so if packet size
	 * is relatively big enough for a SIP packet.
	 */
	if (bytes_read > MIN_SIZE) {
	    pj_ssize_t size_eaten;
	    const pj_sockaddr *src_addr = &rdata->pkt_info.src_addr;

	    /* Init pkt_info part. */
	    rdata->pkt_info.len = bytes_read;
	    rdata->pkt_info.zero = 0;
	    pj_gettimeofday(&rdata->pkt_info.timestamp);
	    if (src_addr->addr.sa_family == pj_AF_INET()) {
		pj_ansi_strcpy(rdata->pkt_info.src_name,
			       pj_inet_ntoa(src_addr->ipv4.sin_addr));
		rdata->pkt_info.src_port = pj_ntohs(src_addr->ipv4.sin_port);
	    } else {
		pj_inet_ntop(pj_AF_INET6(), 
			     pj_sockaddr_get_addr(&rdata->pkt_info.src_addr),
			     rdata->pkt_info.src_name,
			     sizeof(rdata->pkt_info.src_name));
		rdata->pkt_info.src_port = pj_ntohs(src_addr->ipv6.sin6_port);
	    }

	    size_eaten = 
		pjsip_tpmgr_receive_packet(rdata->tp_info.transport->tpmgr, 
					   rdata);

	    if (size_eaten < 0) {
		pj_assert(!"It shouldn't happen!");
		size_eaten = rdata->pkt_info.len;
	    }

	    /* Since this is UDP, the whole buffer is the message. */
	    rdata->pkt_info.len = 0;

	} else if (bytes_read <= MIN_SIZE) {

	    /* TODO: */

	} else if (-bytes_read != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK) &&
		   -bytes_read != PJ_STATUS_FROM_OS(OSERR_EINPROGRESS) && 
		   -bytes_read != PJ_STATUS_FROM_OS(OSERR_ECONNRESET)) 
	{

	    /* Report error to endpoint. */
	    PJSIP_ENDPT_LOG_ERROR((rdata->tp_info.transport->endpt,
				   rdata->tp_info.transport->obj_name,
				   (pj_status_t)-bytes_read, 
				   "Warning: pj_ioqueue_recvfrom()"
				   " callback error"));
	}

	if (i >= MAX_IMMEDIATE_PACKET) {
	    /* Force ioqueue_recvfrom() to return PJ_EPENDING */
	    flags = PJ_IOQUEUE_ALWAYS_ASYNC;
	} else {
	    flags = 0;
	}

	/* Reset pool. 
	 * Need to copy rdata fields to temp variable because they will
	 * be invalid after pj_pool_reset().
	 */
	{
	    pj_pool_t *rdata_pool = rdata->tp_info.pool;
	    struct udp_transport *rdata_tp ;
	    unsigned rdata_index;

	    rdata_tp = (struct udp_transport*)rdata->tp_info.transport;
	    rdata_index = (unsigned)(unsigned long)(pj_ssize_t)
			  rdata->tp_info.tp_data;

	    pj_pool_reset(rdata_pool);
	    init_rdata(rdata_tp, rdata_index, rdata_pool, &rdata);

	    /* Change some vars to point to new location after
	     * pool reset.
	     */
	    op_key = &rdata->tp_info.op_key.op_key;
	}

	/* Only read next packet if transport is not being paused. This
	 * check handles the case where transport is paused while endpoint
	 * is still processing a SIP message.
	 */
	if (tp->is_paused)
	    return;

	/* Read next packet. */
	bytes_read = sizeof(rdata->pkt_info.packet);
	rdata->pkt_info.src_addr_len = sizeof(rdata->pkt_info.src_addr);
	status = pj_ioqueue_recvfrom(key, op_key, 
				     rdata->pkt_info.packet,
				     &bytes_read, flags,
				     &rdata->pkt_info.src_addr, 
				     &rdata->pkt_info.src_addr_len);

	if (status == PJ_SUCCESS) {
	    /* Continue loop. */
	    pj_assert(i < MAX_IMMEDIATE_PACKET);

	} else if (status == PJ_EPENDING) {
	    break;

	} else {

	    if (i < MAX_IMMEDIATE_PACKET) {

		/* Report error to endpoint if this is not EWOULDBLOCK error.*/
		if (status != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK) &&
		    status != PJ_STATUS_FROM_OS(OSERR_EINPROGRESS) && 
		    status != PJ_STATUS_FROM_OS(OSERR_ECONNRESET)) 
		{

		    PJSIP_ENDPT_LOG_ERROR((rdata->tp_info.transport->endpt,
					   rdata->tp_info.transport->obj_name,
					   status, 
					   "Warning: pj_ioqueue_recvfrom"));
		}

		/* Continue loop. */
		bytes_read = 0;
	    } else {
		/* This is fatal error.
		 * Ioqueue operation will stop for this transport!
		 */
		PJSIP_ENDPT_LOG_ERROR((rdata->tp_info.transport->endpt,
				       rdata->tp_info.transport->obj_name,
				       status, 
				       "FATAL: pj_ioqueue_recvfrom() error, "
				       "UDP transport stopping! Error"));
		break;
	    }
	}
    }
}
/* Worker thread for loop transport. */
static int loop_transport_worker_thread(void *arg)
{
    struct loop_transport *loop = arg;
    struct recv_list r;
    struct send_list s;

    pj_list_init(&r);
    pj_list_init(&s);

    while (!loop->thread_quit_flag) {
	pj_time_val now;

	pj_thread_sleep(1);
	pj_gettimeofday(&now);

	pj_lock_acquire(loop->base.lock);

	/* Move expired send notification to local list. */
	while (!pj_list_empty(&loop->send_list)) {
	    struct send_list *node = loop->send_list.next;

	    /* Break when next node time is greater than now. */
	    if (PJ_TIME_VAL_GTE(node->sent_time, now))
		break;

	    /* Delete this from the list. */
	    pj_list_erase(node);

	    /* Add to local list. */
	    pj_list_push_back(&s, node);
	}

	/* Move expired "incoming" packet to local list. */
	while (!pj_list_empty(&loop->recv_list)) {
	    struct recv_list *node = loop->recv_list.next;

	    /* Break when next node time is greater than now. */
	    if (PJ_TIME_VAL_GTE(node->rdata.pkt_info.timestamp, now))
		break;

	    /* Delete this from the list. */
	    pj_list_erase(node);

	    /* Add to local list. */
	    pj_list_push_back(&r, node);

	}

	pj_lock_release(loop->base.lock);

	/* Process send notification and incoming packet notification
	 * without holding down the loop's mutex.
	 */
	while (!pj_list_empty(&s)) {
	    struct send_list *node = s.next;

	    pj_list_erase(node);

	    /* Notify callback. */
	    if (node->callback) {
		(*node->callback)(&loop->base, node->token, node->sent);
	    }

	    /* Decrement tdata reference counter. */
	    pjsip_tx_data_dec_ref(node->tdata);
	}

	/* Process "incoming" packet. */
	while (!pj_list_empty(&r)) {
	    struct recv_list *node = r.next;
	    pj_ssize_t size_eaten;

	    pj_list_erase(node);

	    /* Notify transport manager about the "incoming packet" */
	    size_eaten = pjsip_tpmgr_receive_packet(loop->base.tpmgr,
						    &node->rdata);

	    /* Must "eat" all the packets. */
	    pj_assert(size_eaten == node->rdata.pkt_info.len);

	    /* Done. */
	    pjsip_endpt_release_pool(loop->base.endpt,
				     node->rdata.tp_info.pool);
	}
    }

    return 0;
}
/* Handler for sending outgoing message; called by transport manager. */
static pj_status_t loop_send_msg( pjsip_transport *tp, 
				  pjsip_tx_data *tdata,
				  const pj_sockaddr_t *rem_addr,
				  int addr_len,
				  void *token,
				  void (*cb)(pjsip_transport *transport,
					     void *token, 
					     pj_ssize_t sent_bytes))
{
    struct loop_transport *loop = (struct loop_transport*)tp;
    struct recv_list *recv_pkt;
    
    PJ_ASSERT_RETURN(tp && (tp->key.type == PJSIP_TRANSPORT_LOOP ||
	             tp->key.type == PJSIP_TRANSPORT_LOOP_DGRAM), PJ_EINVAL);

    PJ_UNUSED_ARG(rem_addr);
    PJ_UNUSED_ARG(addr_len);


    /* Need to send failure? */
    if (loop->fail_mode) {
	if (loop->send_delay == 0) {
	    return PJ_STATUS_FROM_OS(OSERR_ECONNRESET);
	} else {
	    add_notification(loop, tdata, -PJ_STATUS_FROM_OS(OSERR_ECONNRESET),
			     token, cb);

	    return PJ_EPENDING;
	}
    }

    /* Discard any packets? */
    if (loop->discard)
	return PJ_SUCCESS;

    /* Create rdata for the "incoming" packet. */
    recv_pkt = create_incoming_packet(loop, tdata);
    if (!recv_pkt)
	return PJ_ENOMEM;

    /* If delay is not configured, deliver this packet now! */
    if (loop->recv_delay == 0) {
	pj_ssize_t size_eaten;

	size_eaten = pjsip_tpmgr_receive_packet( loop->base.tpmgr, 
						 &recv_pkt->rdata);
	pj_assert(size_eaten == recv_pkt->rdata.pkt_info.len);

	pjsip_endpt_release_pool(loop->base.endpt, 
				 recv_pkt->rdata.tp_info.pool);

    } else {
	/* Otherwise if delay is configured, add the "packet" to the 
	 * receive list to be processed by worker thread.
	 */
	pj_lock_acquire(loop->base.lock);
	pj_list_push_back(&loop->recv_list, recv_pkt);
	pj_lock_release(loop->base.lock);
    }

    if (loop->send_delay != 0) {
	add_notification(loop, tdata, tdata->buf.cur - tdata->buf.start,
			 token, cb);
	return PJ_EPENDING;
    } else {
	return PJ_SUCCESS;
    }
}