#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/timeb.h>

#if defined(__APPLE__) || defined(__UCLIBC__)
#include <sys/time.h>
int ftime(struct timeb *tp)
{
	struct timeval tv;

	gettimeofday(&tv, NULL);
	tp->time = tv.tv_sec;
	tp->millitm = tv.tv_usec / 1000;
	tp->timezone = 0;
	tp->dstflag = 0;

	return 0;
}
#endif

#include <FncExtractor.h>
#include <FncMatcher.h>

#include "version.h"
#include "img.h"

#define DEFAULT_SAMPLES_PATH "SampleImages/"

char *verifFileName[] = {
	"verif1.tif",
	"verif2.tif",
};

char *identFileName[] = {
	"ident1.tif",
	"ident2.tif",
	"ident3.tif",
	"ident4.tif",
	"ident5.tif",
};

char *genFileName[] = {
	"gen1.tif",
	"gen2.tif",
	"gen3.tif",
	"gen_test.tif",
};

char *identFileNameTest = "ident_test.tif";

struct timeb T1, T2; /* structs for measuring time */

char *image1 = NULL;
char *image2 = NULL;

#define FEF_START 1
#define FEF_FINISH 2
#define FEF_END 3

void *record;
NSizeType recordSize;
void *recordsDb[5];
NSizeType recordsDbSizes[5];
NSizeType maxRecordSize = 0;

int do_extraction(HFncExtractor hExtractor, char *fileName, void *record, NSizeType *recordSize)
{
	NResult result;
	int milisecs = 0;

	/* Load image from file supported by NImage library (assume resolution of 500 DPI) */
	{
		HNImage hImg;

		result = load_img_file(fileName, &hImg, 500);
		if (result != 0)
		{
			printf("Failed to load image (%s). Exiting. %d\n", fileName, result);
			return -1;
		}

		ftime(&T1);
		result = FnceExtractFromImage(hExtractor, hImg, nfpUnknown, nfitLiveScanPlain,
				record, maxRecordSize, recordSize);
		ftime(&T2);
		NImageFree(hImg);
	}

	milisecs = ((T2.time - T1.time) * 1000) + T2.millitm - T1.millitm;
	printf("%s,\t %d sec, %d millisec\n",
		fileName, milisecs / 1000, milisecs % 1000 );

	if (result != N_OK)
	{
		printf("Extract: An error occured (code %d). Exiting.\n", result);
		return -1;
	}

	if (*recordSize == 0)
	{
		printf("Extract: Image is low quality. Exiting.\n");
		return -1;
	}

	return 0;
}

int do_fast_extraction_start(HFncExtractor hExtractor, char *fileName, void *record,  NSizeType *recordSize)
{
	NResult result;
	int milisecs = 0;

	/* Load image from file supported by NImage library (assume resolution of 500 DPI) */
	{
		HNImage hImg;
		FnceReturnedImage ri;

		result = load_img_file(fileName, &hImg, 500);
		if (result != 0)
		{
			printf("Failed to load image (%s). Exiting. %d\n", fileName, result);
			return -1;
		}

		ri = 2;

		FnceSetParameter(hExtractor, FNCEP_RETURNED_IMAGE, &ri);


		ftime(&T1);
		result = FnceFastExtractStartFromImage(hExtractor, hImg, nfpUnknown, nfitLiveScanPlain,
				record, maxRecordSize, recordSize);
		ftime(&T2);

		NImageFree(hImg);
	}

	milisecs = ((T2.time - T1.time) * 1000) + T2.millitm - T1.millitm;
	printf("%s,\t %d sec, %d millisec\n",
			fileName, milisecs / 1000, milisecs % 1000 );
	if (result != N_OK)
	{
		printf("Fast Extract: An error occured (code %d). Exiting.\n", result);
		return -1;
	}

	if (*recordSize == 0)
	{
		printf("Fast Extract: Image is low quality. Exiting.\n");
		return -1;
	}
	return 0;
}

int do_fast_extraction_finish(HFncExtractor hExtractor, char *fileName, void *record,  NSizeType *recordSize)
{	
	NResult result;
	int milisecs = 0;

	/* Load image from file supported by NImage library (assume resolution of 500 DPI) */
	{
		HNImage hImg;

		result = load_img_file(fileName, &hImg, 500);
		if (result != 0)
		{
			printf("Failed to load image (%s). Exiting. %d\n", fileName, result);
			return -1;
		}

		ftime(&T1);
		result = FnceFastExtractFinishWithImage(hExtractor, hImg,
				record, maxRecordSize, recordSize);
		ftime(&T2);
		NImageFree(hImg);
	}

	milisecs = ((T2.time - T1.time) * 1000) + T2.millitm - T1.millitm;
	printf("%s,\t %d sec, %d millisec\n",
			fileName, milisecs / 1000, milisecs % 1000 );

	if (result != N_OK)
	{
		printf("Fast Extract: An error occured (code %d). Exiting.\n", result);
		return -1;
	}

	if (*recordSize == 0)
	{
		printf("Fast Extract: Image is low quality. Exiting.\n");
		return -1;
	}
	return 0;
}

