 
#define NEED_PACKRECORD 1 

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

//#include "libnf.h"
#include "libnf_perl.h"

#define MATH_INT64_NATIVE_IF_AVAILABLE 1
#include "../perl_math_int64.h"

//#include "config.h"

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdarg.h>
#include <errno.h>
#include <time.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/resource.h>
#include <netinet/in.h>

#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <errno.h>


#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif


/* Ignore Math-Int64 on 64 bit platform */
/* #define MATH_INT64_NATIVE 1 */

#if MATH_INT64_NATIVE
#undef newSVu64
#define newSVu64 newSVuv
#undef SvU64 
#define SvU64 SvUV
#endif

/* defining macros for storing numbers, 64 bit numbers and strings into hash */
#define HV_STORE_NV(r,k,v) (void)hv_store(r, k, strlen(k), newSVnv(v), 0)
#define HV_STORE_U64V(r,k,v) (void)hv_store(r, k, strlen(k), newSVu64(v), 0)
#define HV_STORE_PV(r,k,v) (void)hv_store(r, k, strlen(k), newSVpvn(v, strlen(v)), 0)

/* list of maps used in file taht we create */
typedef struct libnf_file_list_s {
	char			 			*filename;
	struct libnf_file_list_s 	*next;
} libnf_file_list_t;

/* structure that bears all data related to one instance */
typedef struct libnf_instance_s {
	libnf_file_list_t		*files;					/* list of files to read */
	lnf_file_t				*lnf_nffile_r;			/* filehandle for reading */
	lnf_file_t				*lnf_nffile_w;			/* filehandle for wirting */
	int 					blk_record_remains; 	/* counter of processed rows in a signle block */
	lnf_filter_t			*filter;
	int						*field_list;
	int						field_last;
	uint64_t				processed_bytes;		/* read statistics */
	uint64_t				total_files;
	uint64_t				processed_files;
	uint64_t				processed_blocks;
	uint64_t				skipped_blocks;
	uint64_t				processed_records;
	char 					*current_filename;		/* currently processed file name */
	uint64_t				current_processed_blocks;
	time_t 					t_first_flow, t_last_flow;
	time_t					twin_start, twin_end;
	lnf_mem_t				*lnf_mem;				/* lnf_mem - aggregated/sorted results */
	lnf_rec_t				*lnf_rec;
} libnf_instance_t;


/* array of initalized instances */
libnf_instance_t *libnf_instances[NFL_MAX_INSTANCES] = { NULL };

// compare at most 16 chars
#define MAXMODELEN	16	

#define STRINGSIZE 10240
#define IP_STRING_LEN (INET6_ADDRSTRLEN)

/***********************************************************************
*                                                                      *
* functions and macros for converting data types to perl's SV and back *
*                                                                      *
************************************************************************/

/* cinverts unsigned integer 32b. to SV */
static inline SV * uint_to_SV(uint32_t n, int is_defined) {

	if (!is_defined) 
		return newSV(0);

	return newSVuv(n);
}

/* converts unsigned integer 64b. to SV */
static inline SV * uint64_to_SV(uint64_t n, int is_defined) {

	if (!is_defined) 
		return newSV(0);

	return newSVu64(n);
}

/* converts unsigned integer 64b. to SV */
static inline SV * double_to_SV(double n, int is_defined) {

	if (!is_defined) 
		return newSV(0);

	return newSVnv(n);
}

/*
************************************************************************
*                                                                      *
* end of convertion functions                                          *
*                                                                      *
************************************************************************
*/


