/*
Avatar History Plugin
 Copyright (C) 2006  Matthew Wild - Email: mwild1@gmail.com

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

*/

#include "AvatarHistory.h"

extern HINSTANCE hInst;
HANDLE hMenu = NULL; 
DWORD WINAPI AvatarDialogThread(LPVOID param);
static INT_PTR CALLBACK AvatarDlgProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
int ShowSaveDialog(HWND hwnd, TCHAR* fn,HANDLE hContact = NULL);

BOOL ProtocolEnabled(const char *proto);
int FillAvatarListFromDB(HWND list, HANDLE hContact);
int FillAvatarListFromFolder(HWND list, HANDLE hContact);
int CleanupAvatarPic(HWND hwnd);
BOOL UpdateAvatarPic(HWND hwnd);
TCHAR* GetCurrentSelFile(HWND list);
TCHAR * GetContactFolder(TCHAR *fn, HANDLE hContact);
BOOL ResolveShortcut(TCHAR *shortcut, TCHAR *file);

static INT_PTR ShowDialogSvc(WPARAM wParam, LPARAM lParam);
extern HANDLE hServices[];
extern HANDLE hHooks[];

struct AvatarDialogData
{
	HANDLE hContact;
	TCHAR fn[MAX_PATH];
	HWND parent;
};


class ListEntry
{
public:
	ListEntry()
	{
		dbe = NULL;
		filename = NULL;
		filelink = NULL;
	}

	~ListEntry()
	{
		mir_free(filename);
		mir_free(filelink);
	}

	HANDLE dbe;
	TCHAR *filename;
	TCHAR *filelink;
};

int OpenAvatarDialog(HANDLE hContact, char* fn)
{
	HWND hAvatarWindow = WindowList_Find(hAvatarWindowsList, hContact);
	if (hAvatarWindow)
	{
		SetForegroundWindow(hAvatarWindow);
		SetFocus(hAvatarWindow);
		return 0;
	}

	DWORD dwId;
	struct AvatarDialogData *avdlg = (struct AvatarDialogData*)malloc(sizeof(struct AvatarDialogData));
	ZeroMemory(avdlg, sizeof(struct AvatarDialogData));
	avdlg->hContact = hContact;
	if (fn == NULL)
	{
		avdlg->fn[0] = _T('\0');
	}
	else
	{
#ifdef _UNICODE
		MultiByteToWideChar(CP_ACP, 0, fn, -1, avdlg->fn, MAX_REGS(avdlg->fn));
#else
		lstrcpyn(avdlg->fn, fn, sizeof(avdlg->fn));
#endif
	}

	CloseHandle(CreateThread(NULL, 0, AvatarDialogThread, (LPVOID)avdlg, 0, &dwId));
	return 0;
}

DWORD WINAPI AvatarDialogThread(LPVOID param)
{
	struct AvatarDialogData* data = (struct AvatarDialogData*)param;
	DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_AVATARDLG), data->parent, AvatarDlgProc, (LPARAM)param);
	return 0;
}

void EnableDisableControls(HWND hwnd)
{
	HWND list = GetDlgItem(hwnd, IDC_AVATARLIST);
	
	int cursel = SendMessage(list, LB_GETCURSEL, 0, 0);
	int count = SendMessage(list, LB_GETCOUNT, 0, 0);

	if (cursel == LB_ERR)
	{
		EnableWindow(GetDlgItem(hwnd, IDC_BACK), count > 0);
		EnableWindow(GetDlgItem(hwnd, IDC_NEXT), count > 0);
	}
	else
	{
		EnableWindow(GetDlgItem(hwnd, IDC_BACK), cursel > 0);
		EnableWindow(GetDlgItem(hwnd, IDC_NEXT), cursel < count-1);
	}
}

