#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include "myc_err.h"
#include "myc_str.h"
#include "coc_util.h"
#include "uselect.h"
/*-------------------------------------------------------------------------*/
/* CreateSocketAddress stolen from Tcl. Thanks to John Ousterhout */

int CocCreateSockAdr(
    struct sockaddr_in *sockaddrPtr,    /* Socket address */
    const char *host,                   /* Host.  NULL implies INADDR_ANY */
    int port)                           /* Port number */
{
    struct hostent *hostent;            /* Host database entry */
    struct in_addr addr;                /* For 64/32 bit madness */

    (void) memset((char *) sockaddrPtr, '\0', sizeof(struct sockaddr_in));
    sockaddrPtr->sin_family = AF_INET;
    sockaddrPtr->sin_port = htons((unsigned short) (port & 0xFFFF));
    if (host == NULL || host[0]=='\0') {
        addr.s_addr = INADDR_ANY;
    } else {
        hostent = gethostbyname(host);
        if (hostent != NULL) {
            memcpy((char *) &addr,
                (char *) hostent->h_addr_list[0], (size_t) hostent->h_length);
        } else {
            addr.s_addr = inet_addr(host);
            if (addr.s_addr == (unsigned long)-1) {
                return -1;       /* error */
            }
        }
    }
    /*
     * There is a rumor that this assignment may require care on
     * some 64 bit machines.
     */

    sockaddrPtr->sin_addr.s_addr = addr.s_addr;
    return 0;
}

/*-------------------------------------------------------------------------*/

void CocDelay(int msec) {
  struct timeval tmo;

  tmo.tv_sec=msec / 1000;
  tmo.tv_usec=(msec % 1000)*1000+1;
  uselect(1,NULL,NULL,NULL,&tmo);
}

/*-------------------------------------------------------------------------*/

int CocSend(int fd, char *str, int size) {
  int siz;
  siz=htonl(size);
  ERR_SI(send(fd, &siz, 4, 0));
  ERR_SI(send(fd, str, size, 0));
  return(0);
  OnError: return(-1);
}

/*-------------------------------------------------------------------------*/

int CocRecv(int fd, StrBuf *buf, int timeout, int *flag) {
  struct timeval tmo={0,1};
  fd_set mask;
  int i, l;
  int siz, n;
  
  if (timeout>=0) {
    tmo.tv_sec=timeout;

    FD_ZERO(&mask);
    FD_SET(fd, &mask);
    ERR_SI(i=uselect(fd+1,&mask,NULL,NULL,&tmo));
    if (flag!=NULL) {
      *flag=0;
      if (i==0) return(0);
    } else {
      if (i==0) { ERR_MSG("time out"); }
    }
  }

  n=0;
  ERR_SI(i=recv(fd, &n, 4, 0));
  if (i!=4) {
    ERR_COD(ECONNREFUSED);
  }
  siz=ntohl(n);
  if (siz > buf->dsize)
      ERR_MSG("buffer too small");
  ERR_SI(l=recv(fd, buf->buf, siz, 0));
  buf->wrpos=l;
  while (buf->wrpos<siz) {
    if (l==0) {
      ERR_COD(ECONNRESET);
    }
    ERR_SI(l=recv(fd, buf->buf+buf->wrpos, siz, 0));
    buf->wrpos+=l;
  }
  StrReset(buf);
  return(buf->wrpos);
  OnError: return(-1);
}

