#include "headers.h"

HINSTANCE hInst = NULL;

HANDLE hTTBButt = NULL;
BOOL bServiceMode = FALSE;
BOOL usePopups;
HWND hwnd2watchedVarsWindow;
int hLangpack;
BYTE nameOrder[NAMEORDERCOUNT];
HGENMENU hUserMenu;
WatchListArrayStruct WatchListArray;
HANDLE hRestore;
HANDLE sMenuCommand, sImport, sServicemodeLaunch;
HANDLE hModulesLoadedHook = NULL, hSettingsChangedHook=NULL, hOptInitHook=NULL, hPreShutdownHook=NULL;

//========================
//  MirandaPluginInfo
//========================
PLUGININFOEX pluginInfoEx={
    sizeof(PLUGININFOEX),
	__PLUGIN_NAME,
	PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
	__DESCRIPTION,
	__AUTHOR,
	__AUTHOREMAIL,
	__COPYRIGHT,
	__AUTHORWEB,
	UNICODE_AWARE,
	// {A8A417EF-07AA-4F37-869F-7BFD74886534}
	{0xa8a417ef, 0x7aa, 0x4f37, {0x86, 0x9f, 0x7b, 0xfd, 0x74, 0x88, 0x65, 0x34}}
};

extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
{
	return &pluginInfoEx;
}

// we implement service mode interface
extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = {MIID_SERVICEMODE, MIID_LAST};

//========================
//  WINAPI DllMain
//========================
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
	hInst = hinstDLL;
	return TRUE;
}

void settingChanged(HWND hwnd2Settings, HANDLE hContact, char* module, char* setting);

int DBSettingChanged(WPARAM wParam,LPARAM lParam)
{
	DBCONTACTWRITESETTING *cws=(DBCONTACTWRITESETTING*)lParam;
	HANDLE hContact = (HANDLE)wParam;
	char *setting;
	int i;
	SettingListInfo* info;

	if (hwnd2mainWindow)
	{
		HWND hwnd2Settings = GetDlgItem(hwnd2mainWindow, IDC_SETTINGS);
		if (info = (SettingListInfo*)GetWindowLongPtr(hwnd2Settings,GWLP_USERDATA))
		{
			if ((hContact == info->hContact) && !mir_strcmp(info->module, cws->szModule))
			{
				setting = mir_strdup(cws->szSetting);
				if (cws->value.type == DBVT_DELETED)
				{
					LVFINDINFO lvfi;
					int index;
					lvfi.flags = LVFI_STRING;
					lvfi.psz = setting;
					lvfi.vkDirection = VK_DOWN;
					index = ListView_FindItem(hwnd2Settings,-1,&lvfi);
					if (index > -1)
						ListView_DeleteItem(hwnd2Settings, index);
					mir_free(setting);
					return 0;
				}
				settingChanged(hwnd2Settings, hContact, info->module, setting);
				mir_free(setting);
			}
		}
	}
	// watch list
	if (!hwnd2watchedVarsWindow && !usePopups) return 0;

	for (i=0; i<WatchListArray.count; i++)
	{
		if (WatchListArray.item[i].module && (hContact == WatchListArray.item[i].hContact))
		{
			if (!mir_strcmp(cws->szModule, WatchListArray.item[i].module))
			{
				if (!WatchListArray.item[i].setting || !mir_strcmp(cws->szSetting, WatchListArray.item[i].setting))
				{
					if (usePopups)
						popupWatchedVar(hContact, cws->szModule, cws->szSetting);
					if (hwnd2watchedVarsWindow)
						PopulateWatchedWindow(GetDlgItem(hwnd2watchedVarsWindow, IDC_VARS));
					break;
				}
			}
		}
	}
	return 0;
}

INT_PTR DBEditorppMenuCommand(WPARAM wParam, LPARAM lParam)
{

	if (!hwnd2mainWindow) // so only opens 1 at a time
	{
	    hRestore = (HANDLE)wParam;
		SetCursor(LoadCursor(NULL,IDC_WAIT));
		CreateDialog(hInst, MAKEINTRESOURCE(IDD_MAIN), 0, MainDlgProc);
	}
	else
	{
		ShowWindow(hwnd2mainWindow, SW_RESTORE);
		SetForegroundWindow(hwnd2mainWindow);
		if (!hRestore && wParam) {
			hRestore = (HANDLE)wParam;
			refreshTree(4);
		}
	}

	if (hTTBButt)
		CallService(MS_TTB_SETBUTTONSTATE, (WPARAM)hTTBButt, (LPARAM)(TTBST_RELEASED));

	return 0;
}

