/*
MetaContacts Plugin for Miranda IM.

Copyright � 2004 Universite Louis PASTEUR, STRASBOURG.

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.
*/

/** @file meta_main.c 
*
* Functions used by Miranda to launch the plugin.
* Centralizes functions needed by Miranda to get 
* information about the plugin and to initialize it
* and to dispose of it properly.
*/

/*! \mainpage MetaContacts plugin
 * \image html ulp.gif
 * \section desc Description
 * This library is a plugin for Miranda IM.\n
 * It allows the user to group several contacts from different
 * protocols (such as AIM or ICQ) into one unique metacontact.\n
 *
 * \section principle How does it work ?
 * Only one protocol will be used at a time (the default protocol).\n
 * This protocol is referenced in the proper field of the MetaContact
 * section in the Database.
 * 
 * \subsection send Emission of messages
 * The plugin will search through the Database to get the default protocol
 * (i.e. the protocol used to communicate with),\n and then call the Send function 
 * provided by this protocol.
 *
 * \subsection recv Reception of messages
 * When a contact is converted to a metacontact, or when it is added to an existing
 * metacontact, it gets a pseudo protocol \n (named "MetaContacts") in the protocol chain.\n
 * Usually, when a message is received, all the protocols in the chain get this message,
 * the real protocol (for example ICQ) at the end.\n But here, the message will be intercepted
 * by the MetaContact protocol, which will inhibit the further reception.\n The message will
 * then be redirected to the MetaContact, that will display it normally.
 *
 * \subsection handling Handling MetaContacts
 * There are four functionnality for handling MetaContacts :
 * \li Convert a contact to a MetaContact.
 * \li Add a contact to an existing MetaContact.
 * \li Edit a MetaContact.
 * \li Delete a MetaContact.
 *
 * They all are accessible via the context-menu displayed when a right click has occured,
 * but not at the same time : The 2 first will appear when the menu concerns a simple contact.\n
 * whereas the 2 last are only accessible from a MetaContact.\n
 * Those functions are self-explanatory, and a MessageBox is shown before any modification, so, for
 * further information, take a look at the Dialogs shown when they are called.\n
 *
 * \section cvats Caveats
 * Several functionnalities have not yet been developped :
 * \li Assigning contacts by Drag'n'Drop
 * \li Updating dynamically the status of the MetaContact
 * \li Merging history of all the contacts attached to MetaContact
 * \li Handling Files and URLs as well as Messages
 * \li and some other little functionnalities...
 *
 * Some of those functionnalities will not be developped due to the architecture
 * of Miranda (the 2 first, for example)
 * 
 * \section mail Contact
 * For any comment, suggestion or question, send a mail to shaalj@free.fr.\n
 * This code is provided as-is, and I cannot be held responsible for any harm
 * done to your database by this plugin.\n
 * Test it first on a fake database before using it normally.
 */

#include "metacontacts.h"

// Use VersionNo.h to set the version number, and ensure resource file is not open
#include "version.h"

struct MM_INTERFACE mmi;
BOOL os_unicode_enabled = FALSE;
int hLangpack;

//! Information gathered by Miranda, displayed in the plugin pane of the Option Dialog
PLUGININFOEX pluginInfo={
	sizeof(PLUGININFOEX),
	__PLUGIN_NAME,		// altered here and on file listing, so as not to match original
	PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
	__DESC,
	__AUTHOR,
	__AUTHOREMAIL,
	__COPYRIGHT,
	__AUTHORWEB,
	UNICODE_AWARE,
	0,
	{ 0x4c4a27cf, 0x5e64, 0x4242, { 0xa3, 0x32, 0xb9, 0x8b, 0x8, 0x24, 0x3e, 0x89 } } // {4C4A27CF-5E64-4242-A332-B98B08243E89}
};

HINSTANCE hInstance;	//!< Global reference to the application
PLUGINLINK *pluginLink;	//!< Link between Miranda and this plugin

/** Called by Miranda to get the information associated to this plugin.
* It only returns the PLUGININFO structure, without any test on the version
* @param mirandaVersion The version of the application calling this function
*/
extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
{
	return &pluginInfo;
}

static const MUUID interfaces[] = {MIID_PROTOCOL, MIID_METACONTACTS, MIID_LAST};
extern "C" __declspec(dllexport) const MUUID* MirandaPluginInterfaces(void)
{
	return interfaces;
}

/** DLL entry point
* Required to store the instance handle
*/
BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)
{
	hInstance=hinstDLL;
	return TRUE;
}

/** Prepare the plugin to stop
* Called by Miranda when it will exit or when the plugin gets deselected
*/
extern "C" __declspec(dllexport) int Unload(void)
{
	// see also meta_services.c, Meta_PreShutdown
	Meta_CloseHandles();
	//MessageBox(0, "Unload complete", "MC", MB_OK);
	return 0;
}

BOOL IsUnicodeOS()
{
	OSVERSIONINFOW		os;
	memset(&os, 0, sizeof(OSVERSIONINFOW));
	os.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
	return (GetVersionExW(&os) != 0);
}
 