/* returns the information about file get from file header */
SV * libnf_file_info(char *file) {
HV *res;
lnf_file_t *f;
char buf[LNF_INFO_BUFSIZE];

	res = (HV *)sv_2mortal((SV *)newHV());

	if (lnf_open(&f, file, LNF_READ, NULL) != LNF_OK) {
		return NULL;
	}

	if (lnf_info(f, LNF_INFO_VERSION, buf, LNF_INFO_BUFSIZE) == LNF_OK) 
		HV_STORE_PV(res, "version", buf);

	if (lnf_info(f, LNF_INFO_NFDUMP_VERSION, buf, LNF_INFO_BUFSIZE) == LNF_OK) 
		HV_STORE_PV(res, "nfdump_version", buf);

	if (lnf_info(f, LNF_INFO_BLOCKS, buf, LNF_INFO_BUFSIZE) == LNF_OK) 
		HV_STORE_NV(res, "blocks", *(uint64_t *)buf);

	if (lnf_info(f, LNF_INFO_COMPRESSED, buf, LNF_INFO_BUFSIZE) == LNF_OK) 
		HV_STORE_NV(res, "compressed", *(int *)buf);

	if (lnf_info(f, LNF_INFO_ANONYMIZED, buf, LNF_INFO_BUFSIZE) == LNF_OK) 
		HV_STORE_NV(res, "anonymized", *(int *)buf);

	if (lnf_info(f, LNF_INFO_CATALOG, buf, LNF_INFO_BUFSIZE) == LNF_OK) 
		HV_STORE_NV(res, "catalog", *(int *)buf);

	if (lnf_info(f, LNF_INFO_IDENT, buf, LNF_INFO_BUFSIZE) == LNF_OK) 
		HV_STORE_PV(res, "ident", buf);

	if (lnf_info(f, LNF_INFO_FLOWS, buf, LNF_INFO_BUFSIZE) == LNF_OK) 
		HV_STORE_NV(res, "flows", *(uint64_t *)buf);

	if (lnf_info(f, LNF_INFO_BYTES, buf, LNF_INFO_BUFSIZE) == LNF_OK) 
		HV_STORE_NV(res, "bytes", *(uint64_t *)buf);
		
	if (lnf_info(f, LNF_INFO_PACKETS, buf, LNF_INFO_BUFSIZE) == LNF_OK) 
		HV_STORE_NV(res, "packets", *(uint64_t *)buf);

	if (lnf_info(f, LNF_INFO_FIRST, buf, LNF_INFO_BUFSIZE) == LNF_OK) 
		HV_STORE_NV(res, "first", *(uint64_t *)buf);

	if (lnf_info(f, LNF_INFO_LAST, buf, LNF_INFO_BUFSIZE) == LNF_OK) 
		HV_STORE_NV(res, "last", *(uint64_t *)buf);

	if (lnf_info(f, LNF_INFO_FAILURES, buf, LNF_INFO_BUFSIZE) == LNF_OK) 
		HV_STORE_NV(res, "sequence_failures", *(uint64_t *)buf);

	lnf_close(f);	
	
	return newRV((SV *)res);
}

/* returns the information about instance */
SV * libnf_instance_info(int handle) {
libnf_instance_t *instance = libnf_instances[handle];
HV *res;
lnf_file_t *f;
char buf[LNF_INFO_BUFSIZE];
//lnf_info_t i;

	if (libnf_instances[handle] == NULL) {
		croak("%s handler %d not initialized", NFL_LOG, handle);
		return NULL;
	}

	res = (HV *)sv_2mortal((SV *)newHV());

	if (instance->lnf_nffile_r == NULL) {
		return NULL;
	}

	f = instance->lnf_nffile_r;

	if ( f != NULL ) {
		if (lnf_info(f, LNF_INFO_PROC_BLOCKS, buf, LNF_INFO_BUFSIZE) == LNF_OK)
			HV_STORE_NV(res, "current_processed_blocks", *(uint64_t *)buf);

		if (lnf_info(f, LNF_INFO_PROC_BLOCKS, buf, LNF_INFO_BUFSIZE) == LNF_OK) {
			uint64_t b = instance->processed_blocks + *(uint64_t *)buf;
			HV_STORE_NV(res, "processed_blocks", b);
		}

		if (lnf_info(f, LNF_INFO_BLOCKS, buf, LNF_INFO_BUFSIZE) == LNF_OK)
			HV_STORE_NV(res, "current_total_blocks", *(uint64_t *)buf);

		HV_STORE_NV(res, "total_files", instance->total_files);
		HV_STORE_NV(res, "processed_files", instance->processed_files);

	}
/*
	HV_STORE_NV(res, "processed_blocks", instance->processed_blocks);
	HV_STORE_NV(res, "processed_bytes", instance->processed_bytes);
	HV_STORE_NV(res, "processed_records", instance->processed_records);
*/

	return newRV((SV *)res);
}

