/*
Chat module plugin for Miranda IM

Copyright (C) 2003 Jörgen Persson

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 "chat.h"

//globals
HINSTANCE   g_hInst;
PLUGINLINK  *pluginLink;
HANDLE      g_hWindowList;
HMENU       g_hMenu = NULL;

struct MM_INTERFACE memoryManagerInterface;
struct UTF8_INTERFACE utfi;

FONTINFO    aFonts[OPTIONS_FONTCOUNT];
HICON       hIcons[30];
BOOL        IEviewInstalled = FALSE;
HBRUSH      hListBkgBrush = NULL;
BOOL        SmileyAddInstalled = FALSE;
BOOL        PopUpInstalled = FALSE;
HBRUSH      hEditBkgBrush = NULL;
HBRUSH      hListSelectedBkgBrush = NULL;

HIMAGELIST  hImageList = NULL;

HIMAGELIST  hIconsList = NULL;

TCHAR*      pszActiveWndID = 0;
char*       pszActiveWndModule = 0;

/* Missing MinGW GUIDs */
#ifdef __MINGW32__
const CLSID IID_IRichEditOle = { 0x00020D00, 0x00, 0x00, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } };
const CLSID IID_IRichEditOleCallback = { 0x00020D03, 0x00, 0x00, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } };
#endif

struct GlobalLogSettings_t g_Settings;

static void InitREOleCallback(void);

PLUGININFOEX pluginInfo = {
	sizeof(PLUGININFOEX),
	"Chat",
	__VERSION_DWORD,
	"Provides chat rooms for protocols supporting it",
	"Miranda team",
	"project-info@miranda-im.org",
	"© 2003-2011 Miranda team",
	"http://miranda-im.org/",
	UNICODE_AWARE,
	0,
    #ifdef _UNICODE
    {0x9d6c3213, 0x2b4, 0x4fe1, { 0x92, 0xe6, 0x52, 0x6d, 0xe2, 0x4f, 0x8d, 0x65 }} //{9D6C3213-02B4-4fe1-92E6-526DE24F8D65}
    #else
    {0xd3d9f953, 0x85bc, 0x486c, { 0xa7, 0xbe, 0x31, 0x43, 0xa6, 0xfd, 0x29, 0xf }} //{D3D9F953-85BC-486c-A7BE-3143A6FD290F}
    #endif
};

BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)
{
	g_hInst = hinstDLL;
	return TRUE;
}

__declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
{
	if (mirandaVersion < PLUGIN_MAKE_VERSION(0,7,0,2)) return NULL;
	return &pluginInfo;
}

static const MUUID interfaces[] = {MIID_CHAT, MIID_LAST};
__declspec(dllexport) const MUUID * MirandaPluginInterfaces(void)
{
	return interfaces;
}

int __declspec(dllexport) Load(PLUGINLINK *link)
{
	BOOL bFlag = FALSE;
	HINSTANCE hDll;

#ifndef NDEBUG //mem leak detector :-) Thanks Tornado!
	int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); // Get current flag
	flag |= _CRTDBG_LEAK_CHECK_DF; // Turn on leak-checking bit
	_CrtSetDbgFlag(flag); // Set flag to the new value
#endif

	pluginLink = link;

	// set the memory & utf8 managers
	mir_getMMI( &memoryManagerInterface );
	if ( mir_getUTFI( &utfi ) == CALLSERVICE_NOTFOUND )
		return 1;

	hDll = LoadLibraryA("riched20.dll");
	if ( hDll ) {
		char modulePath[MAX_PATH];
		if (GetModuleFileNameA(hDll, modulePath, MAX_PATH)) {
			DWORD dummy;
			VS_FIXEDFILEINFO* vsInfo;
			UINT vsInfoSize;
			DWORD size = GetFileVersionInfoSizeA(modulePath, &dummy);
			BYTE* buffer = (BYTE*) mir_alloc(size);

			if (GetFileVersionInfoA(modulePath, 0, size, buffer))
                if (VerQueryValueA(buffer, "\\", (LPVOID*) &vsInfo, &vsInfoSize))
                    if (LOWORD(vsInfo->dwFileVersionMS) != 0)
                        bFlag= TRUE;
			mir_free(buffer);
	}	}

	if ( !bFlag ) {
		if (IDYES == MessageBox(0, TranslateT("Miranda could not load the Chat plugin because Microsoft Rich Edit v 3 is missing.\nIf you are using Windows 95/98/NT or WINE please upgrade your Rich Edit control.\n\nDo you want to download an update now?."),TranslateT("Information"),MB_YESNO|MB_ICONINFORMATION))
			CallService(MS_UTILS_OPENURL, 1, (LPARAM) "http://members.chello.se/matrix/re3/richupd.exe");
		FreeLibrary(GetModuleHandleA("riched20.dll"));
		return 1;
	}

	UpgradeCheck();

	g_hMenu = LoadMenu(g_hInst, MAKEINTRESOURCE(IDR_MENU));
	InitREOleCallback();
	HookEvents();
	CreateServiceFunctions();
	CreateHookableEvents();
	OptionsInit();
	TabsInit();
	return 0;
}