static INT_PTR CALLBACK AvatarDlgProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
	switch(uMsg)
	{
		case WM_INITDIALOG:
		{
			AvatarDialogData *data = (struct AvatarDialogData*) lParam;
			SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM) createDefaultOverlayedIcon(TRUE));
			SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM) createDefaultOverlayedIcon(FALSE));
			if (db_byte_get(NULL, MODULE_NAME, "LogToHistory", AVH_DEF_LOGTOHISTORY))
				FillAvatarListFromDB(GetDlgItem(hwnd, IDC_AVATARLIST), data->hContact);
			else if (opts.log_per_contact_folders)
				FillAvatarListFromFolder(GetDlgItem(hwnd, IDC_AVATARLIST), data->hContact);
			TCHAR *displayName = (TCHAR*) CallService(MS_CLIST_GETCONTACTDISPLAYNAME,(WPARAM) data->hContact,GCDNF_TCHAR);
			if(displayName)
			{
				TCHAR title[MAX_PATH];
				mir_sntprintf(title,MAX_PATH,TranslateT("Avatar History for %s"),displayName);
				SetWindowText(hwnd,title);
			}

			SetWindowLongPtr(hwnd, GWLP_USERDATA, (ULONG_PTR)data->hContact);
			UpdateAvatarPic(hwnd);
			CheckDlgButton(hwnd, IDC_LOGUSER, (UINT)db_byte_get(data->hContact, MODULE_NAME, "LogToDisk", BST_INDETERMINATE));
			CheckDlgButton(hwnd, IDC_POPUPUSER, (UINT)db_byte_get(data->hContact, MODULE_NAME, "AvatarPopups", BST_INDETERMINATE));
			CheckDlgButton(hwnd, IDC_HISTORYUSER, (UINT)db_byte_get(data->hContact, MODULE_NAME, "LogToHistory", BST_INDETERMINATE));
			ShowWindow(GetDlgItem(hwnd, IDC_OPENFOLDER), opts.log_per_contact_folders ? SW_SHOW : SW_HIDE);
			Utils_RestoreWindowPositionNoSize(hwnd,NULL,MODULE_NAME,"AvatarHistoryDialog");
			WindowList_Add(hAvatarWindowsList,hwnd,data->hContact);
			TranslateDialogDefault(hwnd);
			EnableDisableControls(hwnd);
			free(data);
			data = NULL;
			break;
		}
		case WM_CLOSE:
		{
			CleanupAvatarPic(hwnd);
			EndDialog(hwnd, 0);
			return TRUE;
		}
		case WM_DESTROY:
		{
			Utils_SaveWindowPosition(hwnd,NULL,MODULE_NAME,"AvatarHistoryDialog");
			WindowList_Remove(hAvatarWindowsList,hwnd);
			DestroyIcon((HICON)SendMessage(hwnd, WM_SETICON, ICON_BIG, 0));
			DestroyIcon((HICON)SendMessage(hwnd, WM_SETICON, ICON_SMALL, 0));
			HWND list = GetDlgItem(hwnd, IDC_AVATARLIST);
			int count = SendMessage(list, LB_GETCOUNT, 0, 0);
			for(int i = 0; i < count; i++)
				delete (ListEntry *) SendMessage(list, LB_GETITEMDATA, i, 0);
			break;
		}
		case WM_CONTEXTMENU:
		{
			HWND list = GetDlgItem(hwnd, IDC_AVATARLIST);
			HWND pic = GetDlgItem(hwnd, IDC_AVATAR);
			int pos;

			if ((HANDLE) wParam == list)
			{
				POINT p;
				p.x = LOWORD(lParam); 
				p.y = HIWORD(lParam); 

				ScreenToClient(list, &p);

				pos = SendMessage(list, LB_ITEMFROMPOINT, 0, MAKELONG(p.x, p.y));
				if (HIWORD(pos))
					break;
				pos = LOWORD(pos);

				int count = SendMessage(list, LB_GETCOUNT, 0, 0);
				if (pos >= count)
					break;

				SendMessage(list, LB_SETCURSEL, pos, 0);
				EnableDisableControls(hwnd);
			}
			else if ((HANDLE) wParam == pic)
			{
				pos = SendMessage(list, LB_GETCURSEL, 0, 0);
				if (pos == LB_ERR)
					break;
			}
			else
				break;

			HMENU menu = LoadMenu(hInst, MAKEINTRESOURCE(IDR_MENU1));
			HMENU submenu = GetSubMenu(menu, 0);
			TranslateMenu(submenu);

			if (!UpdateAvatarPic(hwnd))
			{
				RemoveMenu(submenu, 2, MF_BYPOSITION);
				RemoveMenu(submenu, 0, MF_BYPOSITION);
			}

			POINT p;
			p.x = LOWORD(lParam); 
			p.y = HIWORD(lParam); 
			int ret = TrackPopupMenu(submenu, TPM_TOPALIGN|TPM_LEFTALIGN|TPM_RIGHTBUTTON|TPM_RETURNCMD, p.x, p.y, 0, list, NULL);
			DestroyMenu(menu);

			switch(ret)
			{
				case ID_AVATARLISTPOPUP_SAVEAS:
				{
					HANDLE hContact = (HANDLE) GetWindowLongPtr(hwnd, GWLP_USERDATA);
					ListEntry *le = (ListEntry*) SendMessage(list, LB_GETITEMDATA, pos, 0);
					ShowSaveDialog(hwnd, le->filename, hContact);
					break;
				}
				case ID_AVATARLISTPOPUP_DELETE:
				{
					ListEntry *le = (ListEntry*) SendMessage(list, LB_GETITEMDATA, pos, 0);

					BOOL blDelete;

					if(le->dbe)
						blDelete = MessageBox(hwnd, TranslateT("Are you sure you wish to delete this history entry?\nOnly the entry in history will be deleted, bitmap file will be kept!"), 
									TranslateT("Delete avatar log?"), MB_YESNO|MB_ICONWARNING|MB_DEFBUTTON2|MB_SETFOREGROUND|MB_TOPMOST) == IDYES;
					else
						blDelete = MessageBox(hwnd, TranslateT("Are you sure you wish to delete this avatar shortcut?\nOnly shortcut will be deleted, bitmap file will be kept!"), 
									TranslateT("Delete avatar log?"), MB_YESNO|MB_ICONWARNING|MB_DEFBUTTON2|MB_SETFOREGROUND|MB_TOPMOST) == IDYES;
					
					if (blDelete)
					{
						HANDLE hContact = (HANDLE) GetWindowLongPtr(hwnd, GWLP_USERDATA);

						if(le->dbe)
							CallService(MS_DB_EVENT_DELETE, (WPARAM) hContact, (LPARAM) le->dbe);
						else
							DeleteFile(le->filelink);

						delete le;

						SendMessage(list, LB_DELETESTRING, pos, 0);

						int count = SendMessage(list, LB_GETCOUNT, 0, 0);
						if (count > 0)
						{
							if (pos >= count)
								pos = count -1;
							SendMessage(list, LB_SETCURSEL, pos, 0);
						}

						UpdateAvatarPic(hwnd);
						EnableDisableControls(hwnd);
					}
					break;
				}
				case ID_AVATARLISTPOPUP_DELETE_BOTH:
				{
					ListEntry *le = (ListEntry*) SendMessage(list, LB_GETITEMDATA, pos, 0);

					BOOL blDelete;

					if(le->dbe)
						blDelete = MessageBox(hwnd, TranslateT("Are you sure you wish to delete this archived avatar?\nThis will delete the history entry and the bitmap file.\nWARNING:This can affect more than one entry in history!"), 
									TranslateT("Delete avatar?"), MB_YESNO|MB_ICONWARNING|MB_DEFBUTTON2|MB_SETFOREGROUND|MB_TOPMOST) == IDYES;
					else
						blDelete = MessageBox(hwnd, TranslateT("Are you sure you wish to delete this archived avatar?\nThis will delete the shortcut and the bitmap file.\nWARNING:This can affect more than one shortcut!"), 
									TranslateT("Delete avatar?"), MB_YESNO|MB_ICONWARNING|MB_DEFBUTTON2|MB_SETFOREGROUND|MB_TOPMOST) == IDYES;

					if (blDelete)
					{
						HANDLE hContact = (HANDLE) GetWindowLongPtr(hwnd, GWLP_USERDATA);

						DeleteFile(le->filename);

						if(le->dbe)
							CallService(MS_DB_EVENT_DELETE, (WPARAM) hContact, (LPARAM) le->dbe);
						else
							DeleteFile(le->filelink);

						delete le;

						SendMessage(list, LB_DELETESTRING, pos, 0);

						int count = SendMessage(list, LB_GETCOUNT, 0, 0);
						if (count > 0)
						{
							if (pos >= count)
								pos = count -1;
							SendMessage(list, LB_SETCURSEL, pos, 0);
						}

						UpdateAvatarPic(hwnd);
						EnableDisableControls(hwnd);
					}
					break;
				}
			}
			break;
		}
		case WM_COMMAND:
		{
			switch(LOWORD(wParam))
			{
			case IDOK:
				if(HIWORD(wParam) == BN_CLICKED)
				{
					HANDLE hContact = (HANDLE) GetWindowLongPtr(hwnd, GWLP_USERDATA);

					db_byte_set(hContact, MODULE_NAME, "AvatarPopups", (BYTE) IsDlgButtonChecked(hwnd, IDC_POPUPUSER));
					db_byte_set(hContact, MODULE_NAME, "LogToDisk", (BYTE) IsDlgButtonChecked(hwnd, IDC_LOGUSER));
					db_byte_set(hContact, MODULE_NAME, "LogToHistory", (BYTE) IsDlgButtonChecked(hwnd, IDC_HISTORYUSER));

					CleanupAvatarPic(hwnd);
					EndDialog(hwnd, 0);
					return TRUE;
				}
				break;
			case IDC_AVATARLIST:
				if(HIWORD(wParam) == LBN_SELCHANGE)
				{
					UpdateAvatarPic(hwnd);
					EnableDisableControls(hwnd);
					return TRUE;
				}
				break;
			case IDC_OPENFOLDER:
				if(HIWORD(wParam) == BN_CLICKED)
				{
					if (opts.log_per_contact_folders)
					{
						TCHAR avfolder[MAX_PATH];
						HANDLE hContact = (HANDLE)GetWindowLongPtr(hwnd, GWLP_USERDATA);
						GetContactFolder(avfolder, hContact);
						ShellExecute(NULL, db_byte_get(NULL, MODULE_NAME, "OpenFolderMethod", 0) ? _T("explore") : _T("open"), avfolder, NULL, NULL, SW_SHOWNORMAL);
						return TRUE;
					}
				}
				break;
			case IDC_NEXT:
				if(HIWORD(wParam) == BN_CLICKED)
				{
					HWND list = GetDlgItem(hwnd, IDC_AVATARLIST);
					SendMessage(list, LB_SETCURSEL, SendMessage(list, LB_GETCURSEL, 0, 0) +1, 0);
					UpdateAvatarPic(hwnd);
					EnableDisableControls(hwnd);
					return TRUE;
				}
				break;
			case IDC_BACK:
				if(HIWORD(wParam) == BN_CLICKED)
				{
					HWND list = GetDlgItem(hwnd, IDC_AVATARLIST);
					int cursel = SendMessage(list, LB_GETCURSEL, 0, 0);
					if (cursel == LB_ERR)
						SendMessage(list, LB_SETCURSEL, SendMessage(list, LB_GETCOUNT, 0, 0) -1, 0);
					else
						SendMessage(list, LB_SETCURSEL, cursel -1, 0);
					UpdateAvatarPic(hwnd);
					EnableDisableControls(hwnd);
					return TRUE;
				}
				break;
			}
			break;
		}
	}
	return FALSE;
}