/* converts master_record to perl structures (hashref) */
SV * libnf_master_record_to_AV(int handle, lnf_rec_t *lnf_rec) {
libnf_instance_t *instance = libnf_instances[handle];
AV *res_array;
int i=0;
int ret;

	if (instance == NULL ) {
		croak("%s handler %d not initialized", NFL_LOG, handle);
		return 0;
	}
	
	res_array = (AV *)sv_2mortal((SV *)newAV());

	i = 0;
	while ( instance->field_list[i] ) {
		SV * sv;
		int field = instance->field_list[i];

		switch (lnf_fld_type(field)) {
			case LNF_UINT8: {
                uint8_t t8 = 0;
                ret = lnf_rec_fget(lnf_rec, field, (void *)&t8);
				sv = uint_to_SV(t8, ret == LNF_OK);
                break;
            }
			case LNF_UINT16: {
                uint16_t t16 = 0;
                ret = lnf_rec_fget(lnf_rec, field, (void *)&t16);
				sv = uint_to_SV(t16, ret == LNF_OK);
                break;
            }
			case LNF_UINT32: {
                uint32_t t32 = 0;
                ret = lnf_rec_fget(lnf_rec, field, (void *)&t32);
				sv = uint_to_SV(t32, ret == LNF_OK);
                break;
            }
            case LNF_UINT64: {
                uint64_t t64 = 0;
                ret = lnf_rec_fget(lnf_rec, field, (void *)&t64);
				sv = uint64_to_SV(t64, ret == LNF_OK);
                break;
			}
            case LNF_DOUBLE: {
                double d = 0;
                ret = lnf_rec_fget(lnf_rec, field, (void *)&d);
				sv = double_to_SV(d, ret == LNF_OK);
                break;
			}
			case LNF_ADDR: {
				lnf_ip_t tip;

                ret = lnf_rec_fget(lnf_rec, field, (void *)&tip);

				if (ret != LNF_OK) {
					sv = newSV(0);
				} else {
					if (IN6_IS_ADDR_V4COMPAT((struct in6_addr *)&tip)) {
						sv = newSVpvn((char *)&tip.data[3], sizeof(tip.data[3]));
					} else {
						sv = newSVpvn((char *)&tip, sizeof(tip));
					}
				}
				break;
			}
			case LNF_MAC: {
				lnf_mac_t tmac;

                ret = lnf_rec_fget(lnf_rec, field, (void *)&tmac);
				if (ret != LNF_OK) {
					sv = newSV(0);
				} else {
					sv = newSVpvn((char *)&tmac, sizeof(tmac));
				}
				break;
			}
			case LNF_MPLS: {
				lnf_mpls_t tmpls;

                ret = lnf_rec_fget(lnf_rec, field, (void *)&tmpls);
				if (ret != LNF_OK) {
					sv = newSV(0);
				} else {
					sv = newSVpvn((char *)&tmpls, sizeof(lnf_mpls_t));
				}
				break;
			}
			case LNF_STRING: {
				char buf[LNF_MAX_STRING];

                ret = lnf_rec_fget(lnf_rec, field, (void *)&buf);
				if (ret != LNF_OK) {
					sv = newSV(0);
				} else {
					sv = newSVpvn(buf, strlen(buf));
				}
				break;
			}
			case LNF_BASIC_RECORD1: 
				sv = newSV(0);	
				break;
			default: 
				croak("%s Unknown field (id %02x) in %s !!", NFL_LOG, field, __FUNCTION__);
		} /* case */

		i++;
		av_push(res_array, sv);	
	}

 
	return newRV((SV *)res_array);
}


