
#include <sys/socket.h> //socket
#include <sys/types.h>  //socket
#include <sys/socket.h>

#include <netinet/in.h> //hton, ntoh
#include <arpa/inet.h>  //inet_addr

#include <linux/sockios.h>
#include <sys/stat.h>
#include <sys/ioctl.h>

#include <netdb.h>

#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>

#include <linux/sockios.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <netinet/tcp.h>

#include <fcntl.h>

#include <stdio.h>
#include <unistd.h>

extern "C" {
#include <pjnath_client.h>
#include <pjnath_server.h>
}


/**
 *callbacks for bind allocate and netdetect
 **/
void bind_cb(const pj_sockaddr_in* ret){
  printf("bind callback\n");
  printf("binding: %s:%d\n", (char *)inet_ntoa( ((struct sockaddr_in*)ret)->sin_addr), ntohs(((struct sockaddr_in*)ret)->sin_port)); 
}
void allocate_cb(const pj_sockaddr_in* ret){
  printf("alloc callback\n");
  printf("allocated addr %s:%d\n", (char *)inet_ntoa(((struct sockaddr_in*)ret)->sin_addr), ntohs(((struct sockaddr_in*)ret)->sin_port)); 
}
void natdetect_cb(const pj_stun_nat_type result){
  printf("natdetect cb\n");
  printf("Nat type detected : %s\n", pj_stun_get_nat_name(result));
}

/**
 *main
 **/