int do_fast_extraction_end(HFncExtractor hExtractor)
{
	NResult result;

	result = FnceFastExtractEnd(hExtractor);
	if (result != N_OK)
	{
		printf("Fast Extract: An error occured (code %d). Exiting.\n", result);
		return -1;
	}
	return 0;
}

int do_verify(HFncMatcher hMatcher, void *record1, NSizeType size1, void *record2, NSizeType size2)
{
	NResult result;
	NInt score = 0;

	result = FncmVerify(hMatcher, record1, size1, record2, size2, NULL, &score);

	if (result != N_OK)
	{
		printf("Verify: An error occured. Exiting. %d\n", result);
		return -1;
	}

	if (score > 0)
	{
		printf("Fingerprints matched. Similarity: %d\n", score);
	}
	else
	{
		return 1;
		printf("Fingerprints didn't match.\n");
	}

	return 0;
}

int do_identifynext(HFncMatcher hMatcher, void *record, NSizeType size, int *is, int i)
{
	NResult result;
	NInt score = 0;

	result = FncmIdentifyNext(hMatcher, record, size, NULL, &score);
	if (result != N_OK)
	{
		printf("Verify: An error occured. Exiting. %d\n", result);
		return -1;
	}

	if (score > 0)
	{
		*is = i;
		printf("Fingerprints matched. Similarity: %d\n", score);
	}
	else
	{
		printf("Fingerprints didn't match.\n");
		return 1;
	}

	return 0;
}

int do_generalize(HFncExtractor hExtractor, void *records[], NSizeType recordSizes[], void **record, NSizeType *recordSize)
{
	NResult result;
	NInt baseTemplateIndex;

	result = FnceGeneralize(hExtractor, 3, (const void **)records, recordSizes,
		*record, maxRecordSize, recordSize, &baseTemplateIndex);
	if (result != N_OK)
	{
		printf("Generalize: An error occured. Exiting. %d\n", result);
		return -1;
	}

	return 0;
}

/* forward declarations of not important functions */
void setup_img_paths(const char *prefix);
void free_img_paths(void);


