/*

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

Copyright (c) 2012-14 Miranda NG project (http://miranda-ng.org),
Copyright (c) 2000-03 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 <commonheaders.h>
#include "../cluiframes/cluiframes.h"

HINSTANCE g_hInst = 0;
CLIST_INTERFACE *pcli, coreCli;

int hLangpack;

#define DEFAULT_TB_VISIBILITY (1 | 2 | 4 | 8 | 16 | 32 | 64 | 8192)

extern HICON overlayicons[10];

extern int Docking_ProcessWindowMessage(WPARAM wParam, LPARAM lParam);
extern int SetHideOffline(WPARAM wParam, LPARAM lParam);

extern DWORD g_gdiplusToken;

TIME_API tmi;

HMENU BuildGroupPopupMenu(ClcGroup *group);
ClcContact *CreateClcContact(void);
CListEvent *fnCreateEvent(void);
void ReloadThemedOptions();
int TrayCalcChanged(const char *szChangedProto, int averageMode, int iProtoCount);
void RegisterCLUIFrameClasses();
void LoadButtonModule();

void GetDefaultFontSetting(int i, LOGFONT *lf, COLORREF *colour);
int GetWindowVisibleState(HWND hWnd, int iStepX, int iStepY);
int ShowHide(WPARAM wParam, LPARAM lParam);
int ClcShutdown(WPARAM wParam, LPARAM lParam);

CListEvent *AddEvent(CLISTEVENT *cle);
ClcGroup *AddGroup(HWND hwnd, struct ClcData *dat, const TCHAR *szName, DWORD flags, int groupId, int calcTotalMembers);

int     AddContactToGroup(struct ClcData *dat, ClcGroup *group, MCONTACT hContact);
int     AddInfoItemToGroup(ClcGroup *group, int flags, const TCHAR *pszText);
LRESULT ProcessExternalMessages(HWND hwnd, struct ClcData *dat, UINT msg, WPARAM wParam, LPARAM lParam);
int     RemoveEvent(MCONTACT hContact, HANDLE hDbEvent);
INT_PTR TrayIconProcessMessage(WPARAM wParam, LPARAM lParam);
void    RecalcScrollBar(HWND hwnd, struct ClcData *dat);

LRESULT CALLBACK ContactListWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK ContactListControlWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

PLUGININFOEX pluginInfo =
{
	sizeof(PLUGININFOEX),
	__PLUGIN_NAME,
	PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
	__DESCRIPTION,
	__AUTHOR,
	__AUTHOREMAIL,
	__COPYRIGHT,
	__AUTHORWEB,
	UNICODE_AWARE,
	// {8F79B4EE-EB48-4A03-873E-27BE6B7E9A25}
	{0x8f79b4ee, 0xeb48, 0x4a03, {0x87, 0x3e, 0x27, 0xbe, 0x6b, 0x7e, 0x9a, 0x25}}
};


void _DebugTraceW(const wchar_t *fmt, ...)
{
#ifdef _DEBUG
	wchar_t debug[2048];
	int ibsize = 2047;
	va_list va;
	va_start(va, fmt);

	mir_wstrcpy(debug, L"CLN: ");

	mir_vsnwprintf(&debug[5], ibsize - 10, fmt, va);
	OutputDebugStringW(debug);
#endif
}


void _DebugTraceA(const char *fmt, ...)
{
	char debug[2048];
	int ibsize = 2047;
	va_list va;
	va_start(va, fmt);

	mir_strcpy(debug, "CLN: ");
	mir_vsnprintf(&debug[5], ibsize - 10, fmt, va);
#ifdef _DEBUG
	OutputDebugStringA(debug);
#else
	{
		char szLogFileName[MAX_PATH], szDataPath[MAX_PATH];
		FILE *f;

		CallService(MS_DB_GETPROFILEPATH, MAX_PATH, (LPARAM)szDataPath);
		mir_snprintf(szLogFileName, MAX_PATH, "%s\\%s", szDataPath, "clist_nicer.log");
		f = fopen(szLogFileName, "a+");
		if (f) {
			fputs(debug, f);
			fputs("\n", f);
			fclose(f);
		}
	}
#endif
}

BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD dwReason, LPVOID reserved)
{
	g_hInst = hInstDLL;
	DisableThreadLibraryCalls(g_hInst);
	return TRUE;
}

extern "C" __declspec(dllexport) PLUGININFOEX *MirandaPluginInfoEx(DWORD mirandaVersion)
{
	return &pluginInfo;
}

extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = { MIID_CLIST, MIID_LAST };

int PreloadContactListModule(void);
int LoadContactListModule(void);
int LoadCLCModule(void);
void LoadCLUIModule(void);
void OnCreateClc(void);

static int systemModulesLoaded(WPARAM wParam, LPARAM lParam)
{
	GetSystemTime(&cfg::dat.st);
	SystemTimeToFileTime(&cfg::dat.st, &cfg::dat.ft);

	cfg::dat.bMetaEnabled = db_mc_isEnabled();

	cfg::dat.bAvatarServiceAvail = ServiceExists(MS_AV_GETAVATARBITMAP) ? TRUE : FALSE;
	if (cfg::dat.bAvatarServiceAvail)
		HookEvent(ME_AV_AVATARCHANGED, AvatarChanged);
	cfg::dat.tabSRMM_Avail = ServiceExists("SRMsg_MOD/GetWindowFlags") ? TRUE : FALSE;

	memset(&overlayicons, 0, sizeof(overlayicons));

	CLN_LoadAllIcons(1);
	return 0;
}

static int fnIconFromStatusMode(const char *szProto, int status, MCONTACT hContact)
{
	return IconFromStatusMode(szProto, status, hContact, NULL);
}

extern "C" int __declspec(dllexport) CListInitialise()
{
	mir_getLP( &pluginInfo );
	mir_getTMI(&tmi);

	mir_getCLI();
	coreCli = *pcli;

	API::onInit();
	RegisterCLUIFrameClasses();

	memset(&cfg::dat, 0, sizeof(cfg::dat));

	int iCount = CallService(MS_DB_CONTACT_GETCOUNT, 0, 0);

	iCount += 20;
	if (iCount < 300)
		iCount = 300;

	cfg::init();

	cfg::dat.hMenuNotify = CreatePopupMenu();
	cfg::dat.wNextMenuID = 1;
	cfg::dat.sortTimer = cfg::getDword("CLC", "SortTimer", 150);
	cfg::dat.avatarBorder = (COLORREF)cfg::getDword("CLC", "avatarborder", 0);
	cfg::dat.avatarRadius = (COLORREF)cfg::getDword("CLC", "avatarradius", 4);
	cfg::dat.hBrushAvatarBorder = CreateSolidBrush(cfg::dat.avatarBorder);
	cfg::dat.avatarSize = cfg::getWord("CList", "AvatarSize", 24);
	cfg::dat.dualRowMode = cfg::getByte("CLC", "DualRowMode", 0);
	cfg::dat.avatarPadding = cfg::getByte("CList", "AvatarPadding", 0);
	cfg::dat.isTransparent = cfg::getByte("CList", "Transparent", 0);
	cfg::dat.alpha = cfg::getByte("CList", "Alpha", SETTING_ALPHA_DEFAULT);
	cfg::dat.autoalpha = cfg::getByte("CList", "AutoAlpha", SETTING_ALPHA_DEFAULT);
	cfg::dat.fadeinout = cfg::getByte("CLUI", "FadeInOut", 0);
	cfg::dat.autosize = cfg::getByte("CLUI", "AutoSize", 0);
	cfg::dat.bNoOfflineAvatars = cfg::getByte("CList", "NoOfflineAV", 1);
	cfg::dat.bFullTransparent = cfg::getByte("CLUI", "fulltransparent", 0);
	cfg::dat.bDblClkAvatars = cfg::getByte("CLC", "dblclkav", 0);
	cfg::dat.bEqualSections = cfg::getByte("CLUI", "EqualSections", 0);
	cfg::dat.bCenterStatusIcons = cfg::getByte("CLC", "si_centered", 1);
	cfg::dat.boldHideOffline = -1;
	cfg::dat.bSecIMAvail = ServiceExists("SecureIM/IsContactSecured") ? 1 : 0;
	cfg::dat.bNoTrayTips = cfg::getByte("CList", "NoTrayTips", 0);
	cfg::dat.bShowLocalTime = cfg::getByte("CLC", "ShowLocalTime", 1);
	cfg::dat.bShowLocalTimeSelective = cfg::getByte("CLC", "SelectiveLocalTime", 1);
	cfg::dat.bDontSeparateOffline = cfg::getByte("CList", "DontSeparateOffline", 0);
	cfg::dat.bShowXStatusOnSbar = cfg::getByte("CLUI", "xstatus_sbar", 0);
	cfg::dat.bLayeredHack = cfg::getByte("CLUI", "layeredhack", 1);
	cfg::dat.bFirstRun = cfg::getByte("CLUI", "firstrun", 1);
	cfg::dat.langPackCP = CallService(MS_LANGPACK_GETCODEPAGE, 0, 0);
	cfg::dat.realTimeSaving = cfg::getByte("CLUI", "save_pos_always", 0);

	DWORD sortOrder = cfg::getDword("CList", "SortOrder", SORTBY_NAME);
	cfg::dat.sortOrder[0] = LOBYTE(LOWORD(sortOrder));
	cfg::dat.sortOrder[1] = HIBYTE(LOWORD(sortOrder));
	cfg::dat.sortOrder[2] = LOBYTE(HIWORD(sortOrder));

	if (cfg::dat.bFirstRun)
		cfg::writeByte("CLUI", "firstrun", 0);

	ReloadThemedOptions();
	Reload3dBevelColors();

	cfg::dat.dwFlags = cfg::getDword("CLUI", "Frameflags", CLUI_FRAME_STATUSICONS | CLUI_FRAME_SHOWBOTTOMBUTTONS | CLUI_FRAME_BUTTONSFLAT | CLUI_FRAME_CLISTSUNKEN);
	cfg::dat.dwFlags |= (cfg::getByte("CLUI", "ShowSBar", 1) ? CLUI_FRAME_SBARSHOW : 0);
	cfg::dat.soundsOff = cfg::getByte("Skin", "UseSound", 1) ? 0 : 1;

	CallService(MS_DB_GETPROFILEPATHT, MAX_PATH, (LPARAM)cfg::dat.tszProfilePath);
	_tcslwr(cfg::dat.tszProfilePath);

	PreloadContactListModule();

	// get the clist interface
	pcli->hInst = g_hInst;
	pcli->pfnBuildGroupPopupMenu = BuildGroupPopupMenu;
	pcli->pfnCluiProtocolStatusChanged = CluiProtocolStatusChanged;
	pcli->pfnCompareContacts = CompareContacts;
	pcli->pfnCreateClcContact = CreateClcContact;
	pcli->pfnCreateEvent = fnCreateEvent;
	pcli->pfnDocking_ProcessWindowMessage = Docking_ProcessWindowMessage;
	pcli->pfnGetDefaultFontSetting = GetDefaultFontSetting;
	pcli->pfnGetRowBottomY = RowHeight::getItemBottomY;
	pcli->pfnGetRowHeight = RowHeight::getHeight;
	pcli->pfnGetRowTopY = RowHeight::getItemTopY;
	pcli->pfnGetRowTotalHeight = RowHeight::getTotalHeight;
	pcli->pfnGetWindowVisibleState = GetWindowVisibleState;
	pcli->pfnHitTest = HitTest;
	pcli->pfnLoadContactTree = LoadContactTree;
	pcli->pfnOnCreateClc = OnCreateClc;
	pcli->pfnPaintClc = PaintClc;
	pcli->pfnRebuildEntireList = RebuildEntireList;
	pcli->pfnRowHitTest = RowHeight::hitTest;
	pcli->pfnScrollTo = ScrollTo;
	pcli->pfnTrayCalcChanged = TrayCalcChanged;
	pcli->pfnSetHideOffline = SetHideOffline;
	pcli->pfnShowHide = ShowHide;

	pcli->pfnAddContactToGroup = AddContactToGroup;

	pcli->pfnAddEvent = AddEvent;
	pcli->pfnRemoveEvent = RemoveEvent;

	pcli->pfnAddGroup = AddGroup;
	pcli->pfnAddInfoItemToGroup = AddInfoItemToGroup;
	pcli->pfnContactListControlWndProc = ContactListControlWndProc;
	pcli->pfnContactListWndProc = ContactListWndProc;
	pcli->pfnIconFromStatusMode = fnIconFromStatusMode;
	pcli->pfnLoadClcOptions = LoadClcOptions;
	pcli->pfnProcessExternalMessages = ProcessExternalMessages;
	pcli->pfnRecalcScrollBar = RecalcScrollBar;
	pcli->pfnTrayIconProcessMessage = TrayIconProcessMessage;

	int rc = LoadContactListModule();
	if (rc == 0)
		rc = LoadCLCModule();
	LoadCLUIModule();
	LoadButtonModule();

	HookEvent(ME_SYSTEM_MODULESLOADED, systemModulesLoaded);
	return rc;
}

// a plugin loader aware of CList exports will never call this.
extern "C" int __declspec(dllexport) Load(void)
{
	return 1;
}

extern "C" int __declspec(dllexport) Unload(void)
{
	if (IsWindow(pcli->hwndContactList))
		DestroyWindow(pcli->hwndContactList);
	ClcShutdown(0, 0);
	UnLoadCLUIFramesModule();
	return 0;
}