#include "stdafx.h"

HWND hwnd2watchedVarsWindow = NULL;

static WatchListArrayStruct WatchListArray = {0};

static int lastColumn = -1;

ColumnsSettings csWatchList[] = {
	{ LPGENW("Contact"), 0, "Watch0width", 100 },
	{ LPGENW("Module"),  1, "Watch1width", 100 },
	{ LPGENW("Setting"), 2, "Watch2width", 100 },
	{ LPGENW("Value"),   3, "Watch3width", 200 },
	{ LPGENW("Type"),    4, "Watch4width", 75 },
	{0}
};



int WatchedArrayIndex(MCONTACT hContact, const char *module, const char *setting, int strict) {
	for (int i = 0; i < WatchListArray.count; i++) {
		if (hContact == WatchListArray.item[i].hContact)
			if (!mir_strcmp(module, WatchListArray.item[i].module))
				// empty setting = module watching
				if ((!strict && !WatchListArray.item[i].setting) || !mir_strcmp(setting, WatchListArray.item[i].setting)) 
					return i;
	}
	return -1;
}


int addSettingToWatchList(MCONTACT hContact, const char *module, const char *setting)
{
	if (WatchListArray.count == WatchListArray.size)
	{
		WatchListArray.size += 32;
		WatchListArray.item = (struct DBsetting*)mir_realloc(WatchListArray.item, sizeof(struct DBsetting)*WatchListArray.size);
	}
	if (!WatchListArray.item) 
		return 0;

	db_get_s(hContact, module, setting, &(WatchListArray.item[WatchListArray.count].dbv), 0);

	WatchListArray.item[WatchListArray.count].hContact = hContact;
	WatchListArray.item[WatchListArray.count].module = mir_strdup(module);

	if (setting) 
		WatchListArray.item[WatchListArray.count].setting = mir_strdup(setting);
	else
		WatchListArray.item[WatchListArray.count].setting = 0;

	WatchListArray.count++;
	return 1;
}


void freeWatchListItem(int item)
{
    if (item < 0 || item >= WatchListArray.count) return;
    
	if (WatchListArray.item[item].module) 
		mir_free(WatchListArray.item[item].module);

	WatchListArray.item[item].module = 0;

	if (WatchListArray.item[item].setting) 
		mir_free(WatchListArray.item[item].setting);

	WatchListArray.item[item].setting = 0;
	db_free(&(WatchListArray.item[item].dbv));
	WatchListArray.item[item].hContact = 0;
}


void addwatchtolist(HWND hwnd, struct DBsetting *lParam)
{
	DBVARIANT *dbv = &(lParam->dbv);

	if (!lParam->module) return;
	
	LVITEM lvItem;
	lvItem.lParam = (LPARAM)lParam->hContact;
	lvItem.mask = LVIF_TEXT | LVIF_PARAM;
	lvItem.iItem = 0;
	lvItem.iSubItem = 0;

	if (!lParam->setting) // add every item in the module and return
	{
		ModuleSettingLL settinglist;
		if (IsModuleEmpty(lParam->hContact, lParam->module) || !EnumSettings(lParam->hContact, lParam->module, &settinglist))
			return;

		struct DBsetting dummy = {0};
		dummy.hContact = lParam->hContact;
		dummy.module = lParam->module;
		
		for (ModSetLinkLinkItem *setting = settinglist.first; setting; setting = setting->next)
		{
			dummy.setting = setting->name;
			addwatchtolist(hwnd, &dummy);
			
		}
		FreeModuleSettingLL(&settinglist);
		return;
	}
	
	db_free(&(lParam->dbv));

	if (db_get_s(lParam->hContact, lParam->module, lParam->setting, &(lParam->dbv), 0))
		return;

	wchar_t data[32], tmp[16], name[NAME_SIZE];

	GetContactName(lParam->hContact, NULL, name, _countof(name));
	lvItem.pszText = name;

	int index = ListView_InsertItem(hwnd, &lvItem);

	ListView_SetItemText(hwnd, index, 0, lvItem.pszText);
	ListView_SetItemTextA(hwnd, index, 1, lParam->module);
	ListView_SetItemTextA(hwnd, index, 2, lParam->setting);
	ListView_SetItemText(hwnd, index, 4, DBVType(dbv->type));

	data[0] = 0;

	switch (dbv->type) {
	case DBVT_BLOB:
	{
		ptrA str(StringFromBlob(dbv->pbVal, dbv->cpbVal));
		ListView_SetItemTextA(hwnd, index, 3, str);
		break;
	}
	case DBVT_BYTE:
		mir_snwprintf(data, L"0x%02X (%s)", dbv->bVal, _ultow(dbv->bVal, tmp, 10));
		ListView_SetItemText(hwnd, index, 3, data);
		break;
	case DBVT_WORD:
		mir_snwprintf(data, L"0x%04X (%s)", dbv->wVal, _ultow(dbv->wVal, tmp, 10));
		ListView_SetItemText(hwnd, index, 3, data);
		break;

	case DBVT_DWORD:
		mir_snwprintf(data, L"0x%08X (%s)", dbv->dVal, _ultow(dbv->dVal, tmp, 10));
		ListView_SetItemText(hwnd, index, 3, data);
		break;

	case DBVT_ASCIIZ:
		ListView_SetItemTextA(hwnd, index, 3, dbv->pszVal);
		break;

	case DBVT_WCHAR:
	{
		ptrW str(mir_wstrdup(dbv->pwszVal));
		ListView_SetItemText(hwnd, index, 3, str);
		break;
	}

	case DBVT_UTF8:
	{
		ptrW str(mir_utf8decodeW(dbv->pszVal));
		ListView_SetItemText(hwnd, index, 3, str);
		break;
	}
	case DBVT_DELETED:
		if (IsResidentSetting(lParam->module, lParam->setting))
			ListView_SetItemText(hwnd, index, 3, TranslateT("*** resident ***"));
		break;
	} // switch

}