BOOL IsCP_UTF8(void)
{
	CPINFO CPInfo;

	return GetCPInfo(CP_UTF8, &CPInfo);
}

static int OnTTBLoaded(WPARAM wParam,LPARAM lParam)
{
	HICON ico = LoadIcon(hInst, MAKEINTRESOURCE(ICO_DBE_BUTT));

	TTBButton ttb = { sizeof(ttb) };
	ttb.dwFlags = TTBBF_VISIBLE | TTBBF_SHOWTOOLTIP;
	ttb.pszService = "DBEditorpp/MenuCommand";
	ttb.name = LPGEN("Database Editor++");
	ttb.hIconUp = ico;
	ttb.pszTooltipUp = LPGEN("Open Database Editor");
	hTTBButt = TopToolbar_AddButton(&ttb);
	return 0;
}

int ModulesLoaded(WPARAM wParam,LPARAM lParam)
{
	// Register menu item
	CLISTMENUITEM mi = { sizeof(mi) };
	mi.position = 1900000001;
	mi.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(ICO_REGEDIT));
	mi.pszName = modFullname;
	mi.pszService = "DBEditorpp/MenuCommand";
	Menu_AddMainMenuItem(&mi);

	ZeroMemory(&mi, sizeof(mi));
	mi.cbSize = sizeof(mi);
	mi.position = 1900000001;
	mi.flags = db_get_b(NULL,modname,"UserMenuItem",0) ? 0 : CMIF_HIDDEN;
	mi.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(ICO_REGUSER));
	mi.pszName = LPGEN("Open user tree in DBE++");
	mi.pszService = "DBEditorpp/MenuCommand";
	hUserMenu = Menu_AddContactMenuItem(&mi);

	// Register hotkeys
	HOTKEYDESC hkd = { sizeof(hkd) };
	hkd.pszName = "hk_dbepp_open";
	hkd.pszService = "DBEditorpp/MenuCommand";
	hkd.ptszDescription = LPGEN("Open Database Editor");
	hkd.ptszSection = modFullname;
	hkd.DefHotKey = HOTKEYCODE(HOTKEYF_SHIFT|HOTKEYF_EXT, 'D');
	Hotkey_Register(&hkd);

	// icons
	TCHAR szModuleFileName[MAX_PATH];
	if (GetModuleFileName(hInst, szModuleFileName, MAX_PATH))
		addIcons(szModuleFileName);

	UnhookEvent(hModulesLoadedHook);

	usePopups = db_get_b(NULL,modname,"UsePopUps",0);

	// Load the name order
	for(int i=0; i < NAMEORDERCOUNT; i++)
		nameOrder[i] = i;

	DBVARIANT dbv;
	if (!db_get(NULL,"Contact","NameOrder",&dbv)) {
		CopyMemory(nameOrder,dbv.pbVal,dbv.cpbVal);
		db_free(&dbv);
	}

	HookEvent(ME_TTB_MODULELOADED, OnTTBLoaded);

	if ( bServiceMode )
		CallService("DBEditorpp/MenuCommand",0,0);

	return 0;
}

int PreShutdown(WPARAM wParam,LPARAM lParam)
{
	if (hwnd2watchedVarsWindow) DestroyWindow(hwnd2watchedVarsWindow);
	if (hwnd2mainWindow) DestroyWindow(hwnd2mainWindow);
	if (hwnd2importWindow) DestroyWindow(hwnd2importWindow);

	UnhookEvent(hSettingsChangedHook);
	UnhookEvent(hOptInitHook);
	UnhookEvent(hPreShutdownHook);

	DestroyServiceFunction(sServicemodeLaunch);
	DestroyServiceFunction(sMenuCommand);
	DestroyServiceFunction(sImport);
	return 0;
}

INT_PTR ServiceMode(WPARAM wParam,LPARAM lParam)
{
	bServiceMode = TRUE;
	return SERVICE_ONLYDB;  // load database and then call us
}

INT_PTR ImportFromFile(WPARAM wParam,LPARAM lParam) 
{
	ImportSettingsFromFileMenuItem((HANDLE)wParam, (char*)lParam);
	return 0;
}