int __declspec(dllexport) Unload(void)
{
	DBWriteContactSettingWord(NULL, "Chat", "SplitterX", (WORD)g_Settings.iSplitterX);
	DBWriteContactSettingWord(NULL, "Chat", "SplitterY", (WORD)g_Settings.iSplitterY);
	DBWriteContactSettingDword(NULL, "Chat", "roomx", g_Settings.iX);
	DBWriteContactSettingDword(NULL, "Chat", "roomy", g_Settings.iY);
	DBWriteContactSettingDword(NULL, "Chat", "roomwidth" , g_Settings.iWidth);
	DBWriteContactSettingDword(NULL, "Chat", "roomheight", g_Settings.iHeight);

	CList_SetAllOffline(TRUE, NULL);

	mir_free( pszActiveWndID );
	mir_free( pszActiveWndModule );

	DestroyMenu(g_hMenu);
	DestroyServiceFunctions();
	DestroyHookableEvents();
	FreeIcons();
	OptionsUnInit();
	FreeLibrary(GetModuleHandleA("riched20.dll"));
	UnhookEvents();
	return 0;
}

void UpgradeCheck(void)
{
	DWORD dwVersion = DBGetContactSettingDword(NULL, "Chat", "OldVersion", PLUGIN_MAKE_VERSION(0,2,9,9));
	if (	pluginInfo.version > dwVersion)
	{
		if (dwVersion < PLUGIN_MAKE_VERSION(0,3,0,0))
		{
			DBDeleteContactSetting(NULL, "ChatFonts",	"Font18");
			DBDeleteContactSetting(NULL, "ChatFonts",	"Font18Col");
			DBDeleteContactSetting(NULL, "ChatFonts",	"Font18Set");
			DBDeleteContactSetting(NULL, "ChatFonts",	"Font18Size");
			DBDeleteContactSetting(NULL, "ChatFonts",	"Font18Sty");
			DBDeleteContactSetting(NULL, "ChatFonts",	"Font19");
			DBDeleteContactSetting(NULL, "ChatFonts",	"Font19Col");
			DBDeleteContactSetting(NULL, "ChatFonts",	"Font19Set");
			DBDeleteContactSetting(NULL, "ChatFonts",	"Font19Size");
			DBDeleteContactSetting(NULL, "ChatFonts",	"Font19Sty");
			DBDeleteContactSetting(NULL, "Chat",		"ColorNicklistLines");
			DBDeleteContactSetting(NULL, "Chat",		"NicklistIndent");
			DBDeleteContactSetting(NULL, "Chat",		"NicklistRowDist");
			DBDeleteContactSetting(NULL, "Chat",		"ShowFormatButtons");
			DBDeleteContactSetting(NULL, "Chat",		"ShowLines");
			DBDeleteContactSetting(NULL, "Chat",		"ShowName");
			DBDeleteContactSetting(NULL, "Chat",		"ShowTopButtons");
			DBDeleteContactSetting(NULL, "Chat",		"SplitterX");
			DBDeleteContactSetting(NULL, "Chat",		"SplitterY");
			DBDeleteContactSetting(NULL, "Chat",		"IconFlags");
			DBDeleteContactSetting(NULL, "Chat",		"LogIndentEnabled");
	}	}

	DBWriteContactSettingDword(NULL, "Chat", "OldVersion", pluginInfo.version);
}

void LoadLogIcons(void)
{
	hIcons[ICON_ACTION]     = LoadIconEx( "log_action", FALSE );
	hIcons[ICON_ADDSTATUS]  = LoadIconEx( "log_addstatus", FALSE );
	hIcons[ICON_HIGHLIGHT]  = LoadIconEx( "log_highlight", FALSE );
	hIcons[ICON_INFO]       = LoadIconEx( "log_info", FALSE );
	hIcons[ICON_JOIN]       = LoadIconEx( "log_join", FALSE );
	hIcons[ICON_KICK]       = LoadIconEx( "log_kick", FALSE );
	hIcons[ICON_MESSAGE]    = LoadIconEx( "log_message_in", FALSE );
	hIcons[ICON_MESSAGEOUT] = LoadIconEx( "log_message_out", FALSE );
	hIcons[ICON_NICK]       = LoadIconEx( "log_nick", FALSE );
	hIcons[ICON_NOTICE]     = LoadIconEx( "log_notice", FALSE );
	hIcons[ICON_PART]       = LoadIconEx( "log_part", FALSE );
	hIcons[ICON_QUIT]       = LoadIconEx( "log_quit", FALSE );
	hIcons[ICON_REMSTATUS]  = LoadIconEx( "log_removestatus", FALSE );
	hIcons[ICON_TOPIC]      = LoadIconEx( "log_topic", FALSE );
	hIcons[ICON_STATUS1]    = LoadIconEx( "status1", FALSE );
	hIcons[ICON_STATUS2]    = LoadIconEx( "status2", FALSE );
	hIcons[ICON_STATUS3]    = LoadIconEx( "status3", FALSE );
	hIcons[ICON_STATUS4]    = LoadIconEx( "status4", FALSE );
	hIcons[ICON_STATUS0]    = LoadIconEx( "status0", FALSE );
	hIcons[ICON_STATUS5]    = LoadIconEx( "status5", FALSE );
}