int libnf_init(void) {
int handle = 1;
libnf_instance_t *instance;

	/* find the first free handler and assign to array of open handlers/instances */
	while (libnf_instances[handle] != NULL) {
		handle++;
		if (handle >= NFL_MAX_INSTANCES - 1) {
			croak("%s no free handles available, max instances %d reached", NFL_LOG, NFL_MAX_INSTANCES);
			return 0;	
		}
	}

	instance = malloc(sizeof(libnf_instance_t));
	memset(instance, 0, sizeof(libnf_instance_t));

	if (instance == NULL) {
		croak("%s can not allocate memory for instance:", NFL_LOG );
		return 0;
	}

	libnf_instances[handle] = instance;

	/* initialise empty record */	
	lnf_rec_init(&instance->lnf_rec);
	instance->lnf_mem = NULL;

	instance->files = NULL;

	instance->field_list = NULL;

	return handle;
}


int libnf_set_fields(int handle, SV *fields) {
libnf_instance_t *instance = libnf_instances[handle];
I32 last_field = 0;
int i;

	if (instance == NULL ) {
		croak("%s handler %d not initialized", NFL_LOG, handle);
		return 0;
	}

	if ((!SvROK(fields))
		|| (SvTYPE(SvRV(fields)) != SVt_PVAV) 
		|| ((last_field = av_len((AV *)SvRV(fields))) < 0)) {
			croak("%s can not determine the list of fields", NFL_LOG);
			return 0;
	}

	// release memory allocated before	
	if (instance->field_list != NULL) {
		free(instance->field_list);
	}

	// last_field contains the highet index of array ! - not number of items 
	instance->field_list = malloc(sizeof(int) * (last_field + 2));

	if (instance->field_list == NULL) {
		croak("%s can not allocate memory in %s", NFL_LOG, __FUNCTION__);
		return 0;
	}

	for (i = 0; i <= last_field; i++) {
		int field = SvIV(*av_fetch((AV *)SvRV(fields), i, 0));

		if (field != 0 || field > NFL_MAX_FIELDS) {	
			instance->field_list[i] = field;
		} else {
			warn("%s ivalid itemd ID", NFL_LOG);
		}
	}

	instance->field_list[i++] = LNF_FLD_ZERO_;
	instance->field_last = last_field;
	return 1;
}

int libnf_aggr_add(int handle, int field, int flags, int numbits, int numbits6 ) {
libnf_instance_t *instance = libnf_instances[handle];

	if (instance == NULL ) {
		croak("%s handler %d not initialized", NFL_LOG, handle);
		return 0;
	}

	if (instance->lnf_mem == NULL) {
		if (lnf_mem_init(&instance->lnf_mem) != LNF_OK ) {
			return 0;
		}
		/* first and last is always present */
		if (lnf_mem_fadd(instance->lnf_mem, LNF_FLD_FIRST, LNF_AGGR_MIN, 0, 0) != LNF_OK ) {
			return 0;
		}	
		if (lnf_mem_fadd(instance->lnf_mem, LNF_FLD_LAST, LNF_AGGR_MAX, 0, 0) != LNF_OK ) {
			return 0;
		}	
	}

	if (lnf_mem_fadd(instance->lnf_mem, field, flags, numbits, numbits6) != LNF_OK ) {
		return 0;
	}	

	return 1;
} 


