#include "pjnath_client.h"
#include <pj/compat/socket.h>

#define THIS_FILE	"pjnath_ice.c"

static     pj_thread_t		*gpICEThread=NULL;
static int				icequit=0;

/* Forward declarations */
static void try_reading(ClientOptions *g);

static void destroy_ice(pj_ice_sess *ice,
			pj_status_t reason);

static void	   on_ice_complete(pj_ice_sess *ice, pj_status_t status);

static pj_status_t ice_tx_pkt(pj_ice_sess *ice, 
			      unsigned comp_id,
			      const void *pkt, pj_size_t size,
			      const pj_sockaddr_t *dst_addr,
			      unsigned dst_addr_len);

static void	   ice_rx_data(pj_ice_sess *ice, 
			      unsigned comp_id, 
			      void *pkt, pj_size_t size,
			      const pj_sockaddr_t *src_addr,
			      unsigned src_addr_len);

/* Ioqueue callback */
static void on_read_complete(pj_ioqueue_key_t *key, 
                             pj_ioqueue_op_key_t *op_key, 
                             pj_ssize_t bytes_read);

/* Timer callback to call on_ice_complete() callback */
static void on_completion_timer(pj_timer_heap_t *th, 
			        pj_timer_entry *te)
{
    pj_ice_sess *ice = (pj_ice_sess*) te->user_data;

    PJ_UNUSED_ARG(th);

    te->id = PJ_FALSE;

    if (ice->cb.on_ice_complete)
	(*ice->cb.on_ice_complete)(ice, ice->ice_status);
}

/**
*ice_worker_thread:
**/
static int ice_worker_thread(void *unused)
{
  ClientOptions *g= (ClientOptions*)unused;
  pj_thread_desc handler_desc;
  pj_thread_t *ice_thread;
  
  //register thread with pjlib
  pj_thread_register("ICEWORK", handler_desc, &ice_thread);

  while (!icequit) {    

    pj_timer_heap_poll(g->th, NULL);
    //pj_ioqueue_poll(g->ice->stun_cfg.ioqueue, &timeout);
	  try_reading(g);
	  //pj_thread_sleep(10);
  }
  
  return PJ_SUCCESS;
}//ice_worker_thread


/*
* initialize ice session
*/
void init_ice(ClientOptions *g) {

  pj_status_t status;
  pj_ice_sess_cb ice_cb;
  pj_ice_sess_role role;

  //if ice listener thread exists then 
  //destroy that thread
  if(gpICEThread) {
		icequit=1;
		pj_thread_join(gpICEThread);
		pj_thread_destroy(gpICEThread);
		gpICEThread=NULL;
  }

  icequit=0;

  /* Init callback */
  pj_bzero(&ice_cb, sizeof(ice_cb));
  ice_cb.on_ice_complete = &on_ice_complete;
  ice_cb.on_rx_data = &ice_rx_data;
  ice_cb.on_tx_pkt = &ice_tx_pkt;

  /* Create! */
  role=PJ_ICE_SESS_ROLE_UNKNOWN;

  //pj_stun_config_init(&stun_cfg, &g->cp.factory, 0, NULL, g->th);

  //printf("here\n");

  //status = pj_ioqueue_create(g->pool, 16, &stun_cfg.ioqueue);
  status = pj_ice_sess_create(&g->stun_config, NULL, role,
	          1 /*1 components*/, &ice_cb, 
	          NULL, NULL, &g->ice);
  
  if (status != PJ_SUCCESS) {
	  return;
  }
    
  //printf("here2\n");

  status = pj_ioqueue_create(g->ice->pool, 16, &g->ice->stun_cfg.ioqueue);

  /* Associate user data */
  g->ice->user_data = (void*)g;

  if (g->user_name) {
	  pj_stun_auth_cred cred;

	  pj_bzero(&cred, sizeof(cred));

	  cred.type = PJ_STUN_AUTH_CRED_STATIC;
	  cred.data.static_cred.realm = pj_str(g->realm);
	  cred.data.static_cred.username = pj_str(g->user_name);
	  cred.data.static_cred.data_type = 0;
	  cred.data.static_cred.data = pj_str(g->password);
	  cred.data.static_cred.nonce = pj_str(g->nonce);

	  pj_stun_session_set_credential(g->ice->comp[0].stun_sess, &cred);
    //P2PP
	  //puts("Session credential set");
  } else {
    //P2PP
	  //puts("Credential not set");
  }

	
	/* Register to ioqueue*/ 
/*    pj_bzero(&ioqueue_cb, sizeof(ioqueue_cb));
    ioqueue_cb.on_read_complete = &on_read_complete;

    status = pj_ioqueue_register_sock(g->ice->pool, g->ice->stun_cfg.ioqueue,
				      g->sock, (void*)g, &ioqueue_cb,
				      &g->key);
    if (status != PJ_SUCCESS)
      return;

    pj_ioqueue_op_key_init(&g->read_op, sizeof(g->read_op));
    
    //Kick start reading the socket 
    on_read_complete(g->key, &g->read_op, 0);
*/

}//init_ice


