#include <stdio.h>
#include <pcap.h>
#include <sys/time.h>
#include <signal.h>
#include <string.h>

#include "kernel.h"
#include "las_pcap_recv.h"

#define DEFAULT_SNAPLEN 1500

#define mask(n) ((1<<n)-1)

int unpack_it(unsigned char *pack, u_int32_t psize, u_int32_t *info, u_int16_t off, u_int16_t size){
  u_int16_t myword = off/32;
  u_int8_t  start_bit= off%32;
  u_int32_t *wrk = (u_int32_t *)pack;
  u_int32_t wrk_var; 

  if(psize < (off/8 + size/8)){
    printf("Packet limit violation.\nAborting...");
    exit(0);
  }
  
  if(myword>psize){
    *info = 0;
    return -1;
  }
  
  wrk_var = wrk[myword];
  if(size < 32){
    wrk_var &= (mask(size)<<(start_bit));
    wrk_var >>= start_bit;
  }
  memcpy(info,&wrk_var,sizeof(u_int32_t));

  return 0;
}

u_int32_t calc_off(rpack,psize,layer,pheaders)
unsigned char			*rpack;		/* Received packet  */
u_int32_t			psize;		/* Received packet size */
u_int16_t 			layer;		/* The layer we want to get the offset*/
struct phinf			**pheaders;	/* Previous headers */
{
  int 		i;
  u_int32_t	p_offset=0,tmp;
  
  for(i=0;i<layer-1;i++){
    unpack_it(rpack,psize,&tmp,pheaders[i]->off+p_offset,pheaders[i]->size);
    tmp <<= 2 + 3; /* 2bit shift + x8 bits (bit offset)  */
    p_offset += tmp;
  }
  return p_offset;
}

static int	keep_looping; /* Timeout ? */

void stop_looping(){
  keep_looping = 0;
  signal(SIGALRM,SIG_DFL);
}

u_int32_t packet_receiver(head_hash, noh,recv)
struct header_hash_table      	**head_hash;	/* Header definition hash table
                                               	 */
u_int16_t			noh;		/* Number of headers */
struct recv			*recv;		/* Branches */
{
  struct pcap_pkthdr    head;		/* Pcap information */
  char			*pack;
  int 			i,j,thats_it,retval,notimeout;	
  unsigned int		rpinfo;		/* Received packet info */
  u_int32_t		psize;		/* Received packet size */
  u_int32_t		layer_off;	/* layer offset */
  struct itimerval 	ti;		/* Timer structure */
  fd_set		rfds; 		/* Read File Descriptor Set */
  struct timeval	tv;
  pcap_t		*pd;		/* Pcap descriptor  */
  u_int32_t		offset;		/* layer 1 offset   */
  struct pcap_chg {
    int fd;
  }			*pdchg;		/* Shame... */
  
  pd = init_pcap(recv->iface,recv->bpf,&offset); 
  
  if(!pd || !recv){
    printf("Huston we have a problem!\n");
    exit(0);
  }
  memset(&ti,0,sizeof(ti));
  memset(&tv,0,sizeof(tv));
  pdchg = (struct pcap_chg*)pd;
  
  FD_ZERO(&rfds);
  FD_SET(pdchg->fd, &rfds);
 
  notimeout = (recv->to.time == 0);

  if(!notimeout){
    ti.it_value.tv_sec  = recv->to.time/1000000;
    ti.it_value.tv_usec = recv->to.time%1000000;
    signal(SIGALRM,stop_looping);
    setitimer(ITIMER_REAL,&ti,NULL);
  }