int FillAvatarListFromFolder(HWND list, HANDLE hContact)
{
	int max_pos = 0;
	TCHAR dir[MAX_PATH], path[MAX_PATH], lnk[MAX_PATH];
	WIN32_FIND_DATA finddata;

	GetContactFolder(dir, hContact);
	mir_sntprintf(path, MAX_PATH, _T("%s\\*.lnk"), dir);

	HANDLE hFind = FindFirstFile(path, &finddata);
	if (hFind == INVALID_HANDLE_VALUE)
		return 0;

	do
	{
		if(finddata.cFileName[0] != '.')
		{
			mir_sntprintf(lnk, MAX_PATH, _T("%s\\%s"), dir, finddata.cFileName);
			if (ResolveShortcut(lnk, path))
			{
				// Add to list
				ListEntry *le = new ListEntry();
				le->filename = mir_tstrdup(path);
				le->filelink = mir_tstrdup(lnk);

				TCHAR *p = _tcschr(finddata.cFileName, _T('.'));
				if (p != NULL)
					p[0] = _T('\0');
				max_pos = SendMessage(list, LB_ADDSTRING, 0, (LPARAM) finddata.cFileName);
				SendMessage(list, LB_SETITEMDATA, max_pos, (LPARAM) le);
			}
		}
	} while(FindNextFile(hFind, &finddata));
	FindClose(hFind);
	SendMessage(list, LB_SETCURSEL, max_pos, 0); // Set to first item
	return 0;
}