void listen_for_ice(ClientOptions *g) {
  pj_status_t status;
  if(!gpICEThread) {
    status = pj_thread_create(g->ice->pool, "stun", &ice_worker_thread, (void*)g, 
                              0, 0, &gpICEThread);
    if (status != PJ_SUCCESS) {
      return;
    }

	}//end if
}//listen_for_ice


/*
 * Destroy
 */
void destroy_ice_sess(ClientOptions *g, pj_ice_sess *ice) {

	if(gpICEThread){
		icequit=1;
//		pj_thread_sleep(100);
		pj_thread_join(gpICEThread);
		pj_thread_destroy(gpICEThread);
		gpICEThread=NULL;

	}
    destroy_ice(ice, PJ_SUCCESS);

    return;
}

void set_ice_local_cand_host(ClientOptions *g, 
			     struct pj_sockaddr_in *addr) 
{
	pj_uint16_t local_pref = 65535;
	pj_str_t foundation;

	pj_ice_cand_type type=PJ_ICE_CAND_TYPE_HOST;

	pj_ice_calc_foundation(g->ice->pool, &foundation, type, 
			   (pj_sockaddr*)&g->peer_addr);
	
	/*add candidate*/
	pj_ice_sess_add_cand(g->ice, 1, 
		type,
		local_pref,
		&foundation, 
		addr, 
		addr, 
		NULL, 
		sizeof(pj_sockaddr_in),
		NULL);
}

void set_ice_local_cand_srflex(ClientOptions *g, 
			       struct pj_sockaddr_in *addr) 
{
	pj_uint16_t local_pref = 65535;
	pj_str_t foundation;

	pj_ice_cand_type type=PJ_ICE_CAND_TYPE_SRFLX;

	pj_ice_calc_foundation(g->ice->pool, &foundation, type, 
			   (pj_sockaddr*)&g->peer_addr);
	
	/*add candidate*/
	pj_ice_sess_add_cand(g->ice, 1, 
		type,
		local_pref,
		&foundation, 
		addr, 
		&g->peer_addr, 
		NULL, 
		sizeof(pj_sockaddr_in),
		NULL);

}

void set_ice_local_cand_relay(ClientOptions *g, 
			      struct pj_sockaddr_in *addr) {
  pj_uint16_t local_pref = 65535;
  pj_str_t foundation;
  
  pj_ice_cand_type type=PJ_ICE_CAND_TYPE_RELAYED;
  
  pj_ice_calc_foundation(g->ice->pool, &foundation, type, 
			 (pj_sockaddr*)&g->peer_addr);
  
  /*add candidate*/
  pj_ice_sess_add_cand(g->ice, 1, 
		       type,
		       local_pref,
		       &foundation, 
		       addr, 
		       addr, 
		       NULL, 
		       sizeof(pj_sockaddr_in),
		       NULL);
}


void set_ice_remote_cand_host(ClientOptions *g, 
			      struct pj_sockaddr_in *addr) {  
  
  /*
    pj_ice_calc_foundation(g->ice->pool, &foundation, type, 
    &g->peer_addr);
    
    //add candidate
	pj_ice_sess_add_cand(g->ice, 2, 
	type,
	local_pref,
	&foundation, 
	addr, 
	&g->peer_addr, 
	NULL, 
	sizeof(pj_sockaddr_in),
	NULL);
  */
}

void set_ice_remote_cand_srflex(ClientOptions *g, 
				struct pj_sockaddr_in *addr) {
    /*
    pj_ice_calc_foundation(g->ice->pool, &foundation, type, 
    (pj_sockaddr*)&g->peer_addr);
    
    //add candidate
    pj_ice_sess_add_cand(g->ice, 2, 
    type,
    local_pref,
    &foundation, 
    addr, 
    &g->peer_addr, 
    NULL, 
    sizeof(pj_sockaddr_in),
    NULL);
  */
}


