/*

Miranda IM: the free IM client for Microsoft* Windows*

Copyright 2000-2009 Miranda ICQ/IM project, 
all portions of this codebase are copyrighted to the people
listed in contributors.txt.

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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
#include "..\..\core\commonheaders.h"
#include <io.h>

struct StandardIconDescription
{
	int    id;
	const char*  description;
	int    resource_id;
	int    pf2;
	const char*  section;
};

static const struct StandardIconDescription mainIcons[] =
{
	{ SKINICON_OTHER_MIRANDA,         LPGEN("Miranda IM"),          -IDI_MIRANDA        }, 
	{ SKINICON_EVENT_MESSAGE,         LPGEN("Message"),             -IDI_RECVMSG        }, 
	{ SKINICON_EVENT_URL,             LPGEN("URL"),                 -IDI_URL            }, 
	{ SKINICON_EVENT_FILE,            LPGEN("File"),                -IDI_FILE           }, 
	{ SKINICON_OTHER_USERONLINE,      LPGEN("User Online"),         -IDI_USERONLINE     }, 
	{ SKINICON_OTHER_GROUPOPEN,       LPGEN("Group (Open)"),        -IDI_GROUPOPEN      }, 
	{ SKINICON_OTHER_GROUPSHUT,       LPGEN("Group (Closed)"),      -IDI_GROUPSHUT      }, 
	{ SKINICON_OTHER_CONNECTING,      LPGEN("Connecting"),          -IDI_LOAD           }, 
	{ SKINICON_OTHER_ADDCONTACT,      LPGEN("Add Contact"),         -IDI_ADDCONTACT     }, 
	{ SKINICON_OTHER_USERDETAILS,     LPGEN("User Details"),        -IDI_USERDETAILS    }, 
	{ SKINICON_OTHER_HISTORY,         LPGEN("History"),             -IDI_HISTORY        }, 
	{ SKINICON_OTHER_DOWNARROW,       LPGEN("Down Arrow"),          -IDI_DOWNARROW      }, 
	{ SKINICON_OTHER_FINDUSER,        LPGEN("Find User"),           -IDI_FINDUSER       }, 
	{ SKINICON_OTHER_OPTIONS,         LPGEN("Options"),             -IDI_OPTIONS        }, 
	{ SKINICON_OTHER_SENDEMAIL,       LPGEN("Send E-mail"),         -IDI_SENDEMAIL      }, 
	{ SKINICON_OTHER_DELETE,          LPGEN("Delete"),              -IDI_DELETE         }, 
	{ SKINICON_OTHER_RENAME,          LPGEN("Rename"),              -IDI_RENAME         }, 
	{ SKINICON_OTHER_SMS,             LPGEN("SMS"),                 -IDI_SMS            }, 
	{ SKINICON_OTHER_SEARCHALL,       LPGEN("Search All"),          -IDI_SEARCHALL      }, 
	{ SKINICON_OTHER_TICK,            LPGEN("Tick"),                -IDI_TICK           }, 
	{ SKINICON_OTHER_NOTICK,          LPGEN("No Tick"),             -IDI_NOTICK         }, 
	{ SKINICON_OTHER_HELP,            LPGEN("Help"),                -IDI_HELP           }, 
	{ SKINICON_OTHER_MIRANDAWEB,      LPGEN("Miranda Website"),     -IDI_MIRANDAWEBSITE }, 
	{ SKINICON_OTHER_TYPING,          LPGEN("Typing"),              -IDI_TYPING         }, 
	{ SKINICON_OTHER_SMALLDOT,        LPGEN("Small Dot"),           -IDI_SMALLDOT       }, 
	{ SKINICON_OTHER_FILLEDBLOB,      LPGEN("Filled Blob"),         -IDI_FILLEDBLOB     }, 
	{ SKINICON_OTHER_EMPTYBLOB,       LPGEN("Empty Blob"),          -IDI_EMPTYBLOB      }, 
	{ SKINICON_OTHER_UNICODE,         LPGEN("Unicode plugin"),      -IDI_UNICODE        }, 
	{ SKINICON_OTHER_ANSI,            LPGEN("ANSI plugin"),         -IDI_ANSI           }, 
	{ SKINICON_OTHER_LOADED,          LPGEN("Running plugin"),      -IDI_LOADED         }, 
	{ SKINICON_OTHER_NOTLOADED,       LPGEN("Unloaded plugin"),     -IDI_NOTLOADED      }, 
	{ SKINICON_OTHER_UNDO, 	          LPGEN("Undo"),                -IDI_UNDO           }, 
	{ SKINICON_OTHER_WINDOW,          LPGEN("Window"),              -IDI_WINDOW         }, 
	{ SKINICON_OTHER_WINDOWS,         LPGEN("System"),              -IDI_WINDOWS        }, 
	{ SKINICON_OTHER_ACCMGR,          LPGEN("Accounts"),            -IDI_ACCMGR         }, 
	{ SKINICON_OTHER_SHOWHIDE,        LPGEN("ShowHide"),            -IDI_SHOWHIDE       }, 
	{ SKINICON_OTHER_EXIT,            LPGEN("Exit"),                -IDI_EXIT           }, 
	{ SKINICON_OTHER_MAINMENU,        LPGEN("Main Menu"),           -IDI_MIRANDA        }, 
	{ SKINICON_OTHER_STATUS,          LPGEN("Status"),              -IDI_ONLINE         }, 
	{ SKINICON_CHAT_JOIN,             LPGEN("Join chat"),           -IDI_JOINCHAT       }, 
	{ SKINICON_CHAT_LEAVE,            LPGEN("Leave chat"),          -IDI_LEAVECHAT      }, 
	{ SKINICON_OTHER_GROUP,           LPGEN("Move to Group"),       -IDI_MOVETOGROUP    },
	{ SKINICON_OTHER_ON,              LPGEN("On"),                  -IDI_ON             }, 
	{ SKINICON_OTHER_OFF,             LPGEN("Off"),                 -IDI_OFF            },
	{ SKINICON_OTHER_LOADEDGRAY,      LPGEN("Running core plugin"), -IDI_LOADED_GRAY    }, 
	{ SKINICON_OTHER_NOTLOADEDGRAY,   LPGEN("Non-loadable plugin"), -IDI_NOTLOADED_GRAY }, 
	{ SKINICON_OTHER_STATUS_LOCKED,   LPGEN("Locked status"),       -IDI_STATUS_LOCKED, 0, "Status Icons" }, 
};

HANDLE hMainIcons[SIZEOF(mainIcons)];

static const struct StandardIconDescription statusIcons[] =
{
	{ ID_STATUS_OFFLINE,         LPGEN("Offline"),          -IDI_OFFLINE,       0xFFFFFFFF     }, 
	{ ID_STATUS_ONLINE,          LPGEN("Online"),           -IDI_ONLINE,        PF2_ONLINE     }, 
	{ ID_STATUS_AWAY,            LPGEN("Away"),             -IDI_AWAY,          PF2_SHORTAWAY  }, 
	{ ID_STATUS_NA,              LPGEN("NA"),               -IDI_NA,            PF2_LONGAWAY   }, 
	{ ID_STATUS_OCCUPIED,        LPGEN("Occupied"),         -IDI_OCCUPIED,      PF2_LIGHTDND   }, 
	{ ID_STATUS_DND,             LPGEN("DND"),              -IDI_DND,           PF2_HEAVYDND   }, 
	{ ID_STATUS_FREECHAT,        LPGEN("Free for chat"),    -IDI_FREE4CHAT,     PF2_FREECHAT   }, 
	{ ID_STATUS_INVISIBLE,       LPGEN("Invisible"),        -IDI_INVISIBLE,     PF2_INVISIBLE  }, 
	{ ID_STATUS_ONTHEPHONE,      LPGEN("On the phone"),     -IDI_ONTHEPHONE,    PF2_ONTHEPHONE }, 
	{ ID_STATUS_OUTTOLUNCH,      LPGEN("Out to lunch"),     -IDI_OUTTOLUNCH,    PF2_OUTTOLUNCH }
};

HANDLE hStatusIcons[SIZEOF(statusIcons)];

const char* mainIconsFmt   = "core_main_";
const char* statusIconsFmt = "core_status_";
const char* protoIconsFmt  = LPGEN("%s Icons");

#define PROTOCOLS_PREFIX "Status Icons/"
#define GLOBAL_PROTO_NAME "*"




// load small icon (shared) it's not need to be destroyed

static HICON LoadSmallIconShared(HINSTANCE hInstance, LPCTSTR lpIconName)
{
	int cx = GetSystemMetrics(SM_CXSMICON);
	return (HICON)LoadImage(hInstance, lpIconName, IMAGE_ICON, cx, cx, LR_DEFAULTCOLOR | LR_SHARED);
}

// load small icon (not shared) it IS NEED to be destroyed
static HICON LoadSmallIcon(HINSTANCE hInstance, LPCTSTR lpIconName)
{
	HICON hIcon = NULL;				  // icon handle
	int index = -(int)lpIconName;
	TCHAR filename[MAX_PATH] = {0};
	GetModuleFileName(hInstance, filename, MAX_PATH);
	ExtractIconEx(filename, index, NULL, &hIcon, 1);
	return hIcon;
}

// load small icon from hInstance
HICON LoadIconEx(HINSTANCE hInstance, LPCTSTR lpIconName, BOOL bShared)
{
	HICON hResIcon = bShared ? LoadSmallIcon(hInstance, lpIconName) : LoadSmallIconShared(hInstance, lpIconName);
	if ( !hResIcon) { //Icon not found in hInstance lets try to load it from core
		HINSTANCE hCoreInstance=hMirandaInst;
		if (hCoreInstance != hInstance)
			hResIcon = bShared ? LoadSmallIcon(hCoreInstance, lpIconName) : LoadSmallIconShared(hCoreInstance, lpIconName);
	}
	return hResIcon;
}

int ImageList_AddIcon_NotShared(HIMAGELIST hIml, LPCTSTR szResource)
{
	HICON hTempIcon=LoadIconEx(hMirandaInst, szResource, 0);
	int res = ImageList_AddIcon(hIml, hTempIcon);
	Safe_DestroyIcon(hTempIcon);
	return res;
}

int ImageList_AddIcon_IconLibLoaded(HIMAGELIST hIml, int iconId)
{
	HICON hIcon = LoadSkinIcon(iconId);
	int res = ImageList_AddIcon(hIml, hIcon);
	IconLib_ReleaseIcon(hIcon, 0);
	return res;
}

int ImageList_AddIcon_ProtoIconLibLoaded(HIMAGELIST hIml, const char* szProto, int iconId)
{
	HICON hIcon = LoadSkinProtoIcon(szProto, iconId);
	int res = ImageList_AddIcon(hIml, hIcon);
	IconLib_ReleaseIcon(hIcon, 0);
	return res;
}

int ImageList_ReplaceIcon_NotShared(HIMAGELIST hIml, int iIndex, HINSTANCE hInstance, LPCTSTR szResource)
{
	HICON hTempIcon = LoadIconEx(hInstance, szResource, 0);
	int res = ImageList_ReplaceIcon(hIml, iIndex, hTempIcon);
	Safe_DestroyIcon(hTempIcon);
	return res;
}

int ImageList_ReplaceIcon_IconLibLoaded(HIMAGELIST hIml, int nIndex, HICON hIcon)
{
	int res = ImageList_ReplaceIcon(hIml, nIndex, hIcon);
	IconLib_ReleaseIcon(hIcon, 0);
	return res;
}

void Window_SetIcon_IcoLib(HWND hWnd, int iconId)
{
	SendMessage(hWnd, WM_SETICON, ICON_BIG,   (LPARAM)LoadSkinIcon(iconId, true));
	SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM)LoadSkinIcon(iconId));
}

void Window_SetProtoIcon_IcoLib(HWND hWnd, const char* szProto, int iconId)
{
	SendMessage(hWnd, WM_SETICON, ICON_BIG,   (LPARAM)LoadSkinProtoIcon(szProto, iconId, true));
	SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM)LoadSkinProtoIcon(szProto, iconId));
}

void Window_FreeIcon_IcoLib(HWND hWnd)
{
	IconLib_ReleaseIcon((HICON)SendMessage(hWnd, WM_SETICON, ICON_BIG, 0), NULL);
	IconLib_ReleaseIcon((HICON)SendMessage(hWnd, WM_SETICON, ICON_SMALL, 0), NULL);
}

void Button_SetIcon_IcoLib(HWND hwndDlg, int itemId, int iconId, const char* tooltip)
{
	HWND hWnd = GetDlgItem(hwndDlg, itemId);
	SendMessage(hWnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)LoadSkinIcon(iconId));
	SendMessage(hWnd, BUTTONSETASFLATBTN, TRUE, 0);
	SendMessage(hWnd, BUTTONADDTOOLTIP, (WPARAM)tooltip, 0);
}

void Button_FreeIcon_IcoLib(HWND hwndDlg, int itemId)
{
	HICON hIcon = (HICON)SendDlgItemMessage(hwndDlg, itemId, BM_SETIMAGE, IMAGE_ICON, 0);
	IconLib_ReleaseIcon(hIcon, 0);
}

//
//  wParam = szProto
//  lParam = status
//
HICON LoadSkinProtoIcon(const char* szProto, int status, bool big)
{
	int i, statusIndx = -1;
	char iconName[MAX_PATH];
	HICON hIcon;
	DWORD caps2 = (szProto == NULL) ? (DWORD)-1 : CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_2, 0);

	if (status >= ID_STATUS_CONNECTING && status < ID_STATUS_CONNECTING+MAX_CONNECT_RETRIES) {
		mir_snprintf(iconName, SIZEOF(iconName), "%s%d", mainIconsFmt, 7);
		return IcoLib_GetIcon(iconName, big);
	}

	for (i = 0; i < SIZEOF(statusIcons); i++) {
		if (statusIcons[i].id == status) {
			statusIndx = i;
			break;
	}	}

	if (statusIndx == -1)
		return NULL;

	if ( !szProto) {
		// Only return a protocol specific icon if there is only one protocol
		// Otherwise return the global icon. This affects the global status menu mainly.
		if (accounts.getCount() == 1) {
			HICON hIcon;

			// format: core_status_%proto%statusindex
			mir_snprintf(iconName, SIZEOF(iconName), "%s%s%d", statusIconsFmt, szProto, statusIndx);

			hIcon = IcoLib_GetIcon(iconName, big);
			if (hIcon)
				return hIcon;
		}

		// format: core_status_%s%d
		mir_snprintf(iconName, SIZEOF(iconName), "%s%s%d", statusIconsFmt, GLOBAL_PROTO_NAME, statusIndx);
		return IcoLib_GetIcon(iconName, big);
	}

	// format: core_status_%s%d
	mir_snprintf(iconName, SIZEOF(iconName), "%s%s%d", statusIconsFmt, szProto, statusIndx);
	hIcon = IcoLib_GetIcon(iconName, big);
	if (hIcon == NULL && (caps2 == 0 || (caps2 & statusIcons[statusIndx].pf2))) {
		PROTOACCOUNT* pa = Proto_GetAccount(szProto);
		if (pa) {
			TCHAR szPath[MAX_PATH], szFullPath[MAX_PATH], *str;
			SKINICONDESC sid = { 0 };

			//
			//  Queried protocol isn't in list, adding
			//
			TCHAR tszSection[MAX_PATH];
			mir_sntprintf(tszSection, SIZEOF(tszSection), _T("%s%s"), _T(PROTOCOLS_PREFIX), pa->tszAccountName);
			sid.ptszSection = tszSection;

			sid.cbSize = sizeof(sid);
			sid.flags = SIDF_ALL_TCHAR;

			GetModuleFileName(hMirandaInst, szPath, MAX_PATH);
			str = _tcsrchr(szPath, '\\');
			if (str != NULL)
				*str = 0;
			mir_sntprintf(szFullPath, SIZEOF(szFullPath), _T("%s\\Icons\\proto_") _T(TCHAR_STR_PARAM) _T(".dll"), szPath, pa->szProtoName);
			if (GetFileAttributes(szFullPath) != INVALID_FILE_ATTRIBUTES)
				sid.ptszDefaultFile = szFullPath;
			else {
				mir_sntprintf(szFullPath, SIZEOF(szFullPath), _T("%s\\Plugins\\") _T(TCHAR_STR_PARAM) _T(".dll"), szPath, szProto);
				if ((int)ExtractIconEx(szFullPath, statusIcons[i].resource_id, NULL, &hIcon, 1) > 0) {
					DestroyIcon(hIcon);
					sid.ptszDefaultFile = szFullPath;
					hIcon = NULL;
				}

				if (sid.pszDefaultFile == NULL) {
					if (str != NULL)
						*str = '\\';
					sid.ptszDefaultFile = szPath;
			}	}

			//
			// Add global icons to list
			//
			{
				int lowidx, highidx;
				if (caps2 == 0)
					lowidx = statusIndx, highidx = statusIndx+1;
				else
					lowidx = 0, highidx = SIZEOF(statusIcons);

				for (i = lowidx; i < highidx; i++) {
					if (caps2 == 0 || (caps2 & statusIcons[i].pf2)) {
						// format: core_%s%d
						mir_snprintf(iconName, SIZEOF(iconName), "%s%s%d", statusIconsFmt, szProto, i);
						sid.pszName = iconName;
						sid.ptszDescription = cli.pfnGetStatusModeDescription(statusIcons[i].id, 0);
						sid.iDefaultIndex = statusIcons[i].resource_id;
						IcoLib_AddNewIcon(0, &sid);
		}	}	}	}

		// format: core_status_%s%d
		mir_snprintf(iconName, SIZEOF(iconName), "%s%s%d", statusIconsFmt, szProto, statusIndx);
		hIcon = IcoLib_GetIcon(iconName, big);
		if (hIcon)
			return hIcon;
	}

	if (hIcon == NULL) {
		mir_snprintf(iconName, SIZEOF(iconName), "%s%s%d", statusIconsFmt, GLOBAL_PROTO_NAME, statusIndx);
		hIcon = IcoLib_GetIcon(iconName, big);
	}

	return hIcon;
}

HANDLE GetSkinIconHandle(int idx)
{
	int i;
	for (i = 0; i < SIZEOF(mainIcons); i++)
		if (idx == mainIcons[i].id)
			return hMainIcons[i];

	return NULL;
}

HICON LoadSkinIcon(int idx, bool big)
{
	//
	//  Query for global status icons
	//
	if (idx < SKINICON_EVENT_MESSAGE) {
		if (idx >= SIZEOF(statusIcons))
			return NULL;

		return LoadSkinProtoIcon(NULL, statusIcons[ idx ].id, big);
	}

	return IcoLib_GetIconByHandle(GetSkinIconHandle(idx), big);
}

/////////////////////////////////////////////////////////////////////////////////////////
// Initializes the icon skin module

static void convertOneProtocol(char* moduleName, char* iconName)
{
	char* pm = moduleName + strlen(moduleName);
	char* pi = iconName + strlen(iconName);

	for (int i = 0; i < SIZEOF(statusIcons); i++) {
		_itoa(statusIcons[i].id, pm, 10);

		DBVARIANT dbv;
		if ( !DBGetContactSettingTString(NULL, "Icons", moduleName, &dbv)) {
			_itoa(i, pi, 10);

			DBWriteContactSettingTString(NULL, "SkinIcons", iconName, dbv.ptszVal);
			DBFreeVariant(&dbv);

			DBDeleteContactSetting(NULL, "Icons", moduleName);
}	}	}

static INT_PTR sttLoadSkinIcon(WPARAM wParam, LPARAM lParam)
{
	switch (lParam)
	{
	case 0:
		return (INT_PTR)LoadSkinIcon(wParam);

	case 1:
		return (INT_PTR)GetSkinIconHandle(wParam);

	case 2:
		return (INT_PTR)LoadSkinIcon(wParam, true);
	}

	return 0;
}

static INT_PTR sttLoadSkinProtoIcon(WPARAM wParam, LPARAM lParam)
{
	return (INT_PTR)LoadSkinProtoIcon((char*)wParam, (int)lParam, false);
}

static INT_PTR sttLoadSkinProtoIconBig(WPARAM wParam, LPARAM lParam)
{
	return (INT_PTR)LoadSkinProtoIcon((char*)wParam, (int)lParam, true);
}

int LoadSkinIcons(void)
{
	SKINICONDESC sid;
	int i, j = 0;
	char iconName[MAX_PATH], moduleName[MAX_PATH];
    TCHAR modulePath[MAX_PATH];
	DBVARIANT dbv;

	//
	//  Perform "1st-time running import"

	for (i = 0; i < SIZEOF(mainIcons); i++) {
		_itoa(mainIcons[i].id, moduleName, 10);
		if (DBGetContactSettingTString(NULL, "Icons", moduleName, &dbv))
			break;

		mir_snprintf(iconName, SIZEOF(iconName), "%s%d", mainIconsFmt, i);

		DBWriteContactSettingTString(NULL, "SkinIcons", iconName, dbv.ptszVal);
		DBFreeVariant(&dbv);

		DBDeleteContactSetting(NULL, "Icons", moduleName);
	}

	for (;;) {
		// get the next protocol name
		moduleName[0] = 'p';
		moduleName[1] = 0;
		_itoa(j++, moduleName+1, 100);
		if (DBGetContactSettingTString(NULL, "Icons", moduleName, &dbv))
			break;

		DBDeleteContactSetting(NULL, "Icons", moduleName);

		// make old skinicons' prefix
		mir_snprintf(moduleName, SIZEOF(moduleName), TCHAR_STR_PARAM, dbv.ptszVal);
		// make IcoLib's prefix
		mir_snprintf(iconName, SIZEOF(iconName), "%s" TCHAR_STR_PARAM, statusIconsFmt, dbv.ptszVal);

		convertOneProtocol(moduleName, iconName);
		DBFreeVariant(&dbv);
	}
	moduleName[0] = 0;
	strcpy(iconName, "core_status_" GLOBAL_PROTO_NAME);
	convertOneProtocol(moduleName, iconName);

	CreateServiceFunction(MS_SKIN_LOADICON, sttLoadSkinIcon);
	CreateServiceFunction(MS_SKIN_LOADPROTOICON, sttLoadSkinProtoIcon);
	CreateServiceFunction(MS_SKIN_LOADPROTOICONBIG, sttLoadSkinProtoIconBig);

	ZeroMemory(&sid, sizeof(sid));
	sid.cbSize = sizeof(sid);
	GetModuleFileName(NULL, modulePath, SIZEOF(modulePath));
	sid.ptszDefaultFile = modulePath;
    sid.flags = SIDF_PATH_TCHAR;
	sid.pszName = iconName;

	//
	//  Add main icons to list
	//
	for (i = 0; i < SIZEOF(mainIcons); i++) {
		mir_snprintf(iconName, SIZEOF(iconName), "%s%d", mainIconsFmt, i);
		sid.pszSection = mainIcons[i].section == NULL ? "Main Icons" : (char*)mainIcons[i].section;
		sid.pszDescription = (char*)mainIcons[i].description;
		sid.iDefaultIndex = mainIcons[i].resource_id;
		hMainIcons[i] = IcoLib_AddNewIcon(0, &sid);
	}
	//
	// Add global icons to list
	//
	sid.pszSection = PROTOCOLS_PREFIX "Global";
	//
	// Asterisk is used, to avoid conflict with proto-plugins
	// 'coz users can't rename it to name with '*'
	for (i = 0; i < SIZEOF(statusIcons); i++) {
		mir_snprintf(iconName, SIZEOF(iconName), "%s%s%d", statusIconsFmt, GLOBAL_PROTO_NAME, i);
		sid.pszName = iconName;
		sid.pszDescription = (char*)statusIcons[i].description;
		sid.iDefaultIndex = statusIcons[i].resource_id;
		hStatusIcons[i] = IcoLib_AddNewIcon(0, &sid);
	}
	return 0;
}