// FPDatabase.cpp: implementation of the CFPDatabase class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "FPDatabase.h"
#include "DialogMsg.h"
#include "FncExtractor.h"
#include "FncMatcher.h"
#include "NImage.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

#define MAX_FEATURES_SIZE 10000
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CFPDatabase::CFPDatabase()
{
	m_loger = NULL;
	m_count = 0;
}

CFPDatabase::~CFPDatabase()
{
	m_loger = NULL;
	ClearDB();
}

void CFPDatabase::Load()
{
	if (m_loger)
		m_loger->AddToLog(L"Loading fingerprints... ");

	CLogerInterface* loger = m_loger;

	m_loger = NULL; // stop loggin

	FILE* datafile = fopen("fpdatabase", "rb");
	if (datafile != NULL)
	{
		NInt count = 0;
		if (fread(&count, sizeof(count), 1, datafile) == 1)
		{
			for (NInt i = 0; i < count; i++)
			{
				FPRecord fp;

				if (fread(fp.description, sizeof(fp.description), 1, datafile) != 1) 
					break;

				if (fread(&(fp.size), sizeof(fp.size), 1, datafile) != 1)
					break;

				fp.features = new BYTE[fp.size];

				if (fread(fp.features, fp.size, 1, datafile) != 1)
				{
					delete[] fp.features;
					break;
				}

				AddFingerprint(&fp);
			}
		}

		fclose(datafile);
	}

	m_loger = loger; // enable loging

	if (m_loger)
	{
		m_loger->AddToLog(L"done", FALSE);

		CString str;
		str.Format(L"Fingerprints data loaded (count: %d)", m_count);
		m_loger->AddToLog(str);
	}
}

void CFPDatabase::Store()
{
	if (m_loger)
		m_loger->AddToLog(L"Storing fingerprints... ");

	CLogerInterface* loger = m_loger;

	m_loger = NULL; // stop loggin

	FILE* datafile = fopen("fpdatabase", "wb");
	if (datafile != NULL)
	{
		if (fwrite(&m_count, sizeof(m_count), 1, datafile) == 1)
		{
			for (NInt i = 0; i < 256; i++)
			{
				POSITION pos = m_data[i].GetHeadPosition();
				for (NInt j=0; j < m_data[i].GetCount(); j++)
				{
					FPRecord fp = m_data[i].GetNext(pos);

					if (fwrite(fp.description, sizeof(fp.description), 1, datafile) != 1)
						break;
					if (fwrite(&(fp.size), sizeof(fp.size), 1, datafile) != 1)
						break;
					if (fwrite(fp.features, fp.size, 1, datafile) != 1)
						break;
				}
			}
		}

		fclose(datafile);
	}

	m_loger = loger; // enable loging

	if (m_loger)
	{
		m_loger->AddToLog(L"done", FALSE);
		CString str;
		str.Format(L"Fingerprints data stored (count: %d)", m_count);
		m_loger->AddToLog(str);
	}
}

void CFPDatabase::ClearDB()
{
	if (m_loger)
		m_loger->AddToLog(L"Clearing fingerprints data... ");

	for (NInt i = 0; i < 256; i++)
	{
		POSITION pos = m_data[i].GetHeadPosition();
		for (NInt j=0; j < m_data[i].GetCount(); j++)
		{
			FPRecord fp = m_data[i].GetNext(pos);

			delete[] fp.features;
			fp.features = NULL;
			m_count--;
		}
		m_data[i].RemoveAll();
	}

	if (m_loger)
		m_loger->AddToLog(L"done", FALSE);
}

void CFPDatabase::AddFingerprint(FPRecord* finger)
{
	NByte G;
	NFRecordGetGMem(finger->features, finger->size, &G);
	m_data[G].AddTail(*finger);
	m_count++;

	if (m_loger)
		m_loger->AddToLog(L"Fingerprint enrolled into database");
}