void set_ice_remote_cand_relay(ClientOptions *g, 
			       struct pj_sockaddr_in *addr) {
  pj_str_t foundation;
  pj_ice_cand_type type=PJ_ICE_CAND_TYPE_RELAYED;
    
  pj_ice_calc_foundation(g->ice->pool, &foundation, type, 
			   (pj_sockaddr*)&g->peer_addr);
  
  /*add candidate*/
  /*	pj_ice_sess_add_cand(g->ice, 2, 
	type,
	local_pref,
	&foundation, 
	addr, 
	addr, 
	NULL, 
	sizeof(pj_sockaddr_in),
	NULL);
  */
}

static pj_uint32_t CALC_CAND_PRIO(pj_ice_sess *ice,
				  pj_ice_cand_type type,
				  pj_uint32_t local_pref,
				  pj_uint32_t comp_id) {
  return ((ice->prefs[type] & 0xFF) << 24) +
    ((local_pref & 0xFFFF)    << 8) +
    (((256 - comp_id) & 0xFF) << 0);
}

void create_ice_cand(pj_ice_sess_cand *rcand,
		     unsigned comp_id,
		     pj_ice_cand_type type,
		     pj_ice_sess *ice, 
		     const pj_sockaddr_t *addr,
		     const pj_sockaddr_t *base_addr,
		     int addr_len) {
  pj_uint16_t local_pref = 65535;
  
  rcand->comp_id = comp_id;
  rcand->type = type;
  pj_ice_calc_foundation(ice->pool, &rcand->foundation, type,
			 base_addr);
  
  rcand->prio = CALC_CAND_PRIO(ice, type, local_pref, 2);
  pj_memcpy(&rcand->addr, addr, addr_len);
  //  pj_memcpy(&rcand->base_addr, base_addr, addr_len);
  pj_bzero(&rcand->base_addr, sizeof(rcand->base_addr));
  pj_bzero(&rcand->rel_addr, sizeof(rcand->rel_addr));
}

void start_check(ClientOptions *g, 
		 pj_ice_sess_cand *rcand) {
  pj_status_t status;
  pj_str_t rem_ufrag= pj_str(""), rem_passwd=pj_str("");
   
  status = pj_ice_sess_create_check_list(g->ice, &rem_ufrag, &rem_passwd,
					 g->rcand_cnt, g->rcand);
  if (status != PJ_SUCCESS)
    return;
  
  status = pj_ice_sess_start_check(g->ice);
  if (status != PJ_SUCCESS) {
      return;
  }
  
  //NO NEED (gaurav) pj_thread_join(gpICEThread);
  
  return;
}

/*
 * Destroy
 */
static void destroy_ice(pj_ice_sess *ice,
			pj_status_t reason) {
  unsigned i;
  
  if (reason == PJ_SUCCESS) {
    //	LOG4((ice->obj_name, "Destroying ICE session"));
  }
  
  /* Let other callbacks finish */
  if (ice->mutex) {
    pj_mutex_lock(ice->mutex);
    pj_mutex_unlock(ice->mutex);
  }
  
  if (ice->completion_timer.id) {
    pj_timer_heap_cancel(ice->stun_cfg.timer_heap, 
			 &ice->completion_timer);
    ice->completion_timer.id = PJ_FALSE;
  }
  
  for (i=0; i<ice->comp_cnt; ++i) {
    if (ice->comp[i].stun_sess) {
      pj_stun_session_destroy(ice->comp[i].stun_sess);
      ice->comp[i].stun_sess = NULL;
    }
  }
  
  if (ice->clist.timer.id) {
    pj_timer_heap_cancel(ice->stun_cfg.timer_heap, &ice->clist.timer);
    ice->clist.timer.id = PJ_FALSE;
  }
  
  if (ice->mutex) {
    pj_mutex_destroy(ice->mutex);
    ice->mutex = NULL;
  }
  
  if (ice->pool) {
    pj_pool_t *pool = ice->pool;
    ice->pool = NULL;
    pj_pool_release(pool);
  }
}

/*
 * Callback called by ICE session when ICE processing is complete, either
 * successfully or with failure.
 */