extern "C" __declspec(dllexport) int Load(void)
{
	mir_getLP(&pluginInfoEx);

	hwnd2mainWindow = 0;
	hwnd2watchedVarsWindow = 0;
	hwnd2importWindow = 0;
	hRestore = NULL;
	hSettingsChangedHook = HookEvent(ME_DB_CONTACT_SETTINGCHANGED,DBSettingChanged);
	hOptInitHook = HookEvent(ME_OPT_INITIALISE,OptInit);
	hPreShutdownHook = HookEvent(ME_SYSTEM_PRESHUTDOWN, PreShutdown);
	hModulesLoadedHook = HookEvent(ME_SYSTEM_MODULESLOADED,ModulesLoaded);
	sMenuCommand = CreateServiceFunction("DBEditorpp/MenuCommand", DBEditorppMenuCommand);
	sImport = CreateServiceFunction("DBEditorpp/Import", ImportFromFile);
	
	sServicemodeLaunch = CreateServiceFunction(MS_SERVICEMODE_LAUNCH, ServiceMode);

	// Ensure that the common control DLL is loaded.
	INITCOMMONCONTROLSEX icex;
	icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
	icex.dwICC  = ICC_LISTVIEW_CLASSES;
	InitCommonControlsEx(&icex);

	ZeroMemory(&WatchListArray, sizeof(WatchListArray));
	return 0;
}

extern "C" __declspec(dllexport) int Unload(void)
{
	freeAllWatches();
	return 0;
}



//=======================================================
//db_get_s (prob shouldnt use this unless u know how big the string is gonna be..)
//=======================================================

int DBGetContactSettingStringStatic(HANDLE hContact, char* szModule, char* szSetting, char* value, int maxLength)
{
	DBVARIANT dbv;
	if (!db_get(hContact, szModule, szSetting, &dbv))
	{
		strncpy(value, dbv.pszVal, maxLength);
		db_free(&dbv);
		return 1;
	}
	else
	{
		db_free(&dbv);
		return 0;
	}

	return 0;
}

int WriteBlobFromString(HANDLE hContact,const char *szModule,const char *szSetting, const char *szValue, int len)
{
	int j=0, i = 0;
	BYTE *data = NULL;
	BYTE b;
	int tmp;

	if (!(data = (BYTE *)_alloca(2+len/2)))
	{
		msg(Translate("Couldn't allocate enough memory!"), modFullname);
		return 0;
	}

	while(j<len)
	{
		b = szValue[j];

		if ((b>='0' && b<='9') ||
			(b>='A' && b<='F') ||
			(b>='a' && b<='f'))
		{
			if (sscanf(&szValue[j], "%02X", &tmp) == 1)
			{
				data[i++] = (BYTE)tmp;
				j++;
			}
		}
		j++;
	}

	if (i)
		return db_set_blob(hContact,szModule,szSetting, data, (WORD)i);

	return 0;
}


int GetSetting(HANDLE hContact, const char *szModule, const char *szSetting, DBVARIANT *dbv)
{
	return db_get_s(hContact, szModule, szSetting, dbv, 0);
}

int GetValue(HANDLE hContact, const char* szModule, const char* szSetting, char* Value, int length)
{
	DBVARIANT dbv = {0};

	if (Value && length >= 10 && !GetSetting(hContact, szModule, szSetting, &dbv)) {
		switch(dbv.type) {
		case DBVT_ASCIIZ:
			strncpy(Value, dbv.pszVal, length);
			break;
		case DBVT_DWORD:
			_itoa(dbv.dVal,Value,10);
			break;
		case DBVT_BYTE:
			_itoa(dbv.bVal,Value,10);
			break;
		case DBVT_WORD:
			_itoa(dbv.wVal,Value,10);
			break;
		case DBVT_UTF8:
			int len = (int)strlen(dbv.pszVal)+1;
			char *sz = (char*)_alloca(len*3);
			WCHAR *wc = (WCHAR*)_alloca(len*sizeof(WCHAR));
			MultiByteToWideChar(CP_UTF8, 0, dbv.pszVal, -1, wc, len);
			WideCharToMultiByte(CP_ACP, 0, wc, -1, sz, len, NULL, NULL);
			strncpy(Value, sz, length);
			break;
		}

		db_free(&dbv);

        Value[length-1] = 0;
		return 1;
	}

	if (Value)
		Value[0] = 0;

	return 0;
}