int FillAvatarListFromDB(HWND list, HANDLE hContact)
{
	int max_pos = 0;
	BYTE blob[2048];
	HANDLE dbe = (HANDLE) CallService(MS_DB_EVENT_FINDFIRST, (WPARAM) hContact, 0);
	while(dbe != NULL)
	{
		DBEVENTINFO dbei = {0};
		dbei.cbSize = sizeof(dbei);
		dbei.cbBlob = sizeof(blob);
		dbei.pBlob = blob;
		if (CallService(MS_DB_EVENT_GET, (WPARAM) dbe, (LPARAM) &dbei) == 0
			&& dbei.eventType == EVENTTYPE_AVATAR_CHANGE)
		{

			// Get last char from blob
			int i = dbei.cbBlob - 2;
			for(; i >= 0 && dbei.pBlob[i] != 0; i--) ;

			if (i != (int) dbei.cbBlob - 2 && i >= 0)
			{
				// Oki, found one

				// Get time
				TCHAR date[64];
				DBTIMETOSTRINGT tts = {0};
				tts.szFormat = _T("d s");
				tts.szDest = date;
				tts.cbDest = sizeof(date);
				CallService(MS_DB_TIME_TIMESTAMPTOSTRINGT, (WPARAM) dbei.timestamp, (LPARAM) &tts);

				// Get file in disk
				char path[MAX_PATH] = "";
				CallService(MS_UTILS_PATHTOABSOLUTE,(WPARAM) (char *) &dbei.pBlob[i+1],(LPARAM)path);
				TCHAR *filename = mir_a2t(path);

				// Add to list
				ListEntry *le = new ListEntry();
				le->dbe = dbe;
				le->filename = filename;

				max_pos = SendMessage(list,LB_ADDSTRING, 0, (LPARAM) date);
				SendMessage(list, LB_SETITEMDATA, max_pos, (LPARAM) le);
			}
		}

		dbe = (HANDLE) CallService(MS_DB_EVENT_FINDNEXT, (WPARAM) dbe, 0);
	}

	SendMessage(list, LB_SETCURSEL, max_pos, 0); // Set to first item
	return 0;
}

