#include <unistd.h>
#include "fmt.h"
#include "buffer.h"
#include "ip.h"
#include "socket_if.h"
#include "error.h"
#include "iopause.h"
#include "timeoutconn.h"
#include "dnsresolv.h"
#include "remoteinfo.h"

#define TIMEOUT 113

static struct taia now;
static struct taia deadline;

static int mywrite(int fd,char *buf,int len)
{
  iopause_fd x;
  int r;

  x.fd = fd;
  x.events = IOPAUSE_WRITE;
  for (;;) {
    taia_now(&now);
    r = iopause(&x,1,&deadline,&now);
    if (r <= 0) break;
    if (x.revents) break;
    if (taia_less(&deadline,&now)) {
      errno = ETIMEDOUT;
      return -1;
    }
  }
  return write(fd,buf,len);
}

static int myread(int fd,char *buf,int len)
{
  iopause_fd x;
  int r;

  x.fd = fd;
  x.events = IOPAUSE_READ;
  for (;;) {
    taia_now(&now);
    r = iopause(&x,1,&deadline,&now);
    if (r <= 0) break;
    if (x.revents) break;
    if (taia_less(&deadline,&now)) {
      errno = ETIMEDOUT;
      return -1;
    }
  }
  return read(fd,buf,len);
}

static int doit(stralloc *out,int s,char ipremote[16],uint16 portremote,char iplocal[16],uint16 portlocal,unsigned int timeout,uint32 netif)
{
  buffer b;
  char bspace[128];
  char strnum[FMT_ULONG];
  int numcolons;
  char ch;

  if (ip6_isv4mapped(ipremote)) {
    if (socket_bind4(s,iplocal,0) == -1) return -1;
    if (timeoutconn4(s,ipremote,TIMEOUT,timeout) == -1) return -1;
  } else {
    if (socket_bind(s,iplocal,0,netif) == -1) return -1;
    if (timeoutconn(s,ipremote,TIMEOUT,timeout,netif) == -1) return -1;
  }
    

  buffer_init(&b,(ssize_t (*)())mywrite,s,bspace,sizeof(bspace));
  buffer_put(&b,strnum,fmt_ulong(strnum,portremote));
  buffer_put(&b," , ",3);
  buffer_put(&b,strnum,fmt_ulong(strnum,portlocal));
  buffer_put(&b,"\r\n",2);
  if (buffer_flush(&b) == -1) return -1;

  buffer_init(&b,(ssize_t (*)())myread,s,bspace,sizeof(bspace));
  numcolons = 0;
  for (;;) {
    if (buffer_get(&b,&ch,1) != 1) return -1;
    if ((ch == ' ') || (ch == '\t') || (ch == '\r')) continue;
    if (ch == '\n') return 0;
    if (numcolons < 3) {
      if (ch == ':') ++numcolons;
    }
    else {
      if (!stralloc_append(out,&ch)) return -1;
      if (out->len > 256) return 0;
    }
  }
}

int remoteinfo(stralloc *out,char ipremote[16],uint16 portremote,char iplocal[16],uint16 portlocal,unsigned int timeout,uint32 netif)
{
  int s;
  int r;

  if (!stralloc_copys(out,"")) return -1;

  taia_now(&now);
  taia_uint(&deadline,timeout);
  taia_add(&deadline,&now,&deadline);

  s = socket_tcp();
  if (s == -1) return -1;
  r = doit(out,s,ipremote,portremote,iplocal,portlocal,timeout,netif);
  close(s);
  return r;
}
