/*
Miranda SmileyAdd Plugin
Copyright (C) 2005 - 2012 Boris Krasnovskiy All Rights Reserved
Copyright (C) 2003 - 2004 Rein-Peter de Boer
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 version 2
of the License.
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, see .
*/
#include "stdafx.h"
static mir_cs csWndList;
// type definitions 
class MsgWndData : public MZeroedObject
{
public:
	HWND hwnd;
	char ProtocolName[52];
	HWND REdit;
	HWND MEdit;
	HWND LButton;
	mutable HWND hSmlButton;
	mutable HBITMAP hSmlBmp;
	mutable HICON hSmlIco;
	int idxLastChar;
	MCONTACT hContact;
	bool doSmileyReplace;
	bool doSmileyButton;
	bool isSplit;
	bool isSend;
	MsgWndData()
	{
	}
	~MsgWndData()
	{
		clear();
	}
	void clear(void)
	{
		if (hSmlBmp != NULL) {
			DeleteObject(hSmlBmp);
			hSmlBmp = NULL;
		}
		if (hSmlIco != NULL) {
			DestroyIcon(hSmlIco);
			hSmlIco = NULL;
		}
		if (hSmlButton != NULL) {
			DestroyWindow(hSmlButton);
			hSmlButton = NULL;
		}		
	}
	RECT CalcSmileyButtonPos(void)
	{
		RECT rect;
		GetWindowRect(LButton, &rect);
		POINT pt;
		pt.y = rect.top;
		MUUID muidScriver = { 0x84636f78, 0x2057, 0x4302, { 0x8a, 0x65, 0x23, 0xa1, 0x6d, 0x46, 0x84, 0x4c } };
		int iShift = (IsPluginLoaded(muidScriver)) ? 28 : -28;
		if ((GetWindowLongPtr(LButton, GWL_STYLE) & WS_VISIBLE) != 0)
			pt.x = rect.left + iShift;
		else
			pt.x = rect.left;
		ScreenToClient(GetParent(LButton), &pt);
		rect.bottom += pt.y - rect.top;
		rect.right += pt.x - rect.left;
		rect.top = pt.y;
		rect.left = pt.x;
		return rect;
	}
	void CreateSmileyButton(void)
	{
		doSmileyButton = opt.ButtonStatus != 0;
		SmileyPackType *SmileyPack = GetSmileyPack(ProtocolName, hContact);
		doSmileyButton &= SmileyPack != NULL && SmileyPack->VisibleSmileyCount() != 0;
		doSmileyReplace = true;
		doSmileyButton &= db_get_b(NULL, "SRMM", "ShowButtonLine", TRUE) != 0;
		if (ProtocolName[0] != 0) {
			INT_PTR cap = CallProtoService(ProtocolName, PS_GETCAPS, PFLAGNUM_1, 0);
			doSmileyButton &= ((cap & (PF1_IMSEND | PF1_CHAT)) != 0);
			doSmileyReplace &= ((cap & (PF1_IMRECV | PF1_CHAT)) != 0);
		}
		if (doSmileyButton && opt.PluginSupportEnabled) {
			// create smiley button
			RECT rect = CalcSmileyButtonPos();
			hSmlButton = CreateWindowEx(
				WS_EX_LEFT | WS_EX_NOPARENTNOTIFY | WS_EX_TOPMOST,
				MIRANDABUTTONCLASS,
				L"S",
				WS_CHILD | WS_VISIBLE | WS_TABSTOP,
				rect.left,
				rect.top,
				rect.bottom - rect.top + 1,
				rect.bottom - rect.top + 1,
				GetParent(LButton),
				(HMENU)IDC_SMLBUTTON,
				NULL, NULL);
			// Conversion to bitmap done to prevent Miranda from scaling the image
			SmileyType *sml = FindButtonSmiley(SmileyPack);
			if (sml != NULL) {
				hSmlBmp = sml->GetBitmap(GetSysColor(COLOR_BTNFACE), 0, 0);
				SendMessage(hSmlButton, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hSmlBmp);
			}
			else {
				hSmlIco = GetDefaultIcon();
				SendMessage(hSmlButton, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hSmlIco);
			}
			SendMessage(hSmlButton, BUTTONADDTOOLTIP, (WPARAM)LPGEN("Show smiley selection window"), 0);
			SendMessage(hSmlButton, BUTTONSETASFLATBTN, TRUE, 0);
		}
	}
};
static LIST g_MsgWndList(10, HandleKeySortT);
int UpdateSrmmDlg(WPARAM wParam, LPARAM)
{
	mir_cslock lck(csWndList);
	
	for (int i = 0; i < g_MsgWndList.getCount(); ++i) {
		if (wParam == 0 || g_MsgWndList[i]->hContact == wParam) {
			SendMessage(g_MsgWndList[i]->hwnd, WM_SETREDRAW, FALSE, 0);
			SendMessage(g_MsgWndList[i]->hwnd, DM_OPTIONSAPPLIED, 0, 0);
			SendMessage(g_MsgWndList[i]->hwnd, WM_SETREDRAW, TRUE, 0);
		}
	}
	return 0;
}
// find the dialog info in the stored list
static MsgWndData* IsMsgWnd(HWND hwnd)
{
	mir_cslock lck(csWndList);
	return g_MsgWndList.find((MsgWndData*)&hwnd);
}
// global subclass function for all dialogs
static LRESULT CALLBACK MessageDlgSubclass(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	MsgWndData *dat = IsMsgWnd(hwnd);
	if (dat == NULL)
		return 0;
	switch (uMsg) {
	case DM_OPTIONSAPPLIED:
		dat->clear();
		dat->CreateSmileyButton();
		break;
	case DM_APPENDTOLOG:
		if (opt.PluginSupportEnabled) {
			// get length of text now before things can get added...
			GETTEXTLENGTHEX gtl;
			gtl.codepage = 1200;
			gtl.flags = GTL_PRECISE | GTL_NUMCHARS;
			dat->idxLastChar = (int)SendMessage(dat->REdit, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
		}
		break;
	}
	LRESULT result = mir_callNextSubclass(hwnd, MessageDlgSubclass, uMsg, wParam, lParam);
	if (!opt.PluginSupportEnabled)
		return result;
	switch (uMsg) {
	case WM_DESTROY:
		{
			mir_cslock lck(csWndList);
			int ind = g_MsgWndList.getIndex((MsgWndData*)&hwnd);
			if (ind != -1) {
				delete g_MsgWndList[ind];
				g_MsgWndList.remove(ind);
			}
		}
		break;
	case WM_SIZE:
		if (dat->doSmileyButton) {
			RECT rect = dat->CalcSmileyButtonPos();
			SetWindowPos(dat->hSmlButton, NULL, rect.left, rect.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
		}
		break;
	case DM_APPENDTOLOG:
		if (dat->doSmileyReplace) {
			SmileyPackCType *smcp;
			SmileyPackType *SmileyPack = GetSmileyPack(dat->ProtocolName, dat->hContact, &smcp);
			if (SmileyPack != NULL) {
				const CHARRANGE sel = { dat->idxLastChar, LONG_MAX };
				ReplaceSmileys(dat->REdit, SmileyPack, smcp, sel, false, false, false);
			}
		}
		break;
	case DM_REMAKELOG:
		if (dat->doSmileyReplace) {
			SmileyPackCType *smcp;
			SmileyPackType *SmileyPack = GetSmileyPack(dat->ProtocolName, dat->hContact, &smcp);
			if (SmileyPack != NULL) {
				static const CHARRANGE sel = { 0, LONG_MAX };
				ReplaceSmileys(dat->REdit, SmileyPack, smcp, sel, false, false, false);
			}
		}
		break;
	case WM_COMMAND:
		if (LOWORD(wParam) == IDC_SMLBUTTON && HIWORD(wParam) == BN_CLICKED) {
			RECT rect;
			GetWindowRect(dat->hSmlButton, &rect);
			SmileyToolWindowParam *stwp = new SmileyToolWindowParam;
			stwp->pSmileyPack = GetSmileyPack(dat->ProtocolName, dat->hContact);
			stwp->hWndParent = hwnd;
			stwp->hWndTarget = dat->MEdit;
			stwp->targetMessage = EM_REPLACESEL;
			stwp->targetWParam = TRUE;
			stwp->direction = 0;
			stwp->xPosition = rect.left;
			stwp->yPosition = rect.top + 24;
			mir_forkthread(SmileyToolThread, stwp);
		}
		if (LOWORD(wParam) == MI_IDC_ADD && HIWORD(wParam) == BN_CLICKED && dat->doSmileyButton) {
			RECT rect = dat->CalcSmileyButtonPos();
			SetWindowPos(dat->hSmlButton, NULL, rect.left, rect.top,	0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
		}
		break;
	}
	return result;
}
static int MsgDlgHook(WPARAM, LPARAM lParam)
{
	const MessageWindowEventData *wndEvtData = (MessageWindowEventData*)lParam;
	switch (wndEvtData->uType) {
	case MSG_WINDOW_EVT_OPENING:
		if (wndEvtData->cbSize >= sizeof(MessageWindowEventData)) {
			MsgWndData *msgwnd = new MsgWndData();
			msgwnd->hwnd = wndEvtData->hwndWindow;
			msgwnd->hContact = wndEvtData->hContact;
			msgwnd->REdit = wndEvtData->hwndLog;
			msgwnd->MEdit = wndEvtData->hwndInput;
			msgwnd->LButton = GetDlgItem(wndEvtData->hwndWindow, MI_IDC_ADD);
			if (msgwnd->LButton == NULL)
				msgwnd->LButton = GetDlgItem(wndEvtData->hwndWindow, 5019);
			// Get the protocol for this contact to display correct smileys.
			char *protonam = GetContactProto(DecodeMetaContact(msgwnd->hContact));
			if (protonam)
				strncpy_s(msgwnd->ProtocolName, protonam, _TRUNCATE);
			mir_subclassWindow(msgwnd->hwnd, MessageDlgSubclass);
			msgwnd->CreateSmileyButton();
			{
				mir_cslock lck(csWndList);
				g_MsgWndList.insert(msgwnd);
			}
			SetRichOwnerCallback(wndEvtData->hwndWindow, wndEvtData->hwndInput, wndEvtData->hwndLog);
			if (wndEvtData->hwndLog)
				SetRichCallback(wndEvtData->hwndLog, wndEvtData->hContact, false, false);
			if (wndEvtData->hwndInput)
				SetRichCallback(wndEvtData->hwndInput, wndEvtData->hContact, false, false);
		}
		break;
	case MSG_WINDOW_EVT_OPEN:
		if (wndEvtData->cbSize >= sizeof(MessageWindowEventData)) {
			SetRichOwnerCallback(wndEvtData->hwndWindow, wndEvtData->hwndInput, wndEvtData->hwndLog);
			if (wndEvtData->hwndLog)
				SetRichCallback(wndEvtData->hwndLog, wndEvtData->hContact, true, true);
			if (wndEvtData->hwndInput) {
				SetRichCallback(wndEvtData->hwndInput, wndEvtData->hContact, true, true);
				SendMessage(wndEvtData->hwndInput, WM_REMAKERICH, 0, 0);
			}
		}
		break;
	case MSG_WINDOW_EVT_CLOSE:
		if (wndEvtData->cbSize >= sizeof(MessageWindowEventData) && wndEvtData->hwndLog) {
			CloseRichCallback(wndEvtData->hwndLog);
			CloseRichOwnerCallback(wndEvtData->hwndWindow);
		}
		mir_unsubclassWindow(wndEvtData->hwndWindow, MessageDlgSubclass);
		break;
	}
	return 0;
}
void InstallDialogBoxHook(void)
{
	HookEvent(ME_MSG_WINDOWEVENT, MsgDlgHook);
}
void RemoveDialogBoxHook(void)
{
	mir_cslock lck(csWndList);
	for (int i = 0; i < g_MsgWndList.getCount(); i++)
		delete g_MsgWndList[i];
	g_MsgWndList.destroy();
}