int libnf_read_files(int handle, char *filter, int window_start, int window_end, SV *files) {
libnf_instance_t *instance = libnf_instances[handle];

libnf_file_list_t	*pfile;
lnf_rec_t	*lnf_rec;
I32 numfiles = 0;
int i;

	if (instance == NULL ) {
		croak("%s handler %d not initialized", NFL_LOG, handle);
		return 0;
	}

	/* copy files to the instance structure */
	if ((!SvROK(files))
		|| (SvTYPE(SvRV(files)) != SVt_PVAV) 
		|| ((numfiles = av_len((AV *)SvRV(files))) < 0)) {
			croak("%s can not determine the list of files", NFL_LOG);
			return 0;
	}

	pfile = malloc(sizeof(libnf_file_list_t));
	pfile->next = NULL;
	pfile->filename = NULL;
	instance->files = pfile;
	instance->total_files = numfiles + 1;
	instance->twin_start = window_start;
	instance->twin_end = window_end;
	instance->processed_blocks = 0;

	for (i = 0; i <= numfiles; i++) {
		STRLEN l;
		char * file = SvPV(*av_fetch((AV *)SvRV(files), i, 0), l);
		

		pfile->filename = malloc(l + 1);
		strcpy((char *)(pfile->filename), file);
		
		pfile->next = malloc(sizeof(libnf_file_list_t));
		pfile = pfile->next;
		pfile->filename = NULL;
		pfile->next = NULL;

	}
//	instance->nffile_r = NULL;
	instance->lnf_nffile_r = NULL;

	instance->filter = NULL;

	/* set filter */
	if (filter != NULL && strcmp(filter, "") != 0 && strcmp(filter, "any") != 0) {
		if ( lnf_filter_init(&instance->filter, filter) != LNF_OK ) {
			croak("%s can not setup filter (%s)", NFL_LOG, filter);
			return 0;
		}
	} 


	/* if aggregation is requested process all records and store in lnf_mem */
	if (instance->lnf_mem != NULL) {
		while ((lnf_rec = libnf_read_row_files(handle)) != NULL) {
			lnf_mem_write(instance->lnf_mem, lnf_rec);	
		}
	}

	return 1;
}


int libnf_create_file(int handle, char *filename, int compressed, int anonymized, char *ident) {
libnf_instance_t *instance = libnf_instances[handle];
int flags = 0;

	if (instance == NULL ) {
		croak("%s handler %d not initialized", NFL_LOG, handle);
		return 0;
	}


	/* the file was already opened */
	if (instance->lnf_nffile_w != NULL) {
		croak("%s file handler was opened before", NFL_LOG);
		return 0;
	}

	/* writing file */
	flags |= LNF_WRITE;
	flags |= compressed ? LNF_COMP  : 0x0;
	flags |= anonymized ? LNF_ANON  : 0x0;

    if ( lnf_open(&instance->lnf_nffile_w, filename, flags , ident) != LNF_OK ) {
		warn("%s cannot open file %s", NFL_LOG, filename);
		return 0;
    }

	return 1;
}

/* returns hashref or NULL if we are et the end of the file */
/* function is divided into two parts */
/* 1 - if the result is aggregated/sorted we read result from  mem object */
/* 2 - not agregated - call libnf_read_row_file */
SV * libnf_read_row(int handle) {
libnf_instance_t *instance = libnf_instances[handle];
//int ret;
//int match;
lnf_rec_t *lnf_rec;

	if (instance == NULL ) {
		croak("%s handler %d not initialized", NFL_LOG, handle);
		return 0;
	}

	if (instance->lnf_mem == NULL) {
		/* non aggregated result - read directly from file */
		lnf_rec = libnf_read_row_files(handle);

		if (lnf_rec != NULL) {
			return libnf_master_record_to_AV(handle, lnf_rec); 
		} else {
			return NULL;
		}

	} else {
		/* aggregated result - read from lnf_mem */

		if (lnf_mem_read(instance->lnf_mem, instance->lnf_rec) != LNF_EOF) {
			return libnf_master_record_to_AV(handle, instance->lnf_rec);
		} else {
			/* last record - clean lnf_mem object */
			lnf_mem_free(instance->lnf_mem);
			instance->lnf_mem = NULL;
			return NULL;
		}
	}

}


