/*

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

Copyright 2000-2003 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"

int DefaultImageListColorDepth=ILC_COLOR32;

HIMAGELIST himlCListClc;

static HANDLE hSettingChanged1;

int BgStatusBarChange(WPARAM wParam,LPARAM lParam);

int BgClcChange(WPARAM wParam,LPARAM lParam);
int OnFrameTitleBarBackgroundChange(WPARAM wParam,LPARAM lParam);

void InitDisplayNameCache(SortedList *list);
void FreeDisplayNameCache(SortedList *list);
void LoadExtraImageFunc( void );

void LoadExtraImageFunc( void );
void UnloadCLUIModule( void );

extern int sortByStatus;
struct ClcContact * hitcontact=NULL;

extern LRESULT ( CALLBACK *saveContactListControlWndProc )(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

static int stopStatusUpdater = 0;
void StatusUpdaterThread(HWND hwndDlg)
{
	int i,curdelay,lastcheck=0;
	HANDLE hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);

	SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_LOWEST);

	while (!stopStatusUpdater) {
		curdelay=DBGetContactSettingByte(hContact,"CList","StatusMsgAutoDelay",15000);
		if (curdelay<5000) curdelay=5000;

		if ((int)(GetTickCount()-lastcheck)>curdelay)
		{
			lastcheck=GetTickCount();
			if (DBGetContactSettingByte(hContact,"CList","StatusMsgAuto",0)) {
				for (i=0; i<5; i++) {
					if (hContact!=NULL) {
						pdisplayNameCacheEntry pdnce =(pdisplayNameCacheEntry)pcli->pfnGetCacheEntry((HANDLE)hContact);
						if (pdnce && !pdnce->protoNotExists && pdnce->szProto)
						{			
							CallContactService(hContact, PSS_GETAWAYMSG, 0, 0);
						}
						hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0);
					}
					if (hContact==NULL) {
						hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
						if (hContact==NULL) break;
					}
					Sleep(500);
				}
			}
		}
		//Sleep(DBGetContactSettingByte(hContact,"CList","StatusMsgAutoDelay",100));
		Sleep(200);
	}
}

HMENU BuildGroupPopupMenu( struct ClcGroup* group )
{
	return (HMENU)CallService(MS_CLIST_MENUBUILDSUBGROUP,(WPARAM)group,0);
}

void SortClcByTimer (HWND hwnd)
{
	KillTimer(hwnd,TIMERID_DELAYEDRESORTCLC);
	SetTimer(hwnd,TIMERID_DELAYEDRESORTCLC,DBGetContactSettingByte(NULL,"CLUI","DELAYEDTIMER",200),NULL);
}

static int ClcSettingChanged(WPARAM wParam, LPARAM lParam)
{
	DBCONTACTWRITESETTING *cws=(DBCONTACTWRITESETTING*)lParam;

	if ((HANDLE)wParam != NULL && !strcmp(cws->szModule,"MetaContacts") && !strcmp(cws->szSetting,"Handle"))
		pcli->pfnClcBroadcast( INTM_NAMEORDERCHANGED, 0, 0 );
	
	if ((HANDLE)wParam!=NULL&&!strcmp(cws->szModule,"CList")) {
		if( !strcmp( cws->szSetting, "noOffline" ))
			pcli->pfnClcBroadcast( INTM_NAMEORDERCHANGED, wParam, lParam );
		else if(!strcmp(cws->szSetting,"StatusMsg")) 
			pcli->pfnClcBroadcast( INTM_STATUSMSGCHANGED, wParam, lParam );
	}
	return 0;
}

static int ClcModulesLoaded(WPARAM wParam,LPARAM lParam)
{
	CallService(MS_BACKGROUNDCONFIG_REGISTER,(WPARAM)"StatusBar Background/StatusBar",0);
	CallService(MS_BACKGROUNDCONFIG_REGISTER,(WPARAM)"List Background/CLC",0);
	CallService(MS_BACKGROUNDCONFIG_REGISTER,(WPARAM)"Frames TitleBar BackGround/FrameTitleBar",0);
	HookEvent(ME_BACKGROUNDCONFIG_CHANGED,BgClcChange);
	HookEvent(ME_BACKGROUNDCONFIG_CHANGED,BgStatusBarChange);
	HookEvent(ME_BACKGROUNDCONFIG_CHANGED,OnFrameTitleBarBackgroundChange);
	return 0;
}

static int ClcShutdown(WPARAM wParam, LPARAM lParam)
{
	UnhookEvent(hSettingChanged1);
	UnloadCLUIModule();
	return 0;
}

LRESULT CALLBACK ContactListControlWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{     
	struct ClcData *dat = (struct ClcData*)GetWindowLongPtr(hwnd,0);
	if ( msg >= CLM_FIRST && msg < CLM_LAST )
		return pcli->pfnProcessExternalMessages(hwnd,dat,msg,wParam,lParam);

	switch (msg) {
	case WM_CREATE:
		dat = (struct ClcData*)mir_calloc( sizeof(struct ClcData));
		SetWindowLong(hwnd,0,(LPARAM)dat);
		InitDisplayNameCache(&dat->lCLCContactsCache);
		break;

	case INTM_ICONCHANGED:
	{	struct ClcContact *contact=NULL;
		struct ClcGroup *group=NULL;
		int recalcScrollBar=0,shouldShow;
		HANDLE hSelItem=NULL;
		struct ClcContact *selcontact=NULL;
		pdisplayNameCacheEntry cacheEntry = GetContactFullCacheEntry((HANDLE)wParam);

		WORD status;
		int NeedResort=0;

		char *szProto = cacheEntry->szProto;
		if(szProto==NULL) status=ID_STATUS_OFFLINE;
		else status=cacheEntry->status;
		
		shouldShow=(GetWindowLong(hwnd,GWL_STYLE)&CLS_SHOWHIDDEN || !cacheEntry->Hidden) && (!pcli->pfnIsHiddenMode(dat,status)||cacheEntry->noHiddenOffline || CallService(MS_CLIST_GETCONTACTICON,wParam,0)!=LOWORD(lParam));	//this means an offline msg is flashing, so the contact should be shown
		if(!FindItem(hwnd,dat,(HANDLE)wParam,&contact,&group,NULL)) {				
			if(shouldShow && CallService(MS_DB_CONTACT_IS, wParam, 0)) {
				if(dat->selection>=0 && GetRowByIndex(dat,dat->selection,&selcontact,NULL)!=-1)
					hSelItem=pcli->pfnContactToHItem(selcontact);
				AddContactToTree(hwnd,dat,(HANDLE)wParam,0,0);
				NeedResort=1;
				recalcScrollBar=1;					
				FindItem(hwnd,dat,(HANDLE)wParam,&contact,NULL,NULL);
				if (contact) {						
					contact->iImage=(WORD)lParam;
					pcli->pfnNotifyNewContact(hwnd,(HANDLE)wParam);
					dat->NeedResort=1;
				}
			}				
		}
		else {
			//item in list already
			DWORD style=GetWindowLong(hwnd,GWL_STYLE);				
			if(contact->iImage== (WORD)lParam) break;				
			if (sortByStatus) dat->NeedResort=1;

			if(!shouldShow && !(style&CLS_NOHIDEOFFLINE) && (style&CLS_HIDEOFFLINE || group->hideOffline)) {
				if(dat->selection>=0 && GetRowByIndex(dat,dat->selection,&selcontact,NULL)!=-1)
					hSelItem=pcli->pfnContactToHItem(selcontact);
				RemoveItemFromGroup(hwnd,group,contact,0);
				recalcScrollBar=1;
				dat->NeedResort=1;
			}
			else {
				int oldflags;
				contact->iImage=(WORD)lParam;
				oldflags=contact->flags;
				if(!pcli->pfnIsHiddenMode(dat,status)||cacheEntry->noHiddenOffline) contact->flags|=CONTACTF_ONLINE;
				else contact->flags&=~CONTACTF_ONLINE;
				if (oldflags!=contact->flags)
					dat->NeedResort=1;
		}	}
		if(hSelItem) {
			struct ClcGroup *selgroup;
			if(FindItem(hwnd,dat,hSelItem,&selcontact,&selgroup,NULL))
				dat->selection=GetRowsPriorTo(&dat->list,selgroup,li.List_IndexOf((SortedList*)&selgroup->cl, selcontact));
			else
				dat->selection=-1;
		}

		SortClcByTimer(hwnd);
		if(recalcScrollBar) RecalcScrollBar(hwnd,dat);			
		goto LBL_Exit;
	}
	case INTM_STATUSMSGCHANGED:
	{	struct ClcContact *contact=NULL;
		struct ClcGroup *group=NULL;
		DBVARIANT dbv;

		if (!(dat->style&CLS_SHOWSTATUSMESSAGES)) break;
		if(FindItem(hwnd,dat,(HANDLE)wParam,&contact,&group,NULL) && contact!=NULL) {
			contact->flags &= ~CONTACTF_STATUSMSG;
			if (!DBGetContactSettingTString((HANDLE)wParam, "CList", "StatusMsg", &dbv)) {
				int j;
				if (dbv.ptszVal==NULL||_tcslen(dbv.ptszVal)==0) break;
				lstrcpyn(contact->szStatusMsg, dbv.ptszVal, SIZEOF(contact->szStatusMsg));
				for (j=(int)_tcslen(contact->szStatusMsg)-1;j>=0;j--) {
					if (contact->szStatusMsg[j]=='\r' || contact->szStatusMsg[j]=='\n' || contact->szStatusMsg[j]=='\t') {
						contact->szStatusMsg[j] = ' ';
					}
				}
				DBFreeVariant(&dbv);
				if (_tcslen(contact->szStatusMsg)>0) {
					contact->flags |= CONTACTF_STATUSMSG;
					dat->NeedResort=TRUE;
				}
			}
		}

		InvalidateRect(hwnd,NULL,TRUE);

		SortClcByTimer(hwnd);
		RecalcScrollBar(hwnd,dat);
		goto LBL_Exit;
	}
	case WM_TIMER:
		if (wParam==TIMERID_DELAYEDREPAINT) {
			KillTimer(hwnd,TIMERID_DELAYEDREPAINT);
			InvalidateRect(hwnd,NULL,FALSE);
			break;
		}

		if ( wParam == TIMERID_SUBEXPAND) {		
			KillTimer(hwnd,TIMERID_SUBEXPAND);
			if (hitcontact) {
				if (hitcontact->SubExpanded) hitcontact->SubExpanded=0; else hitcontact->SubExpanded=1;
				DBWriteContactSettingByte(hitcontact->hContact,"CList","Expanded",hitcontact->SubExpanded);
			}
			hitcontact=NULL;
			dat->NeedResort=1;
			SortCLC(hwnd,dat,1);		
			RecalcScrollBar(hwnd,dat);
			break;
		}			
		break;

	case WM_DESTROY:
		FreeDisplayNameCache(&dat->lCLCContactsCache);
		stopStatusUpdater = 1;
		break;
	}

	{	LRESULT res = saveContactListControlWndProc(hwnd, msg, wParam, lParam);
		switch (msg) {
			case WM_CREATE:
				mir_forkthread(StatusUpdaterThread,0);
				break;
		}
		return res;
	}

LBL_Exit:
	return DefWindowProc(hwnd, msg, wParam, lParam);
}

int LoadCLCModule(void)
{
	LoadCLUIFramesModule();
	LoadExtraImageFunc();	

	himlCListClc=(HIMAGELIST)CallService(MS_CLIST_GETICONSIMAGELIST,0,0);
	
	HookEvent(ME_SYSTEM_MODULESLOADED,ClcModulesLoaded);
	hSettingChanged1=HookEvent(ME_DB_CONTACT_SETTINGCHANGED,ClcSettingChanged);
	HookEvent(ME_OPT_INITIALISE,ClcOptInit);
	HookEvent(ME_SYSTEM_SHUTDOWN,ClcShutdown);
	return 0;
}
