#include "mldbApi.h"
#include "main.h"
#include <strsafe.h>

itemRecordW *MLDBAPI::GetFile(const wchar_t *filename)
{
	itemRecordW *result = 0;
	if (filename)
	{
		openDb(); // just in case it's not opened yet (this function will return immediately if it's already open)
		if (g_table)
		{
			EnterCriticalSection(&g_db_cs);
			nde_scanner_t s = NDE_Table_CreateScanner(g_table);

			if (s)
			{
				if (NDE_Scanner_LocateFilename(s, MAINTABLE_ID_FILENAME, FIRST_RECORD, filename))
				{
					nde_field_t f = NDE_Scanner_GetFieldByID(s, MAINTABLE_ID_FILENAME);
					if (f)
					{
						result = (itemRecordW *)_aligned_malloc(sizeof(itemRecordW), 16);
						if (result)
						{
							result->filename = NDE_StringField_GetString(f);
							ndestring_retain(result->filename);
							ScannerRefToObjCacheNFNW(s, result, true);
						}
					}
				}
				NDE_Table_DestroyScanner(g_table, s);
			}
			LeaveCriticalSection(&g_db_cs);
		}
	}
	return result;
}

itemRecordW *MLDBAPI::GetFileIf(const wchar_t *filename, const wchar_t *query)
{
	itemRecordW *result = 0;
	if (filename)
	{
		openDb(); // just in case it's not opened yet (this function will return immediately if it's already open)
		if (g_table)
		{
			EnterCriticalSection(&g_db_cs);
			nde_scanner_t s = NDE_Table_CreateScanner(g_table);

			if (s)
			{
				NDE_Scanner_Query(s, query);
				if (NDE_Scanner_LocateFilename(s, MAINTABLE_ID_FILENAME, FIRST_RECORD, filename))
				{
					nde_field_t f = NDE_Scanner_GetFieldByID(s, MAINTABLE_ID_FILENAME);
					if (f)
					{
						result = (itemRecordW *)_aligned_malloc(sizeof(itemRecordW), 16);
						if (result)
						{
							result->filename = NDE_StringField_GetString(f);
							ndestring_retain(result->filename);
							ScannerRefToObjCacheNFNW(s, result, true);
						}
					}
				}
				NDE_Table_DestroyScanner(g_table, s);
			}
			LeaveCriticalSection(&g_db_cs);
		}
	}
	return result;
}

itemRecordListW *MLDBAPI::GetAlbum(const wchar_t *albumname, const wchar_t *albumartist)
{
	wchar_t query[4096] = {0}; // hope it's big enough
	if (albumartist && albumname)
	{
		StringCchPrintfW(query, 4096, L"((albumartist isempty and artist=\"%s\") or albumartist=\"%s\") and album=\"%s\"", albumartist, albumartist, albumname);
		return Query(query);
	}
	else if (albumname)
	{
		StringCchPrintfW(query, 4096, L"album=\"%s\"", albumname);
		return Query(query);
	}
	return 0;
}

itemRecordListW *MLDBAPI::Query(const wchar_t *query)
{
	itemRecordListW *result = 0;

	if (query)
	{
		openDb(); // just in case it's not opened yet (this function will return immediately if it's already open)
		if (g_table)
		{
			EnterCriticalSection(&g_db_cs);
			nde_scanner_t s = NDE_Table_CreateScanner(g_table);
			if (s)
			{
				NDE_Scanner_Query(s, query);
				NDE_Scanner_First(s);

				do
				{
					nde_field_t f = NDE_Scanner_GetFieldByID(s, MAINTABLE_ID_FILENAME);
					if (!f) break;

					if (!result)
						result = (itemRecordListW *)calloc(1, sizeof(itemRecordListW));

					if (!result)
						break;

					allocRecordList(result, result->Size + 1);
					if (!result->Alloc) break;

					result->Items[result->Size].filename = NDE_StringField_GetString(f);
					ndestring_retain(result->Items[result->Size].filename);
					ScannerRefToObjCacheNFNW(s, result, true);
				}
				while (NDE_Scanner_Next(s));

				NDE_Table_DestroyScanner(g_table, s);
			}
			LeaveCriticalSection(&g_db_cs);
		}
	}
	return result;
}