BOOL UpdateAvatarPic(HWND hwnd)
{
	HWND hwndpic = GetDlgItem(hwnd, IDC_AVATAR);
	if (!hwnd || !hwndpic)
		return -1;

	HWND list = GetDlgItem(hwnd, IDC_AVATARLIST);
	TCHAR *filename = GetCurrentSelFile(list);
	if(!filename)
	{
		SetDlgItemText(hwnd,IDC_AVATARPATH,TranslateT("avatar path is null."));
		return 0;
	}
	SetDlgItemText(hwnd,IDC_AVATARPATH,filename);

	HBITMAP avpic = (HBITMAP) CallService(MS_IMG_LOAD, (WPARAM) filename, IMGL_TCHAR);

	BOOL found_image = (avpic != NULL);

	avpic = (HBITMAP)SendMessage(hwndpic, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)avpic);
	if (avpic)
		DeleteObject(avpic);

	return found_image;
}

int CleanupAvatarPic(HWND hwnd)
{
	HWND hwndpic = GetDlgItem(hwnd, IDC_AVATAR);
	if (!hwnd || !hwndpic)
		return -1;

	HBITMAP avpic = (HBITMAP)SendMessage(hwndpic, STM_GETIMAGE, 0, 0);
	if(avpic)
		DeleteObject(avpic);
	return 0;
}