BOOL CFPDatabase::Identify(NByte * features, NSizeType size, NBool full, HFncMatcher hMatcher)
{
	TimeIntervalCounter tc;

	FncmMatchDetails * MatchDetails = NULL;
	FncmCreate(&hMatcher);

	if (m_loger)
		m_loger->AddToLog(L"Starting identification...");

	StartTICounter(&tc);
	
	NInt result = FncmIdentifyStart(hMatcher, (const void *)features, size, &MatchDetails);

	PauseTICounter(&tc);

	if (NFailed(result))
	{
		wchar_t buffer[1024];
		swprintf(buffer, _T("Identification failed (errorcode = %d)!"), result);
		AfxMessageBox(buffer, MB_OK);
		return FALSE;
	}

	NByte G;
	NFRecordGetGMem(features, size, &G);
	NByte Gmin = G;
	NByte Gmax = G;

	NBool updir = TRUE;
	NBool found = FALSE;

	NInt C = 0;

	while (found == FALSE)
	{
		found = IdentifyG(full, G, C, &tc, hMatcher, MatchDetails);

		if (found == FALSE)
		{
			if ((Gmin == 0) && (Gmax == 255))
				break;
			else
			{
				if ((updir == TRUE) && (Gmax == 255))
					updir = FALSE;
				if ((updir == FALSE) && (Gmin == 0))
					updir = TRUE;
			}

			if (updir == TRUE) 
			{
				Gmax = Gmax + 1;
				G = Gmax;
			}
			else
			{
				Gmin = Gmin - 1;
				G = Gmin;
			}

			updir = !updir;
		}
	}
	ResumeTICounter(&tc);

	FncmIdentifyEnd(hMatcher);
	//FncmFree(hMatcher);
	FncmMatchDetailsFree(MatchDetails);
	
	StopTICounter(&tc);
	if (m_loger)
	{
		m_loger->AddToLog(L"Identification completed. Statistics:");
		m_loger->AddToLog(CString("Identification time: ") + CString(tc.str));
		CString str;
		str.Format(L"%d", m_count);
		m_loger->AddToLog(CString("DB record count: ") + str);

		str.Format(L"%d", C);
		m_loger->AddToLog(CString("Matched fingerprint count: ") + str);

		str.Format(L"%.2f", (double)C*(double)1000000/(double)tc.mks);
		m_loger->AddToLog(CString("Matching speed (fingers/sec): ") + str);

		str.Format(L"%.2f", (double)m_count*(double)1000000/(double)tc.mks);
		m_loger->AddToLog(CString("Effective matching speed (fingers/sec): ") + str);
	}
	return found;
}

BOOL CFPDatabase::IdentifyG(NBool full, NBool G, NInt &count, TimeIntervalCounter* tc, HFncMatcher hMatcher, FncmMatchDetails * MatchDetails)
{
	if (m_data[G].GetCount() <= 0)
		return FALSE;

	POSITION pos = m_data[G].GetHeadPosition();

	NInt threshold = 0;
	FncmGetParameter(hMatcher, FNCMP_MATCHING_THRESHOLD, &threshold);

	for (NInt j=0; j < m_data[G].GetCount(); j++)
	{
		FPRecord fp = m_data[G].GetNext(pos);

		NInt score = 0;
		NInt * pScore = & score;

		count++;
		ResumeTICounter(tc);
		FncmIdentifyNext(hMatcher, (const void *) fp.features, MAX_FEATURES_SIZE, MatchDetails, pScore);
		
		PauseTICounter(tc);

		if (MatchDetails->Score >= threshold)
		{
			if (m_loger)
			{
				CString str;
				str.Format(L"Found (sim., desc.): %d %s", MatchDetails->Score, fp.description);
				m_loger->AddToLog(str);

				if (!full)
				{
					CString str;
					str.Format(_T("Identified: %s"), fp.description);

					CDialogMsg dlg;

					dlg.m_color = RGB(0, 255, 0);
					dlg.m_msg= str;
					dlg.DoModal();
				}
			}
			if (full == FALSE)
				return TRUE;
		}
	}

	AfxGetMainWnd()->UpdateWindow();
	AfxGetApp()->PumpMessage();
	return FALSE;
}
