#include "udp_t.h"

static int buffsize =32768; /* receive/send buffer size (bytes) */

/* get socket error ----------------------------------------------------------*/
#ifdef WIN32
static int errsock(void) {return WSAGetLastError();}
#else
static int errsock(void) {return errno;}
#endif

/* decode tcp/ntrip path (path=[user[:passwd]@]addr[:port][/mntpnt[:str]]) ---*/
static void decodeudppath(const char *path, char *addr, char *port, char *user,
                          char *passwd, char *mntpnt, char *str)
{
    char buff[MAXSTRPATH],*p,*q;
    
    if (port) *port='\0';
    if (user) *user='\0';
    if (passwd) *passwd='\0';
    if (mntpnt) *mntpnt='\0';
    if (str) *str='\0';
    
    strcpy(buff,path);
    
    if (!(p=strrchr(buff,'@'))) p=buff;
    
    if ((p=strchr(p,'/'))) {
        if ((q=strchr(p+1,':'))) {
            *q='\0'; if (str) strcpy(str,q+1);
        }
        *p='\0'; if (mntpnt) strcpy(mntpnt,p+1);
    }
    if ((p=strrchr(buff,'@'))) {
        *p++='\0';
        if ((q=strchr(buff,':'))) {
             *q='\0'; if (passwd) strcpy(passwd,q+1);
        }
        if (user) strcpy(user,buff);
    }
    else p=buff;
    
    if ((q=strchr(p,':'))) {
        *q='\0'; if (port) strcpy(port,q+1);
    }
    if (addr) strcpy(addr,p);
}

/* generate udp socket -------------------------------------------------------*/
static udp_t *genudp(int type, int port, const char *saddr, char *msg)
{
    udp_t *udp;
    struct hostent *hp;
    int bs=buffsize,opt=1;
#ifdef WIN32
    int tv = 0;
#else
    struct timeval tv;
    tv.tv_sec  = 2;
    tv.tv_usec = 0;
#endif
    
    if (!(udp=(udp_t *)malloc(sizeof(udp_t)))) return NULL;
    udp->state=2;
    udp->type=type;
    udp->port=port;
    strcpy(udp->saddr,saddr);
    
    if ((udp->sock=socket(AF_INET,SOCK_DGRAM,0))==(socket_t)-1) {
        sprintf(msg,"socket error (%d)",errsock());
        return NULL;
    }
    
    // 设置超时时间，禁止超时。缺省情况下，两个超时都是禁止的。
    if (setsockopt(udp->sock,SOL_SOCKET,SO_RCVTIMEO,(const char *)&tv,sizeof(tv))==-1||
        setsockopt(udp->sock,SOL_SOCKET,SO_SNDTIMEO,(const char *)&tv,sizeof(tv))==-1) {
        sprintf(msg,"sockopt error: notimeo");
        closesocket(udp->sock);
        return 0;
    }
    
    if (setsockopt(udp->sock,SOL_SOCKET,SO_RCVBUF,(const char *)&bs,sizeof(bs))==-1||
        setsockopt(udp->sock,SOL_SOCKET,SO_SNDBUF,(const char *)&bs,sizeof(bs))==-1) {
        sprintf(msg,"sockopt error: bufsiz");
    }
    memset(&udp->addr,0,sizeof(udp->addr));
    udp->addr.sin_family=AF_INET;
    udp->addr.sin_port=htons(port);
    
    if (!udp->type) { /* udp server */
        udp->addr.sin_addr.s_addr=htonl(INADDR_ANY);
#ifdef SVR_REUSEADDR
        setsockopt(udp->sock,SOL_SOCKET,SO_REUSEADDR,(const char *)&opt, sizeof(opt));
#endif
        if (bind(udp->sock,(struct sockaddr *)&udp->addr,sizeof(udp->addr))==-1) {
            sprintf(msg,"bind error (%d): %d",errsock(),port);
            closesocket(udp->sock);
            free(udp);
            return NULL;
        }
    }
    else { /* udp client */
        if (!strcmp(saddr,"255.255.255.255")&&
            setsockopt(udp->sock,SOL_SOCKET,SO_BROADCAST,(const char *)&opt,
                       sizeof(opt))==-1) {
            sprintf(msg,"sockopt error: broadcast");
        }
        if (!(hp=gethostbyname(saddr))) {
            sprintf(msg,"address error (%s)",saddr);
            closesocket(udp->sock);
            free(udp);
            return NULL;
        }
        memcpy(&udp->addr.sin_addr,hp->h_addr,hp->h_length);
    }
    return udp;
}