int PreBuildContactMenu(WPARAM wParam,LPARAM lParam) 
{
	CLISTMENUITEM clmi = {0};
	clmi.cbSize = sizeof(clmi);
	clmi.flags = CMIM_FLAGS;

	char *proto = (char*) CallService(MS_PROTO_GETCONTACTBASEPROTO, wParam, 0);
	if (!ProtocolEnabled(proto))
		clmi.flags |= CMIF_HIDDEN;

	CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hMenu, (LPARAM) &clmi);

	return 0;
}

void InitMenuItem()
{
	CLISTMENUITEM mi = {0};

	hServices[2] = CreateServiceFunction(MS_AVATARHISTORY_SHOWDIALOG, ShowDialogSvc);

	mi.cbSize = sizeof(mi);
	mi.ptszName = LPGENT("View Avatar History");
	mi.flags = CMIF_TCHAR;
	mi.position = 1000090010;
	mi.hIcon = createDefaultOverlayedIcon(FALSE);
	mi.pszService = MS_AVATARHISTORY_SHOWDIALOG;
	hMenu = Menu_AddContactMenuItem(&mi);
	DestroyIcon(mi.hIcon);
}

static INT_PTR ShowDialogSvc(WPARAM wParam, LPARAM lParam)
{
	OpenAvatarDialog((HANDLE)wParam, (char*)lParam);
	return 0;
}

TCHAR* GetCurrentSelFile(HWND list)
{
	int cursel = SendMessage(list, LB_GETCURSEL, 0, 0);
	if (cursel > -1)
		return ((ListEntry*) SendMessage(list, LB_GETITEMDATA, cursel, 0))->filename;
	else
		return NULL;
}

int ShowSaveDialog(HWND hwnd, TCHAR* fn, HANDLE hContact)
{
	TCHAR filter[MAX_PATH];
	TCHAR file[MAX_PATH];
	OPENFILENAME ofn;
	ZeroMemory(&ofn, sizeof(OPENFILENAME));
	DBVARIANT dbvInitDir = {0};
	bool ret = (DBGetContactSettingTString(hContact,MODULE_NAME,"SavedAvatarFolder",&dbvInitDir)== 0);
	ofn.lStructSize = sizeof(OPENFILENAME);
	ofn.hwndOwner = hwnd;
	ofn.hInstance = hInst;

	CallService(MS_UTILS_GETBITMAPFILTERSTRINGST, MAX_PATH, (LPARAM)filter);
	ofn.lpstrFilter = filter;
	
	ofn.nFilterIndex = 1;
	lstrcpyn(file, _tcsrchr(fn, '\\')+1, sizeof(file));
	ofn.lpstrFile = file;

	TCHAR *displayName = (TCHAR*) CallService(MS_CLIST_GETCONTACTDISPLAYNAME,(WPARAM)hContact,GCDNF_TCHAR);
	if(displayName)
	{
		TCHAR title[MAX_PATH];
		mir_sntprintf(title,sizeof(title),TranslateT("Save Avatar for %s"),displayName);
		ofn.lpstrTitle = title;
	}
	else
	{
		ofn.lpstrTitle = TranslateT("Save Avatar");
	}
	ofn.nMaxFile = MAX_PATH;
	ofn.Flags = OFN_PATHMUSTEXIST | OFN_DONTADDTORECENT;
	ofn.lpstrDefExt = _tcsrchr(fn, '.')+1;
	if(ret)
	{
		ofn.lpstrInitialDir = dbvInitDir.ptszVal;
		DBFreeVariant(&dbvInitDir);
	}
	else
	{
		ofn.lpstrInitialDir = _T(".");
	}
	if(GetSaveFileName(&ofn))
	{
		CopyFile(fn, file, FALSE);
		DBWriteContactSettingTString(hContact,MODULE_NAME,"SavedAvatarFolder",file);
	}
	return 0;
}