static void on_ice_complete(pj_ice_sess *ice, pj_status_t status) {
  //ClientOptions *g=(ClientOptions*)ice->user_data;
  if(gpICEThread){
    //order of statements is important
    //pj_thread_sleep(1000);
    icequit=1;
    //		gpICEThread=NULL;
  }
/*********
	  pj_ice_strans *ice_st = (pj_ice_strans*)ice->user_data;
	  if (ice_st->cb.on_ice_complete) {
	  (*ice_st->cb.on_ice_complete)(ice_st, status);
	  }
********/
}

/*
 * Callback called by ICE session when it wants to send outgoing packet.
 */
static pj_status_t ice_tx_pkt(pj_ice_sess *ice, 
			      unsigned comp_id, 
			      const void *pkt, pj_size_t size,
			      const pj_sockaddr_t *dst_addr,
			      unsigned dst_addr_len) {
  struct client_options *g;
  pj_ssize_t len;
  pj_status_t status;
  g=(ClientOptions*)ice->user_data;
  //    g= (struct client_options*)sess->user_data;
  
  if(!g){
    return 0;
  }
  if(g->sock==-1) {
    g->pktlen=size;
    memcpy(g->pkt, pkt, size);
    status=PJ_SUCCESS;
  } else {
    len = (pj_ssize_t)size;
    //    printf("send using sock %d\n", (int) src_sock);
    
    status = pj_sock_sendto(g->sock, pkt, &len, 0, dst_addr, dst_addr_len);
    if (status != PJ_SUCCESS) {
      printf("Error sending packet :%d\n", status);
    }
  }
  
  
  return status;
  
  /***********
    pj_ice_strans *ice_st = (pj_ice_strans*)ice->user_data;
    pj_ice_strans_comp *comp = NULL;
    pj_ssize_t pkt_size;
    pj_status_t status;

    PJ_TODO(TX_TO_RELAY);

    PJ_ASSERT_RETURN(comp_id && comp_id <= ice_st->comp_cnt, PJ_EINVAL);
    comp = ice_st->comp[comp_id-1];

    TRACE_PKT((comp->ice_st->obj_name, 
	      "Component %d TX packet to %s:%d",
	      comp_id,
	      pj_inet_ntoa(((pj_sockaddr_in*)dst_addr)->sin_addr),
	      (int)pj_ntohs(((pj_sockaddr_in*)dst_addr)->sin_port)));

    pkt_size = size;
    status = pj_ioqueue_sendto(comp->key, &comp->write_op, 
			       pkt, &pkt_size, 0,
			       dst_addr, dst_addr_len);
    
    return (status==PJ_SUCCESS||status==PJ_EPENDING) ? PJ_SUCCESS : status;

  ******************/
}

/*
 * Callback called by ICE session when it receives application data.
 */
static void ice_rx_data(pj_ice_sess *ice, 
		        unsigned comp_id, 
		        void *pkt, pj_size_t size,
		        const pj_sockaddr_t *src_addr,
		        unsigned src_addr_len) {
  /*************************
	pj_ice_strans *ice_st = (pj_ice_strans*)ice->user_data;

    if (ice_st->cb.on_rx_data) {
	(*ice_st->cb.on_rx_data)(ice_st, comp_id, pkt, size, 
				 src_addr, src_addr_len);
    }
  **********************/
}

/*
 * Callback called by STUN session to send outgoing packet.
 */
static pj_status_t stun_on_send_msg(pj_stun_session *sess,
				    const void *pkt,
				    pj_size_t size,
				    const pj_sockaddr_t *dst_addr,
				    unsigned dst_addr_len) {
  struct client_options *g;
  pj_ssize_t len;
  pj_status_t status;
  g=(ClientOptions*)pj_stun_session_get_user_data(sess);
  //    g= (struct client_options*)sess->user_data;
  
  if(!g){
    return 0;
  }
  if(g->sock==-1) {
    g->pktlen=size;
    memcpy(g->pkt, pkt, size);
    status=PJ_SUCCESS;
  } else {
    len = (pj_ssize_t)size;
    //    printf("send using sock %d\n", (int) src_sock);
    status = pj_sock_sendto(g->sock, pkt, &len, 0, dst_addr, dst_addr_len);
  }
  
  if (status != PJ_SUCCESS)
    PJ_LOG(2,(THIS_FILE,"Error sending packet", status));
  
  return status;
  
  /**********
  pj_ice_strans_comp *comp;
  pj_ssize_t pkt_size;
  pj_status_t status;
  
  comp = (pj_ice_strans_comp*) pj_stun_session_get_user_data(sess);
  pkt_size = size;
  status = pj_ioqueue_sendto(comp->key, &comp->write_op, 
  pkt, &pkt_size, 0,
  dst_addr, dst_addr_len);
  
  return (status==PJ_SUCCESS||status==PJ_EPENDING) ? PJ_SUCCESS : status;
	****************/
}