/* open udp server -----------------------------------------------------------*/
extern udp_t *openudpsvr(const char *path, char *msg)
{
    char sport[256]="";
    int port;
    
    decodeudppath(path,NULL,sport,NULL,NULL,NULL,NULL);
    if (sscanf(sport,"%d",&port)<1) {
        sprintf(msg,"port error: %s",sport);
        return NULL;
    }
    
    return genudp(0, port, "", msg);
}

/* close udp server ----------------------------------------------------------*/
extern void closeudpsvr(udp_t *udpsvr)
{
    if (udpsvr == NULL)
        return;
        
    closesocket(udpsvr->sock);
    free(udpsvr);
}

/* read udp server -----------------------------------------------------------*/
extern int readudpsvr(udp_t *udpsvr, unsigned char *buff, int n, char *msg)
{
    int    nr;
#if 0
    struct timeval tv={0};
    fd_set rs;
    int    ret;
    
    FD_ZERO(&rs); FD_SET(udpsvr->sock,&rs);
    ret=select(udpsvr->sock+1,&rs,NULL,NULL,&tv);
    if (ret<=0) return ret;
    nr=recvfrom(udpsvr->sock,(char *)buff,n,0,NULL,NULL);
#else
    nr=recvfrom(udpsvr->sock,(char *)buff,n,0,NULL,NULL);
#endif
    return nr<=0?-1:nr;
}

/* get state udp server ------------------------------------------------------*/
extern int stateudpsvr(udp_t *udpsvr)
{
    return udpsvr?udpsvr->state:0;
}

/* get extended state udp server ---------------------------------------------*/
extern int statexudpsvr(udp_t *udpsvr, char *msg)
{
    char *p=msg;
    int state=udpsvr?udpsvr->state:0;
    
    p+=sprintf(p,"udpsvr:\n");
    p+=sprintf(p,"  state   = %d\n",state);
    if (!state) return 0;
    p+=sprintf(p,"  type    = %d\n",udpsvr->type);
    p+=sprintf(p,"  sock    = %d\n",(int)udpsvr->sock);
    p+=sprintf(p,"  port    = %d\n",udpsvr->port);
    return state;
}

/* open udp client -----------------------------------------------------------*/
extern udp_t *openudpcli(const char *path, char *msg)
{
    char sport[256]="",saddr[256]="";
    int port;
    
    decodeudppath(path,saddr,sport,NULL,NULL,NULL,NULL);
    if (sscanf(sport,"%d",&port)<1) {
        sprintf(msg,"port error: %s",sport);
        return NULL;
    }
    return genudp(1, port, saddr, msg);
}

/* close udp client ----------------------------------------------------------*/
extern void closeudpcli(udp_t *udpcli)
{
    if (udpcli == NULL)
        return;
    
    closesocket(udpcli->sock);
    free(udpcli);
}

/* write udp client -----------------------------------------------------------*/
extern int writeudpcli(udp_t *udpcli, unsigned char *buff, int n, char *msg)
{
    return (int)sendto(udpcli->sock,(char *)buff,n,0,
                       (struct sockaddr *)&udpcli->addr,sizeof(udpcli->addr));
}

/* get state udp client ------------------------------------------------------*/
extern int stateudpcli(udp_t *udpcli)
{
    return udpcli?udpcli->state:0;
}

/* get extended state udp client ---------------------------------------------*/
extern int statexudpcli(udp_t *udpcli, char *msg)
{
    char *p=msg;
    int state=udpcli?udpcli->state:0;
    
    p+=sprintf(p,"udpsvr:\n");
    p+=sprintf(p,"  state   = %d\n",state);
    if (!state) return 0;
    p+=sprintf(p,"  type    = %d\n",udpcli->type);
    p+=sprintf(p,"  sock    = %d\n",(int)udpcli->sock);
    p+=sprintf(p,"  addr    = %s\n",udpcli->saddr);
    p+=sprintf(p,"  port    = %d\n",udpcli->port);
    return state;
}