  keep_looping = 1;
  while(keep_looping){
    if(notimeout){
      retval = select(pdchg->fd+1,&rfds,NULL,NULL,NULL);
    }else{
      getitimer(ITIMER_REAL,&ti);
      memcpy(&tv,&(ti.it_value),sizeof(tv));
      retval = select(pdchg->fd+1,&rfds,NULL,NULL,&tv);
     }
   
    if((retval > 0) && FD_ISSET(pdchg->fd,&rfds)){
      pack  = (char*)(pcap_next(pd, &head));
    }else{
      continue;
    }

    if(!pack) continue;

    pack = (pack + offset);
    psize = head.len - offset;
// *****************   Decision Area  *******************************************
    for(i=0;i<recv->brnch_struct->nob;i++){
      thats_it = 1;
      for(j=0;j<recv->brnch_struct->fld_struct[i]->nof;j++){
        if(recv->brnch_struct->fld_struct[i]->fld_vct[j].layer > 1){
          layer_off = calc_off(pack,\
			       psize,\
			       recv->brnch_struct->fld_struct[i]->fld_vct[j].layer,\
			       recv->brnch_struct->fld_struct[i]->fld_vct[j].pheaders,\
			       head_hash,\
			       noh);
	}else
	   layer_off = 0;
        if( unpack_it(pack,\
		  psize,\
		  &rpinfo,\
		  recv->brnch_struct->fld_struct[i]->fld_vct[j].offset+layer_off,\
		  recv->brnch_struct->fld_struct[i]->fld_vct[j].size) < 0 ) continue;
/*	printf("[%d]->off:(%u) siz:(%u) inf:(%u) ",i\
						  ,dpkts->psts_vct[i].fld_vct[j].offset\
						  ,dpkts->psts_vct[i].fld_vct[j].size\
						  ,dpkts->psts_vct[i].fld_vct[j].info);
	printf("received :(%u)\n",rpinfo);
*/      thats_it &= (recv->brnch_struct->fld_struct[i]->fld_vct[j].info == rpinfo);
      }
      if(thats_it){
        recv->plen = head.len - offset;
	if(recv->recvpack != NULL)
	  free(recv->recvpack);
        recv->recvpack = (u_char*)malloc(recv->plen);
        memcpy(recv->recvpack,pack,recv->plen);
	pcap_close(pd);
        return recv->brnch_struct->fld_struct[i]->next_state;
      }
    }
// ******************************************************************************
  }
  pack = NULL;
  recv->plen = 0;
  recv->recvpack = NULL;
  pcap_close(pd);
  return recv->to.nxt;
}

char ebuf[PCAP_ERRBUF_SIZE];

pcap_t *init_pcap(char *device, char *filter_opts, int *lnk_off){
  int                   snaplen = DEFAULT_SNAPLEN;
  int                   promisc = 1;
  pcap_t                *pd;
  bpf_u_int32    	localnet, netmask;
  struct bpf_program    fcode;
  int			datalink;
  int			offset = 0;

  pd = pcap_open_live(device, snaplen, promisc, 1000, ebuf);
  if(pd == NULL){
    printf("Error opening PCAP descriptor.\n");
    exit(0);
  }
  pcap_lookupnet(device, &localnet, &netmask, ebuf);
  pcap_compile(pd, &fcode, filter_opts, 0, netmask);
  pcap_setfilter(pd, &fcode);
  if((datalink = pcap_datalink(pd)) < 0){
    printf("Ooops!\n");
    pcap_close(pd);
    return NULL;
  }else{
      switch(datalink) {
 	case DLT_EN10MB: 
	  offset = 14;
          break;
 	case DLT_IEEE802:
	  offset = 22;
	  break;

 	case DLT_PPP:

#ifdef DLT_PPP_BSDOS
 	case DLT_PPP_BSDOS:
#endif

#if (FREEBSD || OPENBSD || NETBSD || BSDI)
	  offset = 4;
#else

#ifdef SOLARIS
	  offset = 8;
#else
	  offset = 24;
#endif /* ifdef solaris */
#endif /* if freebsd || openbsd || netbsd || bsdi */
	  break;

	case DLT_RAW:
	  offset = 0;
 	  break;

 	default: 
          printf("Unknown datalink type (%d)\n", datalink);
      }
    }
  *lnk_off = offset;
  return pd;
}