/** Initializes the services provided and the link to those needed
* Called when the plugin is loaded into Miranda
*/
extern "C" __declspec(dllexport) int Load(PLUGINLINK *link)
{
	PROTOCOLDESCRIPTOR pd;
	DBVARIANT dbv;
	
	pluginLink=link;

	mir_getMMI(&mmi);
	mir_getLP(&pluginInfo);

	os_unicode_enabled = IsUnicodeOS();

	if (ServiceExists(MS_DB_SETSETTINGRESIDENT)) { // 0.6+
		CallService(MS_DB_SETSETTINGRESIDENT, TRUE, (LPARAM)(META_PROTO "/Status"));
		CallService(MS_DB_SETSETTINGRESIDENT, TRUE, (LPARAM)(META_PROTO "/IdleTS"));
		CallService(MS_DB_SETSETTINGRESIDENT, TRUE, (LPARAM)(META_PROTO "/ContactCountCheck"));
		CallService(MS_DB_SETSETTINGRESIDENT, TRUE, (LPARAM)(META_PROTO "/Handle"));
		CallService(MS_DB_SETSETTINGRESIDENT, TRUE, (LPARAM)(META_PROTO "/WindowOpen"));
	}

	//set all contacts to 'offline', and initialize subcontact counter for db consistency check
	{
		HANDLE hContact = (HANDLE)CallService( MS_DB_CONTACT_FINDFIRST, 0, 0);
		char *proto;
		while(hContact != NULL) {
			//proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
			if (!DBGetContactSetting(hContact, "Protocol", "p", &dbv)) {
				proto = dbv.pszVal;
				if (proto && !lstrcmp( META_PROTO, proto)) {
					DBWriteContactSettingWord(hContact, META_PROTO, "Status", ID_STATUS_OFFLINE);
					DBWriteContactSettingDword(hContact, META_PROTO, "IdleTS", 0);
					DBWriteContactSettingByte(hContact, META_PROTO, "ContactCountCheck", 0);

					// restore any saved defaults that might have remained if miranda was closed or crashed while a convo was happening
					if (DBGetContactSettingDword(hContact, META_PROTO, "SavedDefault", (DWORD)-1) != (DWORD)-1) {
						DBWriteContactSettingDword(hContact, META_PROTO, "Default", DBGetContactSettingDword(hContact, META_PROTO, "SavedDefault", 0));
						DBWriteContactSettingDword(hContact, META_PROTO, "SavedDefault", (DWORD)-1);
					}
					
				}
				DBFreeVariant(&dbv);
			}

			hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDNEXT,( WPARAM )hContact, 0 );
		}	
	}

	Meta_ReadOptions(&options);


	// sets subcontact handles to metacontacts, and metacontact handles to subcontacts
	// (since these handles are not necessarily the same from run to run of miranda)

	// also verifies that subcontacts: have metacontacts, and that contact numbers are reasonable, 
	// that metacontacts: have the correct number of subcontacts, and have reasonable defaults
	if (Meta_SetHandles()) {
		// error - db corruption
		if (!DBGetContactSettingByte(0, META_PROTO, "DisabledMessageShown", 0)) {
			MessageBox(0, Translate("Error - Database corruption.\nPlugin disabled."), Translate("MetaContacts"), MB_OK | MB_ICONERROR);
			DBWriteContactSettingByte(0, META_PROTO, "DisabledMessageShown", 1);
		}
		//Meta_HideMetaContacts(TRUE);
		return 1;
	}

	DBDeleteContactSetting(0, META_PROTO, "DisabledMessageShown");

	// add our modules to the KnownModules list 
	{ 
		DBVARIANT dbv; 
		if (DBGetContactSetting(NULL, "KnownModules", META_PROTO, &dbv))
			DBWriteContactSettingString(NULL, "KnownModules", META_PROTO, META_PROTO); 
		else
			DBFreeVariant(&dbv);
	} 

	ZeroMemory(&pd,sizeof(pd));
	pd.cbSize=PROTOCOLDESCRIPTOR_V3_SIZE;//sizeof(pd);

	pd.szName=META_FILTER;
	pd.type=PROTOTYPE_FILTER;
	CallService(MS_PROTO_REGISTERMODULE,0,(LPARAM)&pd);

	ZeroMemory(&pd,sizeof(pd));
	pd.cbSize=PROTOCOLDESCRIPTOR_V3_SIZE;//sizeof(pd);

	pd.szName=META_PROTO;
	pd.type = PROTOTYPE_PROTOCOL;
	CallService(MS_PROTO_REGISTERMODULE,0,(LPARAM)&pd);

	// further db setup done in modules loaded (nick [protocol string required] & clist display name)

	Meta_InitServices();

	// moved to 'modules loaded' event handler (in meta_services.c) because we need to 
	// check protocol for jabber hack, and the proto modules must be loaded
	//Meta_HideLinkedContactsAndSetHandles();

	if (ServiceExists(MS_MSG_GETWINDOWAPI)) {
		message_window_api_enabled = TRUE;
	}

	// for clist_meta_mw - write hidden group name to DB
	DBWriteContactSettingString(0, META_PROTO, "HiddenGroupName", META_HIDDEN_GROUP);
	
	return 0;
}