void PopulateWatchedWindow()
{
    if (!hwnd2watchedVarsWindow) return;
    HWND hwnd = GetDlgItem(hwnd2watchedVarsWindow, IDC_VARS);
	ListView_DeleteAllItems(hwnd);
	for (int i = 0; i < WatchListArray.count; i++) {
		addwatchtolist(hwnd, &(WatchListArray.item[i]));
	}

	if (lastColumn != -1) {
		ColumnsSortParams params;
		params.hList = hwnd;
		params.column = lastColumn;
		params.last = -1;
		ListView_SortItemsEx(params.hList, ColumnsCompare, (LPARAM)&params);
	}
}


void freeAllWatches()
{
	ListView_DeleteAllItems(GetDlgItem(hwnd2watchedVarsWindow, IDC_VARS));
	for (int i = 0; i < WatchListArray.count; i++)
	{
		freeWatchListItem(i);
	}
	mir_free(WatchListArray.item);
	WatchListArray.item = 0;
	WatchListArray.count = 0;
}


int WatchDialogResize(HWND, LPARAM, UTILRESIZECONTROL *urc)
{
	switch (urc->wId)
	{
	case IDC_VARS:
		urc->rcItem.top = 0;
		urc->rcItem.bottom = urc->dlgNewSize.cy;
		urc->rcItem.left = 0;
		urc->rcItem.right = urc->dlgNewSize.cx;
		return RD_ANCHORY_CUSTOM | RD_ANCHORX_CUSTOM;
	}
	return RD_ANCHORX_LEFT | RD_ANCHORY_TOP;
}


LRESULT CALLBACK WatchSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg) {

	case WM_KEYUP:
		if (wParam == VK_F5) {
			PopulateWatchedWindow();
		}
		break;
	}
	return mir_callNextSubclass(hwnd, WatchSubclassProc, msg, wParam, lParam);
}


