#ifdef USE_LIBPCAP
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pcap/pcap.h>

#include "pktlib.h"
#include "lib.h"

#define BUFFER_SIZE 65536

struct _pktif {
  struct _pktif_base base; /* 共通領域は先頭に置く必要がある */
  struct { /* インターフェースのlibpcap依存部分 */
    pcap_t *pcap_handle;
    char errbuf[PCAP_ERRBUF_SIZE];
#ifdef USE_WINPCAP
    char *ifname;
#endif
  } libpcap;
};

#ifdef USE_WINPCAP
static char *interface_search(pktif_t pktif, char *ifname)
{
  pcap_if_t *alldevs;
  pcap_if_t *d;
  int i, n = -1;

  if (ifname[0] != '-') n = atoi(ifname);

  if (pcap_findalldevs(&alldevs, pktif->libpcap.errbuf) < 0)
    pktlib_error_exit("Cannot find devices.\n");

  if (n < 0) {
    fprintf(stderr, "Available interface list:\n");
  }

  pktif->libpcap.ifname = NULL;
  i = 0;
  for (d = alldevs; d; d = d->next) {
    if (n < 0) {
      fprintf(stderr, "\t%d\t%s\n", i,
	      d->description ? d->description : "No description");
    } else {
      if (i == n) {
	pktif->libpcap.ifname = strdup(d->name);
	break;
      }
    }
    i++;
  }

  pcap_freealldevs(alldevs);

  return pktif->libpcap.ifname;
}
#endif

pktif_t pktif_open(char *ifname, unsigned long flags, int option_size)
{
  pktif_t pktif;
  int size, fd, linktype, buffer_size = BUFFER_SIZE;
  pcap_t *pcap_handle;

  size = sizeof(*pktif) + option_size + strlen(ifname) + 1;
  pktif = malloc(size);
  memset(pktif, 0, size);

#ifdef USE_WINPCAP
  ifname = interface_search(pktif, ifname);
  if (ifname == NULL)
    pktlib_error_exit("Unknown interface.\n");
#endif

  pcap_handle = pcap_open_live(ifname, buffer_size,
			       !(flags & PKTIF_OPEN_FLAG_RECV_NOTPROM) ? 1 : 0,
			       50, pktif->libpcap.errbuf);
  if (pcap_handle == NULL)
    pktlib_error_exit("Cannot open libpcap.\n");

  /* 情報の取得 */
#ifndef USE_WINPCAP
  fd = pcap_get_selectable_fd(pcap_handle);
#else
  fd = -1;
  flags |= PKTIF_OPEN_FLAG_SELECT_DISABLE;
#endif
  if (fd < 0)
    flags |= PKTIF_OPEN_FLAG_SELECT_NOSELECT;

  /* 受信関連の設定 */
#ifndef USE_WINPCAP
  if (flags & PKTIF_OPEN_FLAG_RECV_NOTSENT) {
    if (pcap_setdirection(pcap_handle, PCAP_D_IN) < 0)
      pktlib_error_exit("Fail to libpcap setdirection.\n");
  }
#endif

  /* リンクタイプの取得 */
  linktype = pcap_datalink(pcap_handle);

  pktif->base.next = NULL;
  pktif->base.name = (char *)(pktif + 1) + option_size;
  strcpy(pktif->base.name, ifname);
  pktif->base.fd = fd;
  pktif->base.flags = flags;
  pktif->base.linktype = linktype;
  pktif->base.buffer_size = buffer_size;
  pktif->base.option = (option_size > 0) ? (pktif + 1) : NULL;
  pktif->libpcap.pcap_handle = pcap_handle;

  pktlib_iflist_set(pktif); /* select()のためにリンクリストに接続する */

  return pktif;
}

int pktif_is_empty(pktif_t pktif)
{
  return 1; /* BPFとの整合性のための関数．常に１を返す */
}

struct recv_userdata {
  struct pcap_pkthdr *header;
  const unsigned char **buffer;
};

static void recv_proc(unsigned char *user,
		      const struct pcap_pkthdr *header,
		      const unsigned char *buffer)
{
  struct recv_userdata *rud = (struct recv_userdata *)user;
  memcpy(rud->header, header, sizeof(*(rud->header)));
  *(rud->buffer) = buffer;
}

int pktif_recv(pktif_t pktif, char *buffer, int size, struct timeval *tm)
{
  int r;
  const unsigned char *pktbuf;
  struct pcap_pkthdr header;
  struct recv_userdata rud;

  rud.header = &header;
  rud.buffer = &pktbuf;
  r = pcap_loop(pktif->libpcap.pcap_handle, 1, recv_proc,
		(unsigned char *)&rud);
  if (r < 0)
    pktlib_error_exit("Interface down.\n");

  if (tm) {
    tm->tv_sec  = header.ts.tv_sec;
    tm->tv_usec = header.ts.tv_usec;
  }
  r = header.caplen;
  if (r >= size)
    pktlib_error_exit("Out of buffer.\n");
  memcpy(buffer, pktbuf, r);

  return r;
}

int pktif_send(pktif_t pktif, char *buffer, int size)
{
  int r;
  r = pcap_sendpacket(pktif->libpcap.pcap_handle,
		      (unsigned char *)buffer, size);
  return (r < 0) ? -1 : size;
}
#endif