int GetValueW(HANDLE hContact, const char* szModule, const char* szSetting, WCHAR* Value, int length)
{
	DBVARIANT dbv ={0};

	if (Value && length >= 10 && !GetSetting(hContact, szModule, szSetting, &dbv)) {
		switch(dbv.type) {
		case DBVT_UTF8:
			{
				int len = (int)strlen(dbv.pszVal) + 1;
				WCHAR *wc = (WCHAR*)_alloca(length*sizeof(WCHAR));
				MultiByteToWideChar(CP_UTF8, 0, dbv.pszVal, -1, wc, len);
				wcsncpy((WCHAR*)Value, wc, length);
			}
			break;

		case DBVT_ASCIIZ:
			{
				int len = (int)strlen(dbv.pszVal) + 1;
				WCHAR *wc = (WCHAR*)_alloca(len*sizeof(WCHAR));
				MultiByteToWideChar(CP_ACP, 0, dbv.pszVal, -1, wc, len);
				wcsncpy((WCHAR*)Value, wc, length);
			}
			break;

		case DBVT_DWORD:
			_itow(dbv.dVal,Value,10);
			break;

		case DBVT_BYTE:
			_itow(dbv.bVal,Value,10);
			break;

		case DBVT_WORD:
			_itow(dbv.wVal,Value,10);
			break;
		}

		db_free(&dbv);

		Value[length-1] = 0;
		return 1;
	}

	if (Value)
		Value[0] = 0;

	return 0;
}

char *u2a( wchar_t* src )
{
    if (!src)
		 return NULL;

	int cbLen = WideCharToMultiByte( CP_ACP, 0, src, -1, NULL, 0, NULL, NULL );
	char* result = (char*)mir_calloc((cbLen+1)*sizeof(char));
	if ( result == NULL )
		return NULL;

	WideCharToMultiByte( CP_ACP, 0, src, -1, result, cbLen, NULL, NULL );
	result[ cbLen ] = 0;
	return result;
}

wchar_t *a2u( char* src , wchar_t *buffer, int len )
{
	wchar_t *result = buffer;
	if ( result == NULL || len < 3)
		return NULL;

	MultiByteToWideChar( CP_ACP, 0, src, -1, result, len - 1 );
	result[ len - 1 ] = 0;

	return result;
}

int GetDatabaseString(HANDLE hContact, const char *szModule, const char* szSetting, WCHAR *Value, int length, BOOL unicode)
{
	if (unicode)
		return GetValueW(hContact, szModule, szSetting, Value, length);
	else
		return GetValue(hContact, szModule, szSetting, (char*)Value, length);
}

WCHAR *GetContactName(HANDLE hContact, const char *szProto, int unicode)
{
	int i, r = 0;
	static WCHAR res[512];
	char *proto = (char*)szProto;
	char name[256];

	if (hContact && !proto)
		if (GetValue(hContact,"Protocol","p",name,SIZEOF(name)))
			proto = name;

	if (proto) {
		for(i=0; i < NAMEORDERCOUNT-1; i++) {
			switch(nameOrder[i])  {
			case 0: // custom name
				r = GetDatabaseString(hContact,"CList","MyHandle",res,SIZEOF(res),unicode);
				break;

			case 1: // nick
				r = GetDatabaseString(hContact,proto,"Nick",res,SIZEOF(res),unicode);
				break;

			case 2: // First Name
				r = GetDatabaseString(hContact,proto,"FirstName",res,SIZEOF(res),unicode);
				break;

			case 3: // E-mail
				r = GetDatabaseString(hContact,proto,"e-mail",res,SIZEOF(res),unicode);
				break;

			case 4: // Last Name
				if (GetDatabaseString(hContact,proto,"LastName",res,SIZEOF(res),unicode))
					break;

			case 5: // Unique id
				{
					// protocol must define a PFLAG_UNIQUEIDSETTING
					char *uid = (char*)CallProtoService(proto,PS_GETCAPS,PFLAG_UNIQUEIDSETTING,0);
					if ((INT_PTR)uid != CALLSERVICE_NOTFOUND && uid)
						r = GetDatabaseString(hContact,proto,uid,res,SIZEOF(res),unicode);
					break;
				}
			case 6: // first + last name
				{
					int len = 0;

					if (r = GetDatabaseString(hContact,proto,"FirstName",res,SIZEOF(res),unicode))
					{
						if (unicode)
							len = (int)wcslen(res);
						else
							len = (int)strlen((char*)res);
					}
					else
						res[0] = 0;

					if (len && len < SIZEOF(res) - 2) {
						if (unicode)
							wcscat(res,L" ");
						else
							strcat((char*)res," ");

						len++;
					}

					if (SIZEOF(res)-len > 1)
						r |= GetDatabaseString(hContact,proto,"LastName",&res[len],SIZEOF(res)-len,unicode);

					break;
				}
			}

			if (r) return res;
		}
	}

	return (unicode) ? nick_unknownW : (WCHAR*)nick_unknown;
}