int main(int argc, char *argv[])
{
	char *img_dir = NULL;
	HFncExtractor hExtractor;
	HFncMatcher hMatcher;
	NResult result;
	int ret;
	int no_errors = 0;

	int i, is = -1;


	if (argc > 2)
	{
		printf("usage: %s [PATH_TO_SAMPLE_IMAGES]\n", argv[0]);
		return 1;
	}
	else if (argc == 2)
	{
		img_dir = argv[1];
	}
	else
	{
		img_dir = DEFAULT_SAMPLES_PATH;
	}
	setup_img_paths(img_dir);

	result = FnceCreate(&hExtractor);
	if (result != N_OK)
	{
		printf("FnceCreate returns %d\n", result);
		goto finalize_paths;
	}

	result = FncmCreate(&hMatcher);
	if (result != N_OK)
	{
		printf("FncmCreate returns %d\n", result);
		goto finalize_extractor;
	}

	ret = print_version(hExtractor, hMatcher);
	if (ret < 0)
	{
		goto finalize_matcher;
	}

	FnceGetMaxTemplateSize(hExtractor, &maxRecordSize);

	record = malloc(maxRecordSize);
	if (record == NULL)
	{
		printf("out of memory\n");
		goto finalize_matcher;
	}

	for (i = 0; i < 5; i++)
	{
		recordsDb[i] = malloc(maxRecordSize);
		if (recordsDb[i] == NULL)
		{
			printf("out of memory\n");
			do
			{
				i--;
				free(recordsDb[i]);
			} while (i != 0);

			goto finalize_matcher;
		}
	}

	printf("\t\t\t**** Verification Demo ****\n"
		"> Will process two fingerprint images and see if they match. <\n"
		"Image file, image processing time\n");

	ret = do_extraction(hExtractor, verifFileName[1], recordsDb[0], &recordsDbSizes[0]);
	if (ret < 0)
	{
		goto finalize_records;
	}

	ret = do_extraction(hExtractor, verifFileName[0], record, &recordSize);
	if (ret < 0)
	{
		goto finalize_records;
	}

	ret = do_verify(hMatcher, recordsDb[0], recordsDbSizes[0], record, recordSize);
	if (ret < 0)
	{
		goto finalize_records;
	}


	printf("\n\t\t\t**** Identification Demo ****\n"
		"> Will process five images and see which one of them matches the sixth. <\n"
		"Image file, image processing time\n");

	printf("Extracting images...\n");


	for (i = 0; i < 5; i++)
	{
		ret = do_extraction(hExtractor, identFileName[i], recordsDb[i], &recordsDbSizes[i]);
		if (ret < 0)
		{
			goto finalize_records;
		}
	}

	ret = do_extraction(hExtractor, identFileNameTest, record, &recordSize);
	if (ret < 0)
	{
		goto finalize_records;
	}

	printf("Matching images...\n");

	FncmIdentifyStart(hMatcher, record, recordSize, NULL);

	for (i = 0; i < 5; i++)
	{
		ret = do_identifynext(hMatcher, recordsDb[i], recordsDbSizes[i], &is, i);
		if (ret < 0)
		{
			goto finalize_records;
		}
	}

	FncmIdentifyEnd(hMatcher);

	if (is != -1)
		printf("The test fingerprint matched with %s.\n", identFileName[is]);
	else
		printf("The test fingerprint didn't match with any of the others.\n");


	printf("\n\t\t\t**** Identification Demo With Fast Etraction ****\n"
		"> Will process five images and see which one of them matches the sixth. <\n"
		"Image file, image processing time\n");

	printf("Extracting images...\n");

	for (i = 0; i < 5; i++)
	{
		ret = do_extraction(hExtractor, identFileName[i], recordsDb[i], &recordsDbSizes[i]);
		if (ret < 0)
		{
			goto finalize_records;
		}
	}

	printf("Extracting image with fast extraction...\n");

	ret = do_fast_extraction_start(hExtractor, identFileNameTest, record, &recordSize);
	if (ret < 0)
	{
		goto finalize_records;
	}

	printf("Matching images...\n");

	FncmIdentifyStart(hMatcher, record, recordSize, NULL);

	for (i = 0; i < 5; i++)
	{
		ret = do_identifynext(hMatcher, recordsDb[i], recordsDbSizes[i], &is, i);
		if (ret < 0)
		{
			goto finalize_records;
		}
	}

	FncmIdentifyEnd(hMatcher);

	ret = do_fast_extraction_end(hExtractor);

	if (is != -1)
		printf("The test fingerprint matched with %s.\n", identFileName[is]);
	else
		printf("The test fingerprint didn't match with any of the others.\n");


	printf("\n\t\t\t**** Generalization Demo ****\n"
		"> Will generalize 3 images <\n"
		"Image file, image processing time\n");
	for (i = 0; i < 4; i++)
	{
		ret = do_extraction(hExtractor, genFileName[i], recordsDb[i], &recordsDbSizes[i]);
		if (ret < 0)
		{
			goto finalize_records;
		}
	}

	ret = do_generalize(hExtractor, recordsDb, recordsDbSizes, &record, &recordSize);
	if (ret < 0)
	{
		goto finalize_records;
	}

	ret = do_verify(hMatcher, record, recordSize, recordsDb[3], recordsDbSizes[3]);
	if (ret < 0)
	{
		goto finalize_records;
	}


	no_errors = 1;

 finalize_records:
	for (i = 0; i < 5; i++)
	{
		free(recordsDb[i]);
	}
	free(record);
 finalize_matcher:
	FncmFree(hMatcher);
 finalize_extractor:
	FnceFree(hExtractor);
 finalize_paths:
	free_img_paths();

	return (no_errors) ? 0 : 1;
}


/* not important functions to setup image paths*/

char *
alloc_merge(const char *dir, int dir_len, const char *name)
{
	int name_len = strlen(name);
	char *buf;

	buf = malloc(dir_len + name_len + 1);
	if (buf)
	{
		memcpy(buf, dir, dir_len);
		memcpy(&buf[dir_len], name, name_len+1);
	}

	return buf;
}

#define ARRAY_SIZE(a) ( sizeof(a)/sizeof((a)[0]) )

void
setup_img_paths(const char *prefix)
{
	int i;
	int pref_len = (prefix) ? strlen(prefix) : 0;
	int dir_len;
	char dir[pref_len + 3]; 	/* +3 for possible "./" and trailing 0 */


	if (pref_len == 0)
	{
		prefix = ".";
		pref_len = 1;
	}

	memcpy(dir, prefix, pref_len+1);
	dir_len = pref_len;
	if (dir[dir_len-1] != '/')
	{
		dir[dir_len] = '/';
		dir[dir_len+1] = 0;
		dir_len++;
	}

	for (i=0; i < ARRAY_SIZE(verifFileName); i++)
	{
		verifFileName[i] = alloc_merge(dir, dir_len, verifFileName[i]);
	}

	for (i=0; i < ARRAY_SIZE(identFileName); i++)
	{
		identFileName[i] = alloc_merge(dir, dir_len, identFileName[i]);
	}

	for (i=0; i < ARRAY_SIZE(genFileName); i++)
	{
		genFileName[i] = alloc_merge(dir, dir_len, genFileName[i]);
	}

	identFileNameTest = alloc_merge(dir, dir_len, identFileNameTest);
}

void
free_img_paths(void)
{
	int i;


	for (i=0; i < ARRAY_SIZE(verifFileName); i++)
	{
		free(verifFileName[i]);
	}

	for (i=0; i < ARRAY_SIZE(identFileName); i++)
	{
		free(identFileName[i]);
	}

	for (i=0; i < ARRAY_SIZE(genFileName); i++)
	{
		free(genFileName[i]);
	}

	free(identFileNameTest);
}