int main(int argc, char **argv) {

  struct sockaddr_in myaddr, myaddr2, response, from, dummy;
  int port= 8765, port2 =  8764;
  int udpsock, udpsock2;
  struct client_options op;
  unsigned char buf[1024];
  int buflen=0;
  pj_stun_msg_type restype;
  int result, fromlen;
  char validip[20];
  unsigned short validport;

  if(argc<3){
    printf("Usage: test <stun server ip> <stun server port>\n");
    exit(0);
  }

  memset(&dummy, 0, sizeof(struct sockaddr_in));
  //  memset(&sop, 0, sizeof(struct server_options));
  //  init_server(&sop);
  
  udpsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

  //bind UDP listener to UDP port
  myaddr.sin_family = AF_INET;
  myaddr.sin_addr.s_addr = inet_addr("128.59.19.185");//htonl(INADDR_ANY);
  myaddr.sin_port = htons(port);
  memset(&(myaddr.sin_zero), '\0', 8);

  if(-1==bind(udpsock, (struct sockaddr *)&myaddr, sizeof(myaddr))){
    printf("bind err\n");
  }

  udpsock2 = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

  //bind UDP listener to UDP port
  myaddr2.sin_family = AF_INET;
  myaddr2.sin_addr.s_addr = inet_addr("128.59.19.185");//htonl(INADDR_ANY);
  myaddr2.sin_port = htons(port2);
  memset(&(myaddr2.sin_zero), '\0', 8);

  if(-1==bind(udpsock2, (struct sockaddr *)&myaddr2, sizeof(myaddr2))){
    printf("bind err\n");
  }

  memset(&op, 0, sizeof(struct client_options));
  strncpy(op.ch_srv_addr, argv[1],IPADDRLEN); //"animaltrak.cs.columbia.edu";
  strcpy(op.ch_srv_port, argv[2]); //"3478";
  op.use_fingerprint=0;

  //  set_config(&op);
  op.sock = udpsock;
  init_client(&op);
  op.peer_addr.sin_family = AF_INET;
  op.peer_addr.sin_addr.s_addr = inet_addr("128.59.19.185");//htonl(INADDR_ANY);
  op.peer_addr.sin_port = htons(port);
  memset(&(op.peer_addr.sin_zero), '\0', 8);
  printf("send using sock0 %d\n", (int) udpsock);

  //###############ALLOCATE#################
  send_allocate_request((int)udpsock, 1, &op);
  
  memset(from.sin_zero, '\0', sizeof(from.sin_zero));
  fromlen = sizeof(from);
  if((buflen=recvfrom(udpsock, buf, 1024, 0, (struct sockaddr*)&from, (socklen_t *)&fromlen))==-1){
    printf("err in recv\n");
  } 

  if(stun_msg_check(buf, buflen) ) {
    if(0==parse_stun_msg(&op, buf, buflen,(pj_sockaddr_in*) &from, fromlen, &restype, &result, (pj_sockaddr_in*)&response)){
      printf("bind resp err\n");
    }else{
      printf("allocate: %s:%d\n", (char *)inet_ntoa(response.sin_addr), ntohs(response.sin_port)); 
    }
  } else {
    printf("not stun message msglen %d\n", fromlen);
  }

  //set_ice_local_cand_relay(&op,(pj_sockaddr_in*)&response);

  //###############BINDING#################
  send_bind_request((int)udpsock, &op);
  memset(from.sin_zero, '\0', sizeof(from.sin_zero));
  fromlen = sizeof(from);
  if((buflen=recvfrom(udpsock, buf, 1024, 0, (struct sockaddr*)&from, (socklen_t *)&fromlen))==-1){
    printf("err in recv\n");
  } 

  if(stun_msg_check(buf, buflen) ) {
    if(0==parse_stun_msg(&op, buf, buflen,(pj_sockaddr_in*) &from, fromlen, &restype, &result, (pj_sockaddr_in*)&response)){
      printf("bind resp err\n");
    }else{
      printf("allocate: %s:%d\n", (char *)inet_ntoa(response.sin_addr), ntohs(response.sin_port)); 
    }
  } else {
    printf("not stun message msglen %d\n", fromlen);
  }

  //set_ice_local_cand_srflex(&op,(pj_sockaddr_in*)&response);

  set_ice_local_cand_host(&op,(pj_sockaddr_in*)&myaddr);

  //################REMOTE CANDS.###########
  //unsigned rcand_cnt;    /**< # of remote cand.  */
  pj_ice_sess_cand rcand[3];    /**< Array of cand.    */
  //pj_uint16_t local_pref = 65535;

  //set_ice_remote_cand_srflex(&op,(pj_sockaddr_in*)&myaddr2);

  //set_ice_remote_cand_host(&op,(pj_sockaddr_in*)&myaddr2);
 
  myaddr2.sin_family = AF_INET;
  //myaddr2.sin_addr.s_addr = inet_addr("192.168.1.2");
  //myaddr2.sin_port = htons(5060);
    myaddr2.sin_addr.s_addr = inet_addr("128.59.23.154");
  myaddr2.sin_port = htons(10600);
  memset(&(myaddr2.sin_zero), '\0', 8);

  create_ice_cand(&rcand[0], 
		  1,
		  PJ_ICE_CAND_TYPE_HOST,
		  op.ice,
		  &myaddr2,
		  &myaddr2,
		  sizeof(myaddr2));
  /*    
  myaddr2.sin_family = AF_INET;
  myaddr2.sin_addr.s_addr = inet_addr("128.59.17.203");
  myaddr2.sin_port = htons(5060);
  memset(&(myaddr2.sin_zero), '\0', 8);
  create_ice_cand(&rcand[1], 
		  1,
		  PJ_ICE_CAND_TYPE_SRFLX,
		  op.ice,
		  &myaddr2,
		  &myaddr2,
		  sizeof(myaddr2));

  //  set_ice_remote_cand_relay(&op,(pj_sockaddr_in*)&myaddr2);

  myaddr.sin_family = AF_INET;
  myaddr.sin_addr.s_addr = inet_addr("128.59.19.185");
  myaddr.sin_port = htons(2004);
  memset(&(myaddr.sin_zero), '\0', 8);
  create_ice_cand(&rcand[2], 
		  1,
		  PJ_ICE_CAND_TYPE_RELAYED,
		  op.ice,
		  &myaddr,
		  &myaddr,
		  sizeof(myaddr2));

  */
  
  //  close(udpsock);
  
  

  //##########PERFORM TEST ###########
  start_check(&op, rcand);

  sleep(10);
  char type[20];
  get_valid_rcand(&op, validip, &validport, type);
  printf("result: %s:%d\n", validip, validport);
  //  perform_detection(&op);

  //  shutdown_server();
  
  return 0;
}