void LoadIcons(void)
{
	int i;

	for(i = 0; i < 20; i++)
		hIcons[i] = NULL;

	LoadLogIcons();
	LoadMsgLogBitmaps();

	hImageList = ImageList_Create(GetSystemMetrics(SM_CXSMICON),GetSystemMetrics(SM_CYSMICON),IsWinVerXPPlus()? ILC_COLOR32 | ILC_MASK : ILC_COLOR16 | ILC_MASK,0,3);
	hIconsList = ImageList_Create(GetSystemMetrics(SM_CXSMICON),GetSystemMetrics(SM_CYSMICON),IsWinVerXPPlus()? ILC_COLOR32 | ILC_MASK : ILC_COLOR16 | ILC_MASK,0,100);
	ImageList_AddIcon(hIconsList,LoadSkinnedIcon( SKINICON_EVENT_MESSAGE));
	ImageList_AddIcon(hIconsList,LoadIconEx( "overlay", FALSE ));
	ImageList_SetOverlayImage(hIconsList, 1, 1);
	ImageList_AddIcon(hImageList,LoadImage(g_hInst,MAKEINTRESOURCE(IDI_BLANK),IMAGE_ICON,0,0,0));
	ImageList_AddIcon(hImageList,LoadImage(g_hInst,MAKEINTRESOURCE(IDI_BLANK),IMAGE_ICON,0,0,0));
}

void FreeIcons(void)
{
	FreeMsgLogBitmaps();
	ImageList_Destroy(hImageList);
	ImageList_Destroy(hIconsList);
}

static IRichEditOleCallbackVtbl reOleCallbackVtbl;
struct CREOleCallback reOleCallback;

static STDMETHODIMP_(ULONG) CREOleCallback_QueryInterface(struct CREOleCallback *lpThis, REFIID riid, LPVOID * ppvObj)
{
	if (IsEqualIID(riid, &IID_IRichEditOleCallback)) {
		*ppvObj = lpThis;
		lpThis->lpVtbl->AddRef((IRichEditOleCallback *) lpThis);
		return S_OK;
	}
	*ppvObj = NULL;
	return E_NOINTERFACE;
}

static STDMETHODIMP_(ULONG) CREOleCallback_AddRef(struct CREOleCallback *lpThis)
{
	if (lpThis->refCount == 0) {
		if (S_OK != StgCreateDocfile(NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE | STGM_DELETEONRELEASE, 0, &lpThis->pictStg))
			lpThis->pictStg = NULL;
		lpThis->nextStgId = 0;
	}
	return ++lpThis->refCount;
}

static STDMETHODIMP_(ULONG) CREOleCallback_Release(struct CREOleCallback *lpThis)
{
	if (--lpThis->refCount == 0) {
		if (lpThis->pictStg)
			lpThis->pictStg->lpVtbl->Release(lpThis->pictStg);
	}
	return lpThis->refCount;
}

static STDMETHODIMP_(HRESULT) CREOleCallback_ContextSensitiveHelp(struct CREOleCallback *lpThis, BOOL fEnterMode)
{
	return S_OK;
}

static STDMETHODIMP_(HRESULT) CREOleCallback_DeleteObject(struct CREOleCallback *lpThis, LPOLEOBJECT lpoleobj)
{
	return S_OK;
}

static STDMETHODIMP_(HRESULT) CREOleCallback_GetClipboardData(struct CREOleCallback *lpThis, CHARRANGE * lpchrg, DWORD reco, LPDATAOBJECT * lplpdataobj)
{
	return E_NOTIMPL;
}

static STDMETHODIMP_(HRESULT) CREOleCallback_GetContextMenu(struct CREOleCallback *lpThis, WORD seltype, LPOLEOBJECT lpoleobj, CHARRANGE * lpchrg, HMENU * lphmenu)
{
	return E_INVALIDARG;
}

static STDMETHODIMP_(HRESULT) CREOleCallback_GetDragDropEffect(struct CREOleCallback *lpThis, BOOL fDrag, DWORD grfKeyState, LPDWORD pdwEffect)
{
	return S_OK;
}