INT_PTR CALLBACK WatchDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg) {
	case WM_INITDIALOG:
		hwnd2watchedVarsWindow = hwnd;
		// do the icon
		SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)LoadIcon(hInst, MAKEINTRESOURCE(ICO_REGEDIT)));
		TranslateMenu(GetMenu(hwnd));
		TranslateMenu(GetSubMenu(GetMenu(hwnd), 0));
		TranslateDialogDefault(hwnd);

		SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_APPWINDOW); // taskbar icon

		ListView_SetExtendedListViewStyle(GetDlgItem(hwnd, IDC_VARS), 32 | LVS_EX_LABELTIP); // LVS_EX_GRIDLINES

		loadListSettings(GetDlgItem(hwnd, IDC_VARS), csWatchList);
		Utils_RestoreWindowPositionNoMove(hwnd, NULL, modname, "Watch_");

		mir_subclassWindow(GetDlgItem(hwnd, IDC_VARS), WatchSubclassProc);

		PopulateWatchedWindow();
		return TRUE;

	// for the resize
	case WM_GETMINMAXINFO:
		{
			MINMAXINFO *mmi = (MINMAXINFO*)lParam;
			mmi->ptMinTrackSize.x = 500;
			mmi->ptMinTrackSize.y = 300;
		}
		return 0;

	case WM_SIZE:
		Utils_ResizeDialog(hwnd, hInst, MAKEINTRESOURCEA(IDD_WATCH_DIAG), WatchDialogResize);
		break;

	case WM_COMMAND:
		switch (LOWORD(wParam)) {
		case MENU_REMALL_WATCHES:
			freeAllWatches();
			break;
		case MENU_EXIT:
		case IDCANCEL:
			hwnd2watchedVarsWindow = NULL;
			DestroyWindow(hwnd);
			break;
		case MENU_REFRESH:
			PopulateWatchedWindow();
		}
		break;

	case WM_NOTIFY:
		switch (LOWORD(wParam)) {
		case IDC_VARS:
			switch (((NMHDR*)lParam)->code) {
			case NM_DBLCLK:
				LVHITTESTINFO hti;
				LVITEM lvi;
				{
					HWND hwndVars = GetDlgItem(hwnd, IDC_VARS);
					hti.pt = ((NMLISTVIEW*)lParam)->ptAction;
					if (ListView_SubItemHitTest(hwndVars, &hti) > -1)
					{
						if (hti.flags&LVHT_ONITEM)
						{
							lvi.mask = LVIF_PARAM;
							lvi.iItem = hti.iItem;
							lvi.iSubItem = 0;
							if (ListView_GetItem(hwndVars, &lvi))
							{
								ItemInfo ii;
								ii.hContact = (MCONTACT)lvi.lParam;
								ListView_GetItemTextA(hwndVars, hti.iItem, 1, ii.module, _countof(ii.module));
								ListView_GetItemTextA(hwndVars, hti.iItem, 2, ii.setting, _countof(ii.setting));
								ii.type = FW_SETTINGNAME;
								SendMessage(hwnd2mainWindow, WM_FINDITEM, (WPARAM)&ii, 0);
							}
						}
					}
				}
				break;

			case LVN_COLUMNCLICK:
				LPNMLISTVIEW lv = (LPNMLISTVIEW)lParam;
				ColumnsSortParams params;
				params.hList = GetDlgItem(hwnd, IDC_VARS);
				params.column = lv->iSubItem;
				params.last = lastColumn;
				ListView_SortItemsEx(params.hList, ColumnsCompare, (LPARAM)&params);
				lastColumn = (params.column == lastColumn) ? -1 : params.column;
				break;
			}
			break;
		}
		break;

	case WM_DESTROY:
		ListView_DeleteAllItems(GetDlgItem(hwnd, IDC_VARS));
		saveListSettings(GetDlgItem(hwnd, IDC_VARS), csWatchList);
		Utils_SaveWindowPosition(hwnd, NULL, modname, "Watch_");
		hwnd2watchedVarsWindow = NULL;
		break;
	}
	return 0;
}

void openWatchedVarWindow()
{
	if (!hwnd2watchedVarsWindow) 
		CreateDialog(hInst, MAKEINTRESOURCE(IDD_WATCH_DIAG), NULL, WatchDlgProc);
	else
		SetForegroundWindow(hwnd2watchedVarsWindow);
}


void popupWatchedVar(MCONTACT hContact, const char *module, const char *setting)
{
	COLORREF colorBack = db_get_dw(NULL, modname, "PopupColour", RGB(255, 0, 0));
	COLORREF colorText = db_get_dw(NULL, modname, "PopupTextColour", RGB(0, 0, 0));
	int timeout = db_get_b(NULL, modname, "PopupDelay", 3);

	wchar_t name[NAME_SIZE], text[MAX_SECONDLINE], value[MAX_SECONDLINE];
	GetContactName(hContact, NULL, name, _countof(name));

	// 2nd line
	int type = GetValue(hContact, module, setting, value, _countof(value));
	mir_snwprintf(text, TranslateT("Database Setting Changed: \nModule: \"%s\", Setting: \"%s\"\nNew Value (%s): \"%s\""), _A2T(module), _A2T(setting), DBVType(type), value);

	POPUPDATAT ppd = { 0 };
	ppd.lchContact = (MCONTACT)hContact;
	ppd.lchIcon = LoadIcon(hInst, MAKEINTRESOURCE(ICO_REGEDIT));
	mir_wstrncpy(ppd.lptzContactName, name, _countof(ppd.lptzContactName));
	mir_wstrncpy(ppd.lptzText, text, _countof(ppd.lptzText));
	ppd.colorBack = colorBack;
	ppd.colorText = colorText;
	ppd.iSeconds = timeout ? timeout : -1;
	PUAddPopupT(&ppd);
}