/* returns hashref or NULL if we are et the end of the file */
lnf_rec_t * libnf_read_row_files(int handle) {
libnf_instance_t *instance = libnf_instances[handle];
int ret;
int match;
uint64_t blocks;
lnf_rec_t *lnf_rec;

	if (instance == NULL ) {
		croak("%s handler %d not initialized", NFL_LOG, handle);
		return 0;
	}

#ifdef COMPAT15
int	v1_map_done = 0;
#endif

	lnf_rec = instance->lnf_rec;


begin:
		// get next data block from file
		if (instance->lnf_nffile_r) {
			ret = lnf_read(instance->lnf_nffile_r, lnf_rec);
		} else {
			ret = LNF_EOF;		/* the firt file in the list */
		}

		switch (ret) {
			case LNF_ERR_CORRUPT:
				croak("Skip corrupt data file '%s'\n", (char *)instance->files->filename);
				return NULL;
			case LNF_ERR_READ:
				croak("Read error in file '%s': %s\n", (char *)instance->files->filename, strerror(errno) );
				return NULL;
				// fall through - get next file in chain
			case LNF_EOF: {
				libnf_file_list_t *next;

				//CloseFile(instance->nffile_r);
				if (lnf_info(instance->lnf_nffile_r, LNF_INFO_BLOCKS, &blocks, sizeof(uint64_t)) == LNF_OK) {
					instance->processed_blocks += blocks;
				}
				lnf_close(instance->lnf_nffile_r);
				instance->lnf_nffile_r = NULL;
				if (instance->files->filename == NULL) {	// the end of the list 
					free(instance->files);
					instance->files = NULL;		
					return NULL;
				}
				//instance->nffile_r = OpenFile((char *)instance->files->filename, instance->nffile_r);
				if (lnf_open(&instance->lnf_nffile_r, (char *)instance->files->filename, LNF_READ, NULL) != LNF_OK) {
					croak("%s can not open/read file %s", NFL_LOG, instance->files->filename);
					return NULL;
				}
				instance->processed_files++;

				next = instance->files->next;

				/* prepare instance->files to nex unread file */
				if (instance->current_filename != NULL) {
					free(instance->current_filename);
				}

				instance->current_filename = instance->files->filename;
				free(instance->files);
				instance->files = next;

				goto begin;
			}

			default:
				// successfully read block
				instance->processed_bytes += ret;
	}

	// Time based filter
	// if no time filter is given, the result is always true

/*
	match  = instance->twin_start && (lnf_rec->master_record->first < instance->twin_start || 
						lnf_rec->master_record->last > instance->twin_end) ? 0 : 1;
*/
	/* to be FIXED XXX */
	match = 1;
	// filter netflow record with user supplied filter
//	instance->engine->nfrecord = (uint64_t *)lnf_rec.master_record;
	if ( instance->filter != NULL && match ) {
		match = lnf_filter_match(instance->filter, lnf_rec); 
	}

	if ( match == 0 ) { // record failed to pass all filters
		goto begin;
	}

	/* the record seems OK */
	return lnf_rec; 
//	return libnf_master_record_to_AV(handle, lnf_rec); 

} /* end of _next fnction */
                                  

/* copy row from the instance defined as the source handle to destination */
int libnf_copy_row(int handle, int src_handle) {
libnf_instance_t *instance = libnf_instances[handle];
libnf_instance_t *src_instance = libnf_instances[src_handle];

	if (instance == NULL ) {
		croak("%s handler %d not initialized", NFL_LOG, handle);
		return 0;
	}

	if (src_instance == NULL ) {
		croak("%s seource handler %d not initialized", NFL_LOG, handle);
		return 0;
	}

	if (!lnf_rec_copy(instance->lnf_rec, src_instance->lnf_rec) ) {
		return 0;
	} 

	return 1;

}