static STDMETHODIMP_(HRESULT) CREOleCallback_GetInPlaceContext(struct CREOleCallback *lpThis, LPOLEINPLACEFRAME * lplpFrame, LPOLEINPLACEUIWINDOW * lplpDoc, LPOLEINPLACEFRAMEINFO lpFrameInfo)
{
	return E_INVALIDARG;
}

static STDMETHODIMP_(HRESULT) CREOleCallback_GetNewStorage(struct CREOleCallback *lpThis, LPSTORAGE * lplpstg)
{
	WCHAR szwName[64];
	char szName[64];
	wsprintfA(szName, "s%u", lpThis->nextStgId);
	MultiByteToWideChar(CP_ACP, 0, szName, -1, szwName, SIZEOF(szwName));
	if (lpThis->pictStg == NULL)
		return STG_E_MEDIUMFULL;
	return lpThis->pictStg->lpVtbl->CreateStorage(lpThis->pictStg, szwName, STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE, 0, 0, lplpstg);
}

static STDMETHODIMP_(HRESULT) CREOleCallback_QueryAcceptData(struct CREOleCallback *lpThis, LPDATAOBJECT lpdataobj, CLIPFORMAT * lpcfFormat, DWORD reco, BOOL fReally, HGLOBAL hMetaPict)
{
	return S_OK;
}

static STDMETHODIMP_(HRESULT) CREOleCallback_QueryInsertObject(struct CREOleCallback *lpThis, LPCLSID lpclsid, LPSTORAGE lpstg, LONG cp)
{
	return S_OK;
}

static STDMETHODIMP_(HRESULT) CREOleCallback_ShowContainerUI(struct CREOleCallback *lpThis, BOOL fShow)
{
	return S_OK;
}

static void InitREOleCallback(void)
{
	reOleCallback.lpVtbl = &reOleCallbackVtbl;
	reOleCallback.lpVtbl->AddRef = (ULONG(__stdcall *) (IRichEditOleCallback *)) CREOleCallback_AddRef;
	reOleCallback.lpVtbl->Release = (ULONG(__stdcall *) (IRichEditOleCallback *)) CREOleCallback_Release;
	reOleCallback.lpVtbl->QueryInterface = (ULONG(__stdcall *) (IRichEditOleCallback *, REFIID, PVOID *)) CREOleCallback_QueryInterface;
	reOleCallback.lpVtbl->ContextSensitiveHelp = (HRESULT(__stdcall *) (IRichEditOleCallback *, BOOL)) CREOleCallback_ContextSensitiveHelp;
	reOleCallback.lpVtbl->DeleteObject = (HRESULT(__stdcall *) (IRichEditOleCallback *, LPOLEOBJECT)) CREOleCallback_DeleteObject;
	reOleCallback.lpVtbl->GetClipboardData = (HRESULT(__stdcall *) (IRichEditOleCallback *, CHARRANGE *, DWORD, LPDATAOBJECT *)) CREOleCallback_GetClipboardData;
	reOleCallback.lpVtbl->GetContextMenu = (HRESULT(__stdcall *) (IRichEditOleCallback *, WORD, LPOLEOBJECT, CHARRANGE *, HMENU *)) CREOleCallback_GetContextMenu;
	reOleCallback.lpVtbl->GetDragDropEffect = (HRESULT(__stdcall *) (IRichEditOleCallback *, BOOL, DWORD, LPDWORD)) CREOleCallback_GetDragDropEffect;
	reOleCallback.lpVtbl->GetInPlaceContext = (HRESULT(__stdcall *) (IRichEditOleCallback *, LPOLEINPLACEFRAME *, LPOLEINPLACEUIWINDOW *, LPOLEINPLACEFRAMEINFO))CREOleCallback_GetInPlaceContext;
	reOleCallback.lpVtbl->GetNewStorage = (HRESULT(__stdcall *) (IRichEditOleCallback *, LPSTORAGE *)) CREOleCallback_GetNewStorage;
	reOleCallback.lpVtbl->QueryAcceptData = (HRESULT(__stdcall *) (IRichEditOleCallback *, LPDATAOBJECT, CLIPFORMAT *, DWORD, BOOL, HGLOBAL)) CREOleCallback_QueryAcceptData;
	reOleCallback.lpVtbl->QueryInsertObject = (HRESULT(__stdcall *) (IRichEditOleCallback *, LPCLSID, LPSTORAGE, LONG)) CREOleCallback_QueryInsertObject;
	reOleCallback.lpVtbl->ShowContainerUI = (HRESULT(__stdcall *) (IRichEditOleCallback *, BOOL)) CREOleCallback_ShowContainerUI;
	reOleCallback.refCount = 0;
}