itemRecordListW *MLDBAPI::QueryLimit(const wchar_t *query, unsigned int limit)
{
	itemRecordListW *result = 0;
	if (limit == 0)
		return 0;
	if (query)
	{
		openDb(); // just in case it's not opened yet (this function will return immediately if it's already open)
		if (g_table)
		{
			EnterCriticalSection(&g_db_cs);
			nde_scanner_t s = NDE_Table_CreateScanner(g_table);
			if (s)
			{
				NDE_Scanner_Query(s, query);
				NDE_Scanner_First(s);

				do
				{
					nde_field_t f = NDE_Scanner_GetFieldByID(s, MAINTABLE_ID_FILENAME);
					if (!f) break;

					if (!result)
					{
						result = (itemRecordListW *)calloc(1, sizeof(itemRecordListW));
						if (!result)
							break;

						allocRecordList(result, limit);
						if (!result->Alloc) 
							break;
					}			

					result->Items[result->Size].filename = NDE_StringField_GetString(f);
					ndestring_retain(result->Items[result->Size].filename);
					ScannerRefToObjCacheNFNW(s, result, true);
				}
				while (result->Size < (int)limit && NDE_Scanner_Next(s));

				NDE_Table_DestroyScanner(g_table, s);
			}
			LeaveCriticalSection(&g_db_cs);
		}
	}
	return result;
}

void MLDBAPI::FreeRecord(itemRecordW *record)
{
	freeRecord(record);
}

void MLDBAPI::FreeRecordList(itemRecordListW *recordList)
{
	freeRecordList(recordList);
}

bool FindFileInDB(nde_scanner_t s, const wchar_t *filename);
void MLDBAPI::SetField(const wchar_t *filename, const char *field, const wchar_t *value)
{
	openDb();
	if (g_table)
	{
		EnterCriticalSection(&g_db_cs);
		nde_scanner_t s = NDE_Table_CreateScanner(g_table);
		if (s)
		{
			if (FindFileInDB(s, filename))
			{				
				if (value && value[0])
				{
					NDE_Scanner_Edit(s);
					nde_field_t f = NDE_Scanner_GetFieldByName(s, field);
					if (!f) 
						f=NDE_Scanner_NewFieldByName(s, field);

					if (f)
						NDE_StringField_SetString(f, value);
				}
				else
				{
					nde_field_t f = NDE_Scanner_GetFieldByName(s, field);
					if (f)
						NDE_Scanner_DeleteField(s, f);
				}
				NDE_Scanner_Post(s);
				g_table_dirty++;
			}
			NDE_Table_DestroyScanner(g_table, s);
		}
		LeaveCriticalSection(&g_db_cs);
	}
}

void MLDBAPI::SetFieldInteger(const wchar_t *filename, const char *field, int value)
{
	openDb();
	if (g_table)
	{
		EnterCriticalSection(&g_db_cs);
		nde_scanner_t s = NDE_Table_CreateScanner(g_table);
		if (s)
		{
			if (FindFileInDB(s, filename))
			{				
				NDE_Scanner_Edit(s);
				nde_field_t f = NDE_Scanner_GetFieldByName(s, field);
				if (!f) 
					f=NDE_Scanner_NewFieldByName(s, field);

				if (f)
					NDE_IntegerField_SetValue(f, value);

				NDE_Scanner_Post(s);
				g_table_dirty++;
			}
			NDE_Table_DestroyScanner(g_table, s);
		}
		LeaveCriticalSection(&g_db_cs);
	}
}