/*
 * This is callback called by ioqueue on incoming packet
 */
static void on_read_complete(pj_ioqueue_key_t *key,
			     pj_ioqueue_op_key_t *op_key,
			     pj_ssize_t bytes_read) {
  ClientOptions *g = (ClientOptions *)  pj_ioqueue_get_user_data(key);
  pj_ice_sess *ice = g->ice;
  pj_ssize_t pkt_size;
  enum { RETRY = 10 };
  unsigned retry;
  pj_status_t status;
  int src_addr_len;
  
  if (bytes_read > 0) {
    /*
     * Okay, we got a packet from the socket for the component. There is
     * a bit of situation here, since this packet could be one of these:
     *
     * 1) this could be the response of STUN binding request sent by
     *    this component to a) an initial request to get the STUN mapped
     *    address of this component, or b) subsequent request to keep
     *    the binding alive.
     *
     * 2) this could be a packet (STUN or not STUN) sent from the STUN
     *    relay server. In this case, still there are few options to do
     *    for this packet: a) process this locally if this packet is
     *    related to TURN session management (e.g. Allocate response),
     *    b) forward this packet to ICE if this is related to ICE
     *    discovery process.
     *
     * 3) this could be a STUN request or response sent as part of ICE
     *    discovery process.
     *
     * 4) this could be application's packet, e.g. when ICE processing
     *    is done and agents start sending RTP/RTCP packets to each
     *    other, or when ICE processing is not done and this ICE stream
     *    transport decides to allow sending data.
     *
     * So far we don't have good solution for this.
     * The process below is just a workaround.
     */
    status = pj_stun_msg_check(g->pkt, bytes_read,
			       PJ_STUN_IS_DATAGRAM);
    
    if (status == PJ_SUCCESS) {
      if (g->sess &&
	  PJ_STUN_IS_RESPONSE(((pj_stun_msg_hdr*)g->pkt)->type))
	{
	  status = pj_stun_session_on_rx_pkt(g->ice->comp[0].stun_sess, g->pkt,
					     bytes_read,
					     PJ_STUN_IS_DATAGRAM, NULL,
					     &g->src_addr,
					     g->src_addr_len);
	} else if (ice) {
	  //PJ_TODO(DISTINGUISH_BETWEEN_LOCAL_AND_RELAY);

	  PJ_LOG(2, (THIS_FILE,
		     "RX packet from %s:%d",
		     pj_inet_ntoa(g->src_addr.ipv4.sin_addr),
		     (int)pj_ntohs(g->src_addr.ipv4.sin_port)));
	  
	  status = pj_ice_sess_on_rx_pkt(ice, 1,
					 g->pkt, bytes_read,
					 &g->src_addr,
					 g->src_addr_len);
	} else {
	  /* This must have been a very late STUN reponse,
	   * or an early STUN Binding Request when our local
	   * ICE has not been created yet. */
	}
    } else {
      //if not stun packet do nothing
    }
    
  } else if (bytes_read < 0) {
    //ice_st_perror(comp->ice_st, "ioqueue read callback error",
    //	  -bytes_read);
  }

  /* Read next packet */
  for (retry=0; retry<RETRY;) {
    pkt_size = sizeof(g->pkt);
    src_addr_len = sizeof(g->src_addr);
    status = pj_ioqueue_recvfrom(key, op_key, g->pkt, &pkt_size,
				 PJ_IOQUEUE_ALWAYS_ASYNC,
				 &g->src_addr, &g->src_addr_len);
    if (status == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK) ||
	status == PJ_STATUS_FROM_OS(OSERR_EINPROGRESS) ||
	status == PJ_STATUS_FROM_OS(OSERR_ECONNRESET))
      {
	//ice_st_perror(comp->ice_st, "ioqueue recvfrom() error", status);
	++retry;
	continue;
      } else if (status != PJ_SUCCESS && status != PJ_EPENDING) {
	retry += 2;
	//PJ_LOG( "ioqueue recvfrom() error", status);
      } else {
	break;
      }
  }
}