/* TAG for check_items_map.pl: libnf_write_row */
int libnf_write_row(int handle, SV * arrayref) {
libnf_instance_t *instance = libnf_instances[handle];
//extension_map_t *map;
//bit_array_t ext;
int last_field;
int i;
//res;
lnf_ip_t tip;

int field, ret;
lnf_rec_t *lnf_rec;

	if (instance == NULL ) {
		croak("%s handler %d not initialized", NFL_LOG, handle);
		return 0;
	}

	if ((!SvROK(arrayref))
		|| (SvTYPE(SvRV(arrayref)) != SVt_PVAV) 
		|| ((last_field = av_len((AV *)SvRV(arrayref))) < 0)) {
			croak("%s can not determine fields to store", NFL_LOG);
			return 0;
	}

	if (last_field != instance->field_last) {
		croak("%s number of fields do not match", NFL_LOG);
		return 0;
	}

	lnf_rec = instance->lnf_rec;

	i = 0;
	while ( instance->field_list[i] ) {

		SV * sv = (SV *)(*av_fetch((AV *)SvRV(arrayref), i, 0));

		if (!SvOK(sv)) {	// undef value 
			i++;
			continue;
		}

		field = instance->field_list[i];


		switch (lnf_fld_type(field)) {
			case LNF_UINT8:
			case LNF_UINT16:
			case LNF_UINT32: {
				uint32_t t32 = SvUV(sv);
				ret = lnf_rec_fset(lnf_rec, field, (void *)&t32);
				break;
			}
			case LNF_UINT64: {
				uint64_t t64 = SvU64(sv);
				ret = lnf_rec_fset(lnf_rec, field, (void *)&t64);
				break;
			}
			case LNF_DOUBLE: {
				double d = SvNV(sv);
				ret = lnf_rec_fset(lnf_rec, field, (void *)&d);
				break;
			}
			case LNF_ADDR: {
				char *s;
				STRLEN len;

			    s = SvPV(sv, len);

			    if ( len == sizeof(tip.data[3]) )  {
			        memset(&tip, 0x0, sizeof(tip));
			        memcpy(&tip.data[3], s, sizeof(tip.data[3]));
				} else if (len == sizeof(tip) ) {
			        memcpy(&tip, s, sizeof(tip));
				} else {
					warn("%s invalid IP address value for %d", NFL_LOG, field);
					return 0;
				}

				ret = lnf_rec_fset(lnf_rec, field, (void *)&tip);
				break;
			}
			case LNF_MAC: {
				char *s;
				STRLEN len;

				s = SvPV(sv, len);

				if ( len != sizeof(lnf_mac_t) ) {
					warn("%s invalid MAC address value for %d", NFL_LOG, field);
					return 0;
				}

				ret = lnf_rec_fset(lnf_rec, field, (void *)s);
				break;
			}

			case LNF_MPLS: {
				char *s;
				STRLEN len;

				s = SvPV(sv, len);

				if ( len != sizeof(lnf_mpls_t) ) {
					warn("%s invalid MPLS stack value for %d", NFL_LOG, field);
					return 0;
				}

				ret = lnf_rec_fset(lnf_rec, field, (void *)s);
				break;
			}

			case LNF_STRING: {
				char buf[LNF_MAX_STRING];
				STRLEN len;
				char *s;

				s = SvPV(sv, len);	
				if (len > sizeof(buf) - 1) {
					len = sizeof(buf) - 1;
				}
				memcpy(buf, s, len);
				buf[len] = '\0';

				ret = lnf_rec_fset(lnf_rec, field, (void *)buf);
				break;
			}

			default:
				croak("%s Unknown ID (%d) in %s !!", NFL_LOG, field, __FUNCTION__);
		}
		if (ret != LNF_OK) {
			croak("%s Error when processing field %d, error code: %d !!", NFL_LOG, field, ret);
		}
		i++;
	}


	lnf_write(instance->lnf_nffile_w, lnf_rec);

	lnf_rec_clear(lnf_rec);

	return 1;
}

void libnf_finish(int handle) {
libnf_instance_t *instance = libnf_instances[handle];
libnf_file_list_t  *tmp_pfile, *pfile;

	if (instance == NULL ) {
		croak("%s handler %d not initialized", NFL_LOG, handle);
		return;
	}

	if (instance->lnf_nffile_w) {
		lnf_close(instance->lnf_nffile_w);
		instance->lnf_nffile_w = NULL;
	}

	if (instance->lnf_nffile_r) {	
		lnf_close(instance->lnf_nffile_r);
		instance->lnf_nffile_r = NULL;
	}

	lnf_rec_free(instance->lnf_rec);
	if (instance->filter != NULL) {
		lnf_filter_free(instance->filter);
		instance->filter = NULL;
	}

	/* release file list */
	pfile = instance->files;
	while (pfile != NULL) {
		if (pfile->filename != NULL) {
			free(pfile->filename);
		}
		tmp_pfile = pfile;
		pfile = pfile->next;
		free(tmp_pfile);
	}

	instance->files = NULL;

	/* field list */

	if (instance->field_list != NULL) {
		free(instance->field_list);
	}


	free(instance); 
	libnf_instances[handle] = NULL;

	return ;

} // End of process_data_finish