void MLDBAPI::SetFieldInt128(const wchar_t *filename, const char *field, uint8_t value[16])
{
	openDb();
	if (g_table)
	{
		EnterCriticalSection(&g_db_cs);
		nde_scanner_t s = NDE_Table_CreateScanner(g_table);
		if (s)
		{
			if (FindFileInDB(s, filename))
			{				
				if (value)
				{
					NDE_Scanner_Edit(s);
					nde_field_t f = NDE_Scanner_GetFieldByName(s, field);
					if (!f) 
						f=NDE_Scanner_NewFieldByName(s, field);

					if (f)
						NDE_Int128Field_SetValue(f, value);
				}
				else
				{
					nde_field_t f = NDE_Scanner_GetFieldByName(s, field);
					if (f)
						NDE_Scanner_DeleteField(s, f);
				}
				NDE_Scanner_Post(s);
				g_table_dirty++;
			}
			NDE_Table_DestroyScanner(g_table, s);
		}
		LeaveCriticalSection(&g_db_cs);
	}
}

void MLDBAPI::Sync()
{
	openDb();
	if (g_table)
	{
		EnterCriticalSection(&g_db_cs);
		NDE_Table_Sync(g_table);
		g_table_dirty=0;
		LeaveCriticalSection(&g_db_cs);
	}
}

int MLDBAPI::AddFile(const wchar_t *filename)
{
	int guess = g_config->ReadInt(L"guessmode", 0);
	int meta = g_config->ReadInt(L"usemetadata", 1);
	return addFileToDb(filename, 0, meta, guess);
}

int MLDBAPI::RemoveFile(const wchar_t *filename)
{
	return RemoveFileFromDB(filename);
}

void MLDBAPI::RetainString(wchar_t *str)
{
	ndestring_retain(str);
}

void MLDBAPI::ReleaseString(wchar_t *str)
{
	ndestring_release(str);
}

wchar_t *MLDBAPI::DuplicateString(const wchar_t *str)
{
	return ndestring_wcsdup(str);
}

int MLDBAPI::GetMaxInteger(const char *field, int *max)
{
	openDb();
	if (!g_table)
		return 1;

	EnterCriticalSection(&g_db_cs);
	nde_field_t f = NDE_Table_GetColumnByName(g_table, field);
	if (!f)
	{
		LeaveCriticalSection(&g_db_cs);
		return 1;
	}

	unsigned char field_id = NDE_ColumnField_GetFieldID(f);
	nde_scanner_t s = NDE_Table_CreateScanner(g_table);
	if (s)
	{
		int maximum_so_far=0;
		NDE_Scanner_Query(s, L"");
		NDE_Scanner_First(s);
		do
		{
			nde_field_t f = NDE_Scanner_GetFieldByID(s, field_id);
			if (f) 
			{
				int this_value = NDE_IntegerField_GetValue(f);
				if (this_value > maximum_so_far)
					maximum_so_far = this_value;
			}

		} while (NDE_Scanner_Next(s) && !NDE_Scanner_EOF(s));

		NDE_Table_DestroyScanner(g_table, s);
		*max=maximum_so_far;
		LeaveCriticalSection(&g_db_cs);
		return 0;
	}
	else
	{
		LeaveCriticalSection(&g_db_cs);
		return 1;
	}	
}

#define CBCLASS MLDBAPI
START_DISPATCH;
CB(API_MLDB_GETFILE, GetFile)
CB(API_MLDB_GETFILEIF, GetFileIf)
CB(API_MLDB_GETALBUM, GetAlbum)
CB(API_MLDB_QUERY, Query)
CB(API_MLDB_QUERYLIMIT, QueryLimit)
VCB(API_MLDB_FREERECORD, FreeRecord)
VCB(API_MLDB_FREERECORDLIST, FreeRecordList)
VCB(API_MLDB_SETFIELD, SetField)
VCB(API_MLDB_SETFIELDINT, SetFieldInteger)
VCB(API_MLDB_SETFIELDINT128, SetFieldInt128)
VCB(API_MLDB_SYNC, Sync)
CB(API_MLDB_ADDFILE, AddFile)
CB(API_MLDB_REMOVEFILE, RemoveFile)
VCB(API_MLDB_RETAINSTRING, RetainString)
VCB(API_MLDB_RELEASESTRING, ReleaseString)
CB(API_MLDB_DUPLICATESTRING, DuplicateString)
CB(API_MLDB_GETMAXINTEGER, GetMaxInteger)
END_DISPATCH;
#undef CBCLASS