static void try_reading(ClientOptions *g) {
  pj_ice_sess *ice = g->ice;
  pj_ssize_t pkt_size;
  pj_status_t status;
  int src_addr_len;
  pj_fd_set_t read, write, exceptfds;
  pj_time_val timeout;
  int n, fdmax;

  timeout.sec=0;
  timeout.msec=50;

  PJ_FD_ZERO(&read);
  PJ_FD_ZERO(&write);
  PJ_FD_ZERO(&exceptfds);
  PJ_FD_SET(g->sock, &read);

  fdmax = g->sock;
  n = pj_sock_select(fdmax+1, &read, NULL, NULL, &timeout);
  if(n < 1) 
	  return;

  if(PJ_FD_ISSET(g->sock, &read) ){


	  pkt_size = sizeof(g->pkt);
	  src_addr_len = sizeof(g->src_addr);
	  status = pj_sock_recvfrom(g->sock, g->pkt, &pkt_size,
					 0,
					 &g->src_addr, &src_addr_len);

		if(status!=PJ_SUCCESS) {
			return;
		}
  } else {
	  return;
  }
//	printf("Packet recvd: %d\n",pkt_size);

    status = pj_stun_msg_check(g->pkt, pkt_size,
			       PJ_STUN_IS_DATAGRAM);

    if (status == PJ_SUCCESS) {
      if (g->sess &&
	  PJ_STUN_IS_RESPONSE(((pj_stun_msg_hdr*)g->pkt)->type))
	{
	  status = pj_stun_session_on_rx_pkt(g->ice->comp[0].stun_sess, g->pkt,
					     pkt_size,
					     PJ_STUN_IS_DATAGRAM, NULL,
					     &g->src_addr,
					     src_addr_len);
	} else if (ice) {
	  //PJ_TODO(DISTINGUISH_BETWEEN_LOCAL_AND_RELAY);

	  PJ_LOG(2, (THIS_FILE,
		     "RX packet from %s:%d",
		     pj_inet_ntoa(g->src_addr.ipv4.sin_addr),
		     (int)pj_ntohs(g->src_addr.ipv4.sin_port)));

	  status = pj_ice_sess_on_rx_pkt(ice, 1,
					 g->pkt, pkt_size,
					 &g->src_addr,
					 src_addr_len);
	} else {
	  /* This must have been a very late STUN reponse,
	   * or an early STUN Binding Request when our local
	   * ICE has not been created yet. */
	}
	}

}

void get_valid_rcand(ClientOptions *g, char *ipaddr, unsigned short *port, char* type) {
  
  const pj_ice_sess_cand *lcand;
  const pj_ice_sess_cand *rcand;
  int relayed=-1, srflex=-1, host=-1, bestaddr=-1;
  unsigned int i;

  if(g->ice->valid_list.count==0)
    return;


  /* If there's no nominated pair, find relayed candidate */
  for (i=0; i<g->ice->valid_list.count; ++i) {
		lcand = g->ice->valid_list.checks[i].lcand;
		rcand = g->ice->valid_list.checks[i].rcand;
		if (rcand->type == PJ_ICE_CAND_TYPE_RELAYED) 
		{
			relayed=i;
			  //strcpy(ipaddr, pj_inet_ntoa(rcand->addr.ipv4.sin_addr));
			  //*port=(int)pj_ntohs(rcand->addr.ipv4.sin_port);
		}
		if (rcand->type == PJ_ICE_CAND_TYPE_SRFLX) 
		{
			srflex=i;
		}
		if (rcand->type == PJ_ICE_CAND_TYPE_HOST) 
		{
			host=i;
		}
    }

	if(host!=-1){
		bestaddr=host;
	}else if(srflex!=-1){
		bestaddr=srflex;
	}else {
		bestaddr=relayed;
	}
  lcand = g->ice->valid_list.checks[bestaddr].lcand;
  rcand = g->ice->valid_list.checks[bestaddr].rcand;

  strcpy(ipaddr, pj_inet_ntoa(rcand->addr.ipv4.sin_addr));
  *port=(int)pj_ntohs(rcand->addr.ipv4.sin_port);

  if(rcand->type==PJ_ICE_CAND_TYPE_HOST) {
    strcpy(type, "host");
  } else if(rcand->type==PJ_ICE_CAND_TYPE_SRFLX) {
    strcpy(type, "srflx");
  } else if(rcand->type==PJ_ICE_CAND_TYPE_RELAYED) {
    strcpy(type, "relay");
  } else {
    strcpy(type, "");
  }
	return;
}

int is_ice_complete(ClientOptions *g) {
  return g->ice->is_complete;
}