/*

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"

#define MS_SYSTEM_GET_MD5I	"Miranda/System/GetMD5I"

INT_PTR ResizeDialog(WPARAM wParam, LPARAM lParam);
int InitOpenUrl(void);
int InitWindowList(void);
int InitPathUtils(void);
void FreeWindowList(void);
int InitHyperlink(void);
int InitColourPicker(void);
int InitJson(void);
int InitBitmapFilter(void);
void InitXmlApi(void);
void InitTimeZones(void);
void UninitTimeZones(void);
int InitCrypt(void);

static BOOL bModuleInitialized = FALSE;

static CountryListEntry countries[] = {
	{0, "Unspecified", ""}, 
	{9999, "Other", ""}, 
	{0xFFFF, "Unknown", ""}, 
	{93, "Afghanistan", "AF"},
	{358, "Aland Islands", "AX"},
	{355, "Albania", "AL"},
	{213, "Algeria", "DZ"},
	{1684, "American Samoa", "AS"},
	{376, "Andorra", "AD"},
	{244, "Angola", "AO"},
	{1264, "Anguilla", "AI"},
	{0, "Antarctica", "AQ"},
	{1268, "Antigua and Barbuda", "AG"},
	{54, "Argentina", "AR"},
	{374, "Armenia", "AM"},
	{297, "Aruba", "AW"},
	{61, "Australia", "AU"},
	{43, "Austria", "AT"},
	{994, "Azerbaijan", "AZ"},
	{1242, "Bahamas", "BS"},
	{973, "Bahrain", "BH"},
	{880, "Bangladesh", "BD"},
	{1246, "Barbados", "BB"},
	{375, "Belarus", "BY"},
	{32, "Belgium", "BE"},
	{501, "Belize", "BZ"},
	{229, "Benin", "BJ"},
	{1441, "Bermuda", "BM"},
	{975, "Bhutan", "BT"},
	{591, "Bolivia, Plurinational State of", "BO"},
	{5997, "Bonaire, Sint Eustatius and Saba", "BQ"},
	{387, "Bosnia and Herzegovina", "BA"},
	{267, "Botswana", "BW"},
	{55, "Bouvet Island", "BV"},
	{55, "Brazil", "BR"},
	{246, "British Indian Ocean Territory", "IO"},
	{673, "Brunei Darussalam", "BN"},
	{359, "Bulgaria", "BG"},
	{226, "Burkina Faso", "BF"},
	{257, "Burundi", "BI"},
	{855, "Cambodia", "KH"},
	{237, "Cameroon", "CM"},
	{1, "Canada", "CA"},
	{238, "Cape Verde", "CV"},
	{1345, "Cayman Islands", "KY"},
	{236, "Central African Republic", "CF"},
	{235, "Chad", "TD"},
	{56, "Chile", "CL"},
	{86, "China", "CN"},
	{61, "Christmas Island", "CX"},
	{61, "Cocos (Keeling) Islands", "CC"},
	{57, "Colombia", "CO"},
	{269, "Comoros", "KM"},
	{242, "Congo", "CG"},
	{243, "Congo, the Democratic Republic of the", "CD"},
	{682, "Cook Islands", "CK"},
	{506, "Costa Rica", "CR"},
	{225, "Cote d'Ivoire", "CI"},
	{385, "Croatia", "HR"},
	{53, "Cuba", "CU"},
	{5999, "Curacao", "CW"},
	{357, "Cyprus", "CY"},
	{420, "Czech Republic", "CZ"},
	{45, "Denmark", "DK"},
	{253, "Djibouti", "DJ"},
	{1767, "Dominica", "DM"},
	{1809, "Dominican Republic", "DO"},
	{593, "Ecuador", "EC"},
	{20, "Egypt", "EG"},
	{503, "El Salvador", "SV"},
	{240, "Equatorial Guinea", "GQ"},
	{291, "Eritrea", "ER"},
	{372, "Estonia", "EE"},
	{251, "Ethiopia", "ET"},
	{500, "Falkland Islands (Malvinas)", "FK"},
	{298, "Faroe Islands", "FO"},
	{679, "Fiji", "FJ"},
	{358, "Finland", "FI"},
	{33, "France", "FR"},
	{594, "French Guiana", "GF"},
	{689, "French Polynesia", "PF"},
	{0, "French Southern Territories", "TF"},
	{241, "Gabon", "GA"},
	{220, "Gambia", "GM"},
	{995, "Georgia", "GE"},
	{49, "Germany", "DE"},
	{233, "Ghana", "GH"},
	{350, "Gibraltar", "GI"},
	{30, "Greece", "GR"},
	{299, "Greenland", "GL"},
	{1473, "Grenada", "GD"},
	{590, "Guadeloupe", "GP"},
	{1671, "Guam", "GU"},
	{502, "Guatemala", "GT"},
	{44, "Guernsey", "GG"},
	{224, "Guinea", "GN"},
	{245, "Guinea-Bissau", "GW"},
	{592, "Guyana", "GY"},
	{509, "Haiti", "HT"},
	{0, "Heard Island and McDonald Islands", "HM"},
	{379, "Holy See (Vatican City State)", "VA"},
	{504, "Honduras", "HN"},
	{852, "Hong Kong", "HK"},
	{36, "Hungary", "HU"},
	{354, "Iceland", "IS"},
	{91, "India", "IN"},
	{62, "Indonesia", "ID"},
	{98, "Iran, Islamic Republic of", "IR"},
	{964, "Iraq", "IQ"},
	{353, "Ireland", "IE"},
	{44, "Isle of Man", "IM"},
	{972, "Israel", "IL"},
	{39, "Italy", "IT"},
	{1876, "Jamaica", "JM"},
	{81, "Japan", "JP"},
	{44, "Jersey", "JE"},
	{962, "Jordan", "JO"},
	{76, "Kazakhstan", "KZ"},
	{254, "Kenya", "KE"},
	{686, "Kiribati", "KI"},
	{850, "Korea, Democratic People's Republic of", "KP"},
	{82, "Korea, Republic of", "KR"},
	{965, "Kuwait", "KW"},
	{996, "Kyrgyzstan", "KG"},
	{856, "Lao People's Democratic Republic", "LA"},
	{371, "Latvia", "LV"},
	{961, "Lebanon", "LB"},
	{266, "Lesotho", "LS"},
	{231, "Liberia", "LR"},
	{218, "Libya", "LY"},
	{423, "Liechtenstein", "LI"},
	{370, "Lithuania", "LT"},
	{352, "Luxembourg", "LU"},
	{853, "Macao", "MO"},
	{389, "Macedonia, The Former Yugoslav Republic of", "MK"},
	{261, "Madagascar", "MG"},
	{265, "Malawi", "MW"},
	{60, "Malaysia", "MY"},
	{960, "Maldives", "MV"},
	{223, "Mali", "ML"},
	{356, "Malta", "MT"},
	{692, "Marshall Islands", "MH"},
	{596, "Martinique", "MQ"},
	{222, "Mauritania", "MR"},
	{230, "Mauritius", "MU"},
	{262, "Mayotte", "YT"},
	{52, "Mexico", "MX"},
	{691, "Micronesia, Federated States of", "FM"},
	{373, "Moldova, Republic of", "MD"},
	{377, "Monaco", "MC"},
	{976, "Mongolia", "MN"},
	{382, "Montenegro", "ME"},
	{1664, "Montserrat", "MS"},
	{212, "Morocco", "MA"},
	{258, "Mozambique", "MZ"},
	{95, "Myanmar", "MM"},
	{264, "Namibia", "NA"},
	{674, "Nauru", "NR"},
	{977, "Nepal", "NP"},
	{31, "Netherlands", "NL"},
	{687, "New Caledonia", "NC"},
	{64, "New Zealand", "NZ"},
	{505, "Nicaragua", "NI"},
	{227, "Niger", "NE"},
	{234, "Nigeria", "NG"},
	{683, "Niue", "NU"},
	{672, "Norfolk Island", "NF"},
	{1670, "Northern Mariana Islands", "MP"},
	{47, "Norway", "NO"},
	{968, "Oman", "OM"},
	{92, "Pakistan", "PK"},
	{680, "Palau", "PW"},
	{970, "Palestinian Territory, Occupied", "PS"},
	{507, "Panama", "PA"},
	{675, "Papua New Guinea", "PG"},
	{595, "Paraguay", "PY"},
	{51, "Peru", "PE"},
	{63, "Philippines", "PH"},
	{64, "Pitcairn", "PN"},
	{48, "Poland", "PL"},
	{351, "Portugal", "PT"},
	{1787, "Puerto Rico", "PR"},
	{974, "Qatar", "QA"},
	{262, "Reunion", "RE"},
	{40, "Romania", "RO"},
	{7, "Russian Federation", "RU"},
	{250, "Rwanda", "RW"},
	{590, "Saint Barthelemy", "BL"},
	{290, "Saint Helena, Ascension and Tristan da Cunha", "SH"},
	{1869, "Saint Kitts and Nevis", "KN"},
	{1758, "Saint Lucia", "LC"},
	{590, "Saint Martin (French part)", "MF"},
	{508, "Saint Pierre and Miquelon", "PM"},
	{1784, "Saint Vincent and the Grenadines", "VC"},
	{685, "Samoa", "WS"},
	{378, "San Marino", "SM"},
	{239, "Sao Tome and Principe", "ST"},
	{966, "Saudi Arabia", "SA"},
	{221, "Senegal", "SN"},
	{381, "Serbia", "RS"},
	{248, "Seychelles", "SC"},
	{232, "Sierra Leone", "SL"},
	{65, "Singapore", "SG"},
	{1721, "Sint Maarten (Dutch part)", "SX"},
	{421, "Slovakia", "SK"},
	{386, "Slovenia", "SI"},
	{677, "Solomon Islands", "SB"},
	{252, "Somalia", "SO"},
	{27, "South Africa", "ZA"},
	{500, "South Georgia and the South Sandwich Islands", "GS"},
	{211, "South Sudan", "SS"},
	{34, "Spain", "ES"},
	{94, "Sri Lanka", "LK"},
	{249, "Sudan", "SD"},
	{597, "Suriname", "SR"},
	{4779, "Svalbard and Jan Mayen", "SJ"},
	{268, "Swaziland", "SZ"},
	{46, "Sweden", "SE"},
	{41, "Switzerland", "CH"},
	{963, "Syrian Arab Republic", "SY"},
	{886, "Taiwan, Province of China", "TW"},
	{992, "Tajikistan", "TJ"},
	{255, "Tanzania, United Republic of", "TZ"},
	{66, "Thailand", "TH"},
	{670, "Timor-Leste", "TL"},
	{228, "Togo", "TG"},
	{690, "Tokelau", "TK"},
	{676, "Tonga", "TO"},
	{1868, "Trinidad and Tobago", "TT"},
	{216, "Tunisia", "TN"},
	{90, "Turkey", "TR"},
	{993, "Turkmenistan", "TM"},
	{1649, "Turks and Caicos Islands", "TC"},
	{688, "Tuvalu", "TV"},
	{256, "Uganda", "UG"},
	{380, "Ukraine", "UA"},
	{971, "United Arab Emirates", "AE"},
	{44, "United Kingdom", "GB"},
	{1, "United States", "US"},
	{699, "United States Minor Outlying Islands", "UM"},
	{598, "Uruguay", "UY"},
	{998, "Uzbekistan", "UZ"},
	{678, "Vanuatu", "VU"},
	{58, "Venezuela, Bolivarian Republic of", "VE"},
	{84, "Viet Nam", "VN"},
	{1284, "Virgin Islands, British", "VG"},
	{1340, "Virgin Islands, U.S.", "VI"},
	{681, "Wallis and Futuna", "WF"},
	{5289, "Western Sahara", "EH"},
	{967, "Yemen", "YE"},
	{260, "Zambia", "ZM"},
	{263, "Zimbabwe", "ZW"}
};

static INT_PTR GetCountryByNumber(WPARAM wParam, LPARAM)
{
	for (int i = 0; i < SIZEOF(countries); i++)
		if ((int)wParam == countries[i].id)
			return (INT_PTR)countries[i].szName;

	return NULL;
}

static INT_PTR GetCountryByISOCode(WPARAM wParam, LPARAM)
{
	for (int i = 0; i < SIZEOF(countries); i++)
		if ( lstrcmpiA((char*)wParam, countries[i].ISOcode) == 0)
			return (INT_PTR)countries[i].szName;

	return NULL;
}

static INT_PTR GetCountryList(WPARAM wParam, LPARAM lParam)
{
	*(int*)wParam = SIZEOF(countries);
	*(CountryListEntry**)lParam = countries;
	return 0;
}

/////////////////////////////////////////////////////////////////////////////////////////

static INT_PTR SaveWindowPosition(WPARAM, LPARAM lParam)
{
	SAVEWINDOWPOS *swp = (SAVEWINDOWPOS*)lParam;
	WINDOWPLACEMENT wp;
	char szSettingName[64];

	wp.length = sizeof(wp);
	GetWindowPlacement(swp->hwnd, &wp);
	mir_snprintf(szSettingName, SIZEOF(szSettingName), "%sx", swp->szNamePrefix);
	db_set_dw(swp->hContact, swp->szModule, szSettingName, wp.rcNormalPosition.left);
	mir_snprintf(szSettingName, SIZEOF(szSettingName), "%sy", swp->szNamePrefix);
	db_set_dw(swp->hContact, swp->szModule, szSettingName, wp.rcNormalPosition.top);
	mir_snprintf(szSettingName, SIZEOF(szSettingName), "%swidth", swp->szNamePrefix);
	db_set_dw(swp->hContact, swp->szModule, szSettingName, wp.rcNormalPosition.right-wp.rcNormalPosition.left);
	mir_snprintf(szSettingName, SIZEOF(szSettingName), "%sheight", swp->szNamePrefix);
	db_set_dw(swp->hContact, swp->szModule, szSettingName, wp.rcNormalPosition.bottom-wp.rcNormalPosition.top);
	return 0;
}


static INT_PTR AssertInsideScreen(WPARAM wParam, LPARAM lParam)
{
	LPRECT rc = (LPRECT) wParam;
	if (rc == NULL)
		return -1;

	RECT rcScreen;
	SystemParametersInfo(SPI_GETWORKAREA, 0, &rcScreen, FALSE);

	if (MyMonitorFromWindow) 
	{
		if (MyMonitorFromRect(rc, MONITOR_DEFAULTTONULL))
			return 0;

		MONITORINFO mi = {0};
		HMONITOR hMonitor = MyMonitorFromRect(rc, MONITOR_DEFAULTTONEAREST);
		mi.cbSize = sizeof(mi);
		if (MyGetMonitorInfo(hMonitor, &mi))
			rcScreen = mi.rcWork;
	}
	else 
	{
		RECT rcDest;
		if (IntersectRect(&rcDest, &rcScreen, rc))
			return 0;
	}

	if (rc->top >= rcScreen.bottom)
		OffsetRect(rc, 0, rcScreen.bottom - rc->bottom);
	else if (rc->bottom <= rcScreen.top)
		OffsetRect(rc, 0, rcScreen.top - rc->top);
	if (rc->left >= rcScreen.right)
		OffsetRect(rc, rcScreen.right - rc->right, 0);
	else if (rc->right <= rcScreen.left)
		OffsetRect(rc, rcScreen.left - rc->left, 0);

	return 1;
}

static INT_PTR RestoreWindowPosition(WPARAM wParam, LPARAM lParam)
{
	SAVEWINDOWPOS *swp = (SAVEWINDOWPOS*)lParam;
	WINDOWPLACEMENT wp;
	char szSettingName[64];
	int x, y;

	wp.length = sizeof(wp);
	GetWindowPlacement(swp->hwnd, &wp);
	mir_snprintf(szSettingName, SIZEOF(szSettingName), "%sx", swp->szNamePrefix);
	x = db_get_dw(swp->hContact, swp->szModule, szSettingName, -1);
	mir_snprintf(szSettingName, SIZEOF(szSettingName), "%sy", swp->szNamePrefix);
	y = (int)db_get_dw(swp->hContact, swp->szModule, szSettingName, -1);
	if (x == -1) return 1;
	if (wParam&RWPF_NOSIZE) {
		OffsetRect(&wp.rcNormalPosition, x-wp.rcNormalPosition.left, y-wp.rcNormalPosition.top);
	}
	else {
		wp.rcNormalPosition.left = x;
		wp.rcNormalPosition.top = y;
		mir_snprintf(szSettingName, SIZEOF(szSettingName), "%swidth", swp->szNamePrefix);
		wp.rcNormalPosition.right = wp.rcNormalPosition.left+db_get_dw(swp->hContact, swp->szModule, szSettingName, -1);
		mir_snprintf(szSettingName, SIZEOF(szSettingName), "%sheight", swp->szNamePrefix);
		wp.rcNormalPosition.bottom = wp.rcNormalPosition.top+db_get_dw(swp->hContact, swp->szModule, szSettingName, -1);
	}
	wp.flags = 0;
	if (wParam & RWPF_HIDDEN)
		wp.showCmd = SW_HIDE;
	if (wParam & RWPF_NOACTIVATE)
		wp.showCmd = SW_SHOWNOACTIVATE;

	if ( !(wParam & RWPF_NOMOVE))
		AssertInsideScreen((WPARAM) &wp.rcNormalPosition, 0);

	SetWindowPlacement(swp->hwnd, &wp);
	return 0;
}

/////////////////////////////////////////////////////////////////////////////////////////
static INT_PTR RestartMiranda(WPARAM, LPARAM)
{
	TCHAR mirandaPath[ MAX_PATH ], cmdLine[ 100 ];
	PROCESS_INFORMATION pi;
	STARTUPINFO si = { 0 };
	si.cb = sizeof(si);
	GetModuleFileName(NULL, mirandaPath, SIZEOF(mirandaPath));
	mir_sntprintf(cmdLine, SIZEOF(cmdLine), _T("\"%s\" /restart:%d"), mirandaPath, GetCurrentProcessId());
	CallService("CloseAction", 0, 0);
	CreateProcess(mirandaPath, cmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
	return 0;
}

/////////////////////////////////////////////////////////////////////////////////////////

typedef BOOL (APIENTRY *PGENRANDOM)(PVOID, ULONG);

static INT_PTR GenerateRandom(WPARAM wParam, LPARAM lParam)
{
	if (wParam == 0 || lParam == 0) return 0;

	PGENRANDOM pfnRtlGenRandom = NULL;
	HMODULE hModule = GetModuleHandleA("advapi32");
	if (hModule)
	{
		pfnRtlGenRandom = (PGENRANDOM)GetProcAddress(hModule, "SystemFunction036");
		if (pfnRtlGenRandom)
		{
			if ( !pfnRtlGenRandom((PVOID)lParam, wParam))
				pfnRtlGenRandom = NULL;
		}
	}
	if (pfnRtlGenRandom == NULL)
	{
		srand(GetTickCount());
		unsigned short* buf = (unsigned short*)lParam;
		for (; (long)(wParam-=2) >= 0;)
			*(buf++) = (unsigned short)rand();
		if (lParam < 0)
			*(char*)buf = (char)(rand() & 0xFF);
	}
	return 0;
}

/////////////////////////////////////////////////////////////////////////////////////////
int LoadUtilsModule(void)
{
	bModuleInitialized = TRUE;

	CreateServiceFunction(MS_UTILS_RESIZEDIALOG, ResizeDialog);
	CreateServiceFunction(MS_UTILS_SAVEWINDOWPOSITION, SaveWindowPosition);
	CreateServiceFunction(MS_UTILS_RESTOREWINDOWPOSITION, RestoreWindowPosition);
	CreateServiceFunction(MS_UTILS_ASSERTINSIDESCREEN, AssertInsideScreen);
	CreateServiceFunction(MS_UTILS_GETCOUNTRYBYNUMBER, GetCountryByNumber);
	CreateServiceFunction(MS_UTILS_GETCOUNTRYBYISOCODE, GetCountryByISOCode);
	CreateServiceFunction(MS_UTILS_GETCOUNTRYLIST, GetCountryList);
	CreateServiceFunction(MS_UTILS_GETRANDOM, GenerateRandom);
	CreateServiceFunction(MS_SYSTEM_RESTART, RestartMiranda);

	InitOpenUrl();
	InitWindowList();
	InitHyperlink();
	InitPathUtils();
	InitColourPicker();
	InitBitmapFilter();
	InitXmlApi();
	InitJson();
	InitTimeZones();
	InitCrypt();
	return 0;
}

void UnloadUtilsModule(void)
{
	if ( !bModuleInitialized)
		return;

	FreeWindowList();
	UninitTimeZones();
}