/*
"Last Seen mod" plugin for Miranda IM
Copyright ( C ) 2002-03  micron-x
Copyright ( C ) 2005-07  Y.B.

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 name      : $URL: http://svn.berlios.de/svnroot/repos/mgoodies/trunk/lastseen-mod/utils.c $
Revision       : $Rev: 1570 $
Last change on : $Date: 2007-12-30 01:30:07 +0300 (Вс, 30 дек 2007) $
Last change by : $Author: y_b $
*/
#include "seen.h"
#include <m_ignore.h>
#include <time.h>


void FileWrite(HANDLE);
void HistoryWrite(HANDLE hcontact);
//void SetOffline(void);
void ShowHistory(HANDLE hContact, BYTE isAlert);

char * courProtoName = 0;

//copied from ..\..\miranda32\protocols\protocols\protocols.c
PROTOCOLDESCRIPTOR* Proto_IsProtocolLoaded(const char* szProto)
{
	return (PROTOCOLDESCRIPTOR*) CallService(MS_PROTO_ISPROTOCOLLOADED, 0, (LPARAM)szProto);
}


/*
Returns true if the protocols is to be monitored
*/
int IsWatchedProtocol(const char* szProto)
{
	DBVARIANT dbv;
	char *szProtoPointer, *szWatched;
	int iProtoLen, iWatchedLen;
	int retval = 0;
	PROTOCOLDESCRIPTOR *pd;

	if (szProto == NULL)
		return 0;
	
	pd=Proto_IsProtocolLoaded(szProto);
	if (pd==NULL || pd->type!=PROTOTYPE_PROTOCOL || CallProtoService(pd->szName,PS_GETCAPS,PFLAGNUM_2,0)==0)
		return 0;

	iProtoLen = (int)_tcslen(szProto);
	if(DBGetContactSetting(NULL, S_MOD, "WatchedProtocols", &dbv))
		szWatched = DEFAULT_WATCHEDPROTOCOLS;
	else
		szWatched = dbv.pszVal;
	iWatchedLen = (int)_tcslen(szWatched);

	if (*szWatched == '\0') 
	{
		retval=1; //empty string: all protocols are watched
	} 
	else 
	{
		char sTemp [MAXMODULELABELLENGTH+1]="";
		strcat(sTemp,szProto);
		strcat(sTemp," ");
		szProtoPointer = strstr(szWatched, sTemp);
		if (szProtoPointer == NULL)
			retval=0;
		else 
			retval=1;
	}

	DBFreeVariant(&dbv);
	return retval;
}

BOOL isYahoo(char * protoname){
	if (protoname) {
		char *pszUniqueSetting = (char*)CallProtoService(protoname, PS_GETCAPS, PFLAG_UNIQUEIDSETTING, 0);
		if (pszUniqueSetting){
			return (!strcmp(pszUniqueSetting,"yahoo_id"));
	}	}
	return FALSE;
}
BOOL isJabber(char * protoname){
	if (protoname) {
		char *pszUniqueSetting = (char*)CallProtoService(protoname, PS_GETCAPS, PFLAG_UNIQUEIDSETTING, 0);
		if (pszUniqueSetting){
			return (!strcmp(pszUniqueSetting,"jid"));
	}	}
	return FALSE;
}
BOOL isICQ(char * protoname){
	if (protoname) {
		char *pszUniqueSetting = (char*)CallProtoService(protoname, PS_GETCAPS, PFLAG_UNIQUEIDSETTING, 0);
		if (pszUniqueSetting){
			return (!strcmp(pszUniqueSetting,"UIN"));
	}	}
	return FALSE;
}
BOOL isMSN(char * protoname){
	if (protoname) {
		char *pszUniqueSetting = (char*)CallProtoService(protoname, PS_GETCAPS, PFLAG_UNIQUEIDSETTING, 0);
		if (pszUniqueSetting){
			return (!strcmp(pszUniqueSetting,"e-mail"));
	}	}
	return FALSE;
}

DWORD isSeen(HANDLE hcontact, SYSTEMTIME *st){
	DWORD res = 0;
	FILETIME ft;
	ULONGLONG ll;
	res = DBGetContactSettingDword(hcontact,S_MOD,"seenTS",0);
	if (res){
		if (st) {
			ll = UInt32x32To64(CallService(MS_DB_TIME_TIMESTAMPTOLOCAL,res,0), 10000000) + NUM100NANOSEC;
			ft.dwLowDateTime = (DWORD)ll;
			ft.dwHighDateTime = (DWORD)(ll >> 32);
			FileTimeToSystemTime(&ft, st);
		}
		return res;
	} else {
		SYSTEMTIME lst;
		ZeroMemory(&lst,sizeof(lst));
		if (lst.wYear = DBGetContactSettingWord(hcontact,S_MOD,"Year",0)) {
			if (lst.wMonth = DBGetContactSettingWord(hcontact,S_MOD,"Month",0)) {
				if (lst.wDay = DBGetContactSettingWord(hcontact,S_MOD,"Day",0)) {
					lst.wDayOfWeek = DBGetContactSettingWord(hcontact,S_MOD,"WeekDay",0);
					lst.wHour = DBGetContactSettingWord(hcontact,S_MOD,"Hours",0);
					lst.wMinute = DBGetContactSettingWord(hcontact,S_MOD,"Minutes",0);
					lst.wSecond = DBGetContactSettingWord(hcontact,S_MOD,"Seconds",0);
					if (SystemTimeToFileTime(&lst,&ft)) {
						ll = ((LONGLONG)ft.dwHighDateTime<<32)|((LONGLONG)ft.dwLowDateTime);
						ll -= NUM100NANOSEC;
						ll /= 10000000;
						//perform LOCALTOTIMESTAMP
						res = (DWORD)ll - CallService(MS_DB_TIME_TIMESTAMPTOLOCAL,0,0);
						//nevel look for Year/Month/Day/Hour/Minute/Second again
						DBWriteContactSettingDword(hcontact,S_MOD,"seenTS",res);
					}
		}	}	}
		if (st) CopyMemory (st, &lst, sizeof (SYSTEMTIME));
	}
	return res;
}

char *ParseString(char *szstring,HANDLE hcontact,BYTE isfile)
{
#define MAXSIZE 1024
	static char sztemp[MAXSIZE+1];
	int sztemplen = 0;
	char szdbsetting[128]="";
	char *charPtr;
	UINT loop=0;
	int isetting=0;
	DWORD dwsetting=0;
	struct in_addr ia;
	char *weekdays[]={"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};
	char *wdays_short[]={"Sun.","Mon.","Tue.","Wed.","Thu.","Fri.","Sat."};
	char *monthnames[]={"January","February","March","April","May","June","July","August","September","October","November","December"};
	char *mnames_short[]={"Jan.","Feb.","Mar.","Apr.","May","Jun.","Jul.","Aug.","Sep.","Oct.","Nov.","Dec."};
	CONTACTINFO ci;
	BOOL wantempty;
	SYSTEMTIME st;

	sztemp[0] = '\0';
	if (!isSeen(hcontact,&st)) {
		strcat(sztemp,Translate("<never seen>"));
		return sztemp;
	}

	ci.cbSize=sizeof(CONTACTINFO);
	ci.hContact=hcontact;
	ci.szProto=hcontact?(char *)CallService(MS_PROTO_GETCONTACTBASEPROTO,(WPARAM)hcontact,0):courProtoName;
	for (;loop<strlen(szstring);loop++)
	{
		if (sztemplen == MAXSIZE) break;
		if ((szstring[loop]!='%')&(szstring[loop]!='#'))
		{
			strncat(sztemp,szstring+loop,1);
			sztemplen++;
			continue;
		}

		else
		{
			wantempty = (szstring[loop]=='#');
			switch(szstring[++loop]) {
				case 'Y':
					if (!st.wYear) goto LBL_noData;
					sztemplen += mir_snprintf(sztemp+sztemplen,MAXSIZE-sztemplen,"%04i",st.wYear);
					break;

				case 'y':
					if (!st.wYear) goto LBL_noData;
					wsprintf(szdbsetting,"%04i",st.wYear);
					sztemplen += mir_snprintf(sztemp+sztemplen,MAXSIZE-sztemplen,"%s",szdbsetting+2);
					break;

				case 'm':
					if (!(isetting=st.wMonth)) goto LBL_noData;
LBL_2DigNum:
					sztemplen += mir_snprintf(sztemp+sztemplen,MAXSIZE-sztemplen,"%02i",isetting);
					break;

				case 'd':
					if (isetting=st.wDay) goto LBL_2DigNum;
					else goto LBL_noData;

				case 'W':
					isetting=st.wDayOfWeek;
					if(isetting==-1){
LBL_noData:
						charPtr = wantempty?"":Translate("<unknown>");
						goto LBL_charPtr;
					}
					charPtr = Translate(weekdays[isetting]);
LBL_charPtr:
					sztemplen += mir_snprintf(sztemp+sztemplen,MAXSIZE-sztemplen,"%s",charPtr);
					break;

				case 'w':
					isetting=st.wDayOfWeek;
					if(isetting==-1)goto LBL_noData;
					charPtr = Translate(wdays_short[isetting]);
					goto LBL_charPtr;

				case 'E':
					if (!(isetting=st.wMonth))goto LBL_noData;
					charPtr = Translate(monthnames[isetting-1]);
					goto LBL_charPtr;

				case 'e':
					if (!(isetting=st.wMonth))goto LBL_noData;
					charPtr = Translate(mnames_short[isetting-1]);
					goto LBL_charPtr;

				case 'H':
					if ((isetting=st.wHour)==-1)goto LBL_noData;
					goto LBL_2DigNum;

				case 'h':
					if ((isetting=st.wHour)==-1)goto LBL_noData;
					if (!isetting) isetting=12;
					isetting = isetting-((isetting>12)?12:0);
					goto LBL_2DigNum;

				case 'p':
					if ((isetting=st.wHour)==-1)goto LBL_noData;
					charPtr = (isetting>=12)?"PM":"AM";
					goto LBL_charPtr;

				case 'M':
					if ((isetting=st.wMinute)==-1)goto LBL_noData;
					goto LBL_2DigNum;

				case 'S':
					if ((isetting=st.wHour)==-1)goto LBL_noData;
					goto LBL_2DigNum;

				case 'n':
					charPtr = hcontact?(char *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME,(WPARAM)hcontact,0):(wantempty?"":"---");
					goto LBL_charPtr;
				case 'N':
					ci.dwFlag=CNF_NICK;
					if (!CallService(MS_CONTACT_GETCONTACTINFO,(WPARAM)0,(LPARAM)&ci)) {
						charPtr = ci.pszVal;
					} else goto LBL_noData;
					goto LBL_charPtr;
				case 'G':
					{
						DBVARIANT dbv;
						if (!DBGetContactSetting(hcontact,"CList","Group",&dbv)) {
							strcpy(szdbsetting,dbv.pszVal);
							DBFreeVariant(&dbv);
							charPtr = szdbsetting;
							goto LBL_charPtr;
						} else; //do nothing
					}
					break;

				case 'u':
					ci.dwFlag=CNF_UNIQUEID;
					if (!CallService(MS_CONTACT_GETCONTACTINFO,(WPARAM)0,(LPARAM)&ci))
					{
						switch(ci.type)
						{
							case CNFT_BYTE:
								_ltot(ci.bVal, szdbsetting, 10);
								break;
							case CNFT_WORD:
								_ltot(ci.wVal, szdbsetting, 10);
								break;
							case CNFT_DWORD:
								_ltot(ci.dVal, szdbsetting, 10);
								break;
							case CNFT_ASCIIZ:
								strcpy(szdbsetting, ci.pszVal);
								break;
						}

					}
					else if (ci.szProto != NULL) 
					{
						if (isYahoo(ci.szProto)) // YAHOO support
						{
							DBVARIANT dbv;
							DBGetContactSetting(hcontact,ci.szProto,"id",&dbv);
							strcpy(szdbsetting,dbv.pszVal);
							DBFreeVariant(&dbv);
						}
						else if (isJabber(ci.szProto)) // JABBER support
						{
							DBVARIANT dbv;
							if (DBGetContactSetting(hcontact,ci.szProto,"LoginName",&dbv)) goto LBL_noData;
							strcpy(szdbsetting,dbv.pszVal);
							DBFreeVariant(&dbv);
							DBGetContactSetting(hcontact,ci.szProto,"LoginServer",&dbv);
							strcat(szdbsetting,"@");
							strcat(szdbsetting,dbv.pszVal);
							DBFreeVariant(&dbv);
						} else goto LBL_noData;
					}
					else goto LBL_noData;
					charPtr = szdbsetting;
					goto LBL_charPtr;

				case 's':
					if (isetting=DBGetContactSettingWord(hcontact,S_MOD,hcontact?"StatusTriger":courProtoName,0)) {
						strcpy(szdbsetting,Translate((const char *)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION,(WPARAM)(isetting|0x8000),0)));
						if (!(isetting&0x8000)) {
							strcat(szdbsetting,"/");
							strcat(szdbsetting,Translate("Idle"));
						}
						charPtr = szdbsetting;
					} else goto LBL_noData;
					goto LBL_charPtr;
				case 'T':
					{
						DBVARIANT dbv;
						if (!DBGetContactSetting(hcontact,"CList","StatusMsg",&dbv)) {
							sztemplen += mir_snprintf(sztemp+sztemplen,MAXSIZE-sztemplen,"%s",dbv.pszVal);
							DBFreeVariant(&dbv);
						} else goto LBL_noData;
					}
					break;
				case 'o':
					if (isetting=DBGetContactSettingWord(hcontact,S_MOD,hcontact?"OldStatus":courProtoName,0)) {
						strcpy(szdbsetting,Translate((const char *)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION,(WPARAM)isetting,0)));
						if (includeIdle) if (hcontact) if (DBGetContactSettingByte(hcontact,S_MOD,"OldIdle",0)) {
							strcat(szdbsetting,"/");
							strcat(szdbsetting,Translate("Idle"));
						}
						charPtr = szdbsetting;
					} else goto LBL_noData;
					goto LBL_charPtr;

				case 'i':
				case 'r': if (isJabber(ci.szProto)) {
							DBVARIANT dbv;
							if (!DBGetContactSetting(hcontact,ci.szProto,szstring[loop]=='i'?"Resource":"System",&dbv)) {
								strcpy(szdbsetting,dbv.pszVal);
								DBFreeVariant(&dbv);
								charPtr = szdbsetting;
							} else goto LBL_noData;
						  } else {
							dwsetting=DBGetContactSettingDword(hcontact,ci.szProto,szstring[loop]=='i'?"IP":"RealIP",0);
							if(dwsetting){
								ia.S_un.S_addr=htonl(dwsetting);
								charPtr = inet_ntoa(ia);
							} else goto LBL_noData;
						  }
					goto LBL_charPtr;
				case 'P':if (ci.szProto) charPtr = ci.szProto; else charPtr = wantempty?"":"ProtoUnknown";
					goto LBL_charPtr;
				case 'b':
					charPtr = /*"\n"*/"\x0D\x0A";
					goto LBL_charPtr;
				case 'C': // Get Client Info
					if (isMSN(ci.szProto)) {
						if (hcontact) {
							dwsetting = (int)DBGetContactSettingDword(hcontact,ci.szProto,"FlagBits",0);
							wsprintf(szdbsetting,"MSNC%i",(dwsetting&0x70000000)>>28);
							if (dwsetting & 0x00000001) strcat(szdbsetting," MobD"); //Mobile Device
							if (dwsetting & 0x00000004) strcat(szdbsetting," InkG"); //GIF Ink Send/Receive
							if (dwsetting & 0x00000008) strcat(szdbsetting," InkI"); //ISF Ink Send/Receive
							if (dwsetting & 0x00000010) strcat(szdbsetting," WCam"); //Webcam
							if (dwsetting & 0x00000020) strcat(szdbsetting," MPkt"); //Multi packet messages
							if (dwsetting & 0x00000040) strcat(szdbsetting," SMSr"); //Paging
							if (dwsetting & 0x00000080) strcat(szdbsetting," DSMS"); //Using MSN Direct
							if (dwsetting & 0x00000200) strcat(szdbsetting," WebM"); //WebMessenger
							if (dwsetting & 0x00001000) strcat(szdbsetting," MS7+"); //Unknown (Msgr 7 always[?] sets it)
							if (dwsetting & 0x00004000) strcat(szdbsetting," DirM"); //DirectIM
							if (dwsetting & 0x00008000) strcat(szdbsetting," Wink"); //Send/Receive Winks
							if (dwsetting & 0x00010000) strcat(szdbsetting," MSrc"); //MSN Search ??
							if (dwsetting & 0x00040000) strcat(szdbsetting," VoiC"); //Voice Clips
						} else strcpy(szdbsetting,"Miranda");
					} else {
						DBVARIANT dbv;
						if (!DBGetContactSetting(hcontact,ci.szProto,"MirVer",&dbv)) {
							strcpy(szdbsetting,dbv.pszVal);
							DBFreeVariant(&dbv);
						} else goto LBL_noData;
					}
					charPtr = szdbsetting;
					goto LBL_charPtr;
				case 't':
					charPtr = "\t";
					goto LBL_charPtr;

				default:
					strncpy(szdbsetting,szstring+loop-1,2);
					goto LBL_charPtr;
			}
		}
	}

	return sztemp;
}



void _DBWriteTime(SYSTEMTIME *st,HANDLE hcontact)
{
	DBWriteContactSettingWord((HANDLE)hcontact,S_MOD,"Day",st->wDay);
	DBWriteContactSettingWord((HANDLE)hcontact,S_MOD,"Month",st->wMonth);
	DBWriteContactSettingWord((HANDLE)hcontact,S_MOD,"Year",st->wYear);
	DBWriteContactSettingWord((HANDLE)hcontact,S_MOD,"Hours",st->wHour);
	DBWriteContactSettingWord((HANDLE)hcontact,S_MOD,"Minutes",st->wMinute);
	DBWriteContactSettingWord((HANDLE)hcontact,S_MOD,"Seconds",st->wSecond);
	DBWriteContactSettingWord((HANDLE)hcontact,S_MOD,"WeekDay",st->wDayOfWeek);

}

void DBWriteTimeTS(DWORD t, HANDLE hcontact){
	SYSTEMTIME st;
	FILETIME ft;
	ULONGLONG ll = UInt32x32To64(CallService(MS_DB_TIME_TIMESTAMPTOLOCAL,t,0), 10000000) + NUM100NANOSEC;
	ft.dwLowDateTime = (DWORD)ll;
	ft.dwHighDateTime = (DWORD)(ll >> 32);
	FileTimeToSystemTime(&ft, &st);
	DBWriteContactSettingDword(hcontact,S_MOD,"seenTS",t);
	_DBWriteTime(&st, hcontact);
}
void GetColorsFromDWord(LPCOLORREF First, LPCOLORREF Second, DWORD colDword){
	WORD temp;
	COLORREF res=0;
	temp = (WORD)(colDword>>16);
	res |= ((temp & 0x1F) <<3);
	res |= ((temp & 0x3E0) <<6);
	res |= ((temp & 0x7C00) <<9);
	if (First) *First = res;
	res = 0;
	temp = (WORD)colDword;
	res |= ((temp & 0x1F) <<3);
	res |= ((temp & 0x3E0) <<6);
	res |= ((temp & 0x7C00) <<9);
	if (Second) *Second = res;
}

DWORD StatusColors15bits[] = {
	0x63180000, // 0x00C0C0C0, 0x00000000, Offline - LightGray
	0x7B350000, // 0x00F0C8A8, 0x00000000, Online  - LightBlue
	0x33fe0000, // 0x0070E0E0, 0x00000000, Away -LightOrange
	0x295C0000, // 0x005050E0, 0x00000000, DND  -DarkRed
	0x5EFD0000, // 0x00B8B8E8, 0x00000000, NA   -LightRed
	0x295C0000, // 0x005050E0, 0x00000000, Occupied
	0x43900000, // 0x0080E080, 0x00000000, Free for chat - LightGreen
	0x76AF0000, // 0x00E8A878, 0x00000000, Invisible
	0x431C0000, // 0x0080C0E0, 0x00000000, On the phone
	0x5EFD0000, // 0x00B8B8E8, 0x00000000, Out to lunch
};

DWORD GetDWordFromColors(COLORREF First, COLORREF Second){
	DWORD res = 0;
	res |= (First&0xF8)>>3;
	res |= (First&0xF800)>>6;
	res |= (First&0xF80000)>>9;
	res <<= 16;
	res |= (Second&0xF8)>>3;
	res |= (Second&0xF800)>>6;
	res |= (Second&0xF80000)>>9;
	return res;
}

LRESULT CALLBACK PopupDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {

	switch(message) {
		case WM_COMMAND: 
			if (HIWORD(wParam) == STN_CLICKED){
				HANDLE hContact = PUGetContact(hwnd);
				if (hContact > 0) CallService(MS_MSG_SENDMESSAGE,(WPARAM)hContact,0);
			}
		case WM_CONTEXTMENU:
			PUDeletePopUp(hwnd);
			break;
		case UM_INITPOPUP: return 0;
	}
	return DefWindowProc(hwnd, message, wParam, lParam);
};

void ShowPopup(HANDLE hcontact, const char * lpzProto, int newStatus){
	if(CallService(MS_IGNORE_ISIGNORED,(WPARAM)hcontact,IGNOREEVENT_USERONLINE)) return;
	if (ServiceExists(MS_POPUP_QUERY)) {
		if (DBGetContactSettingByte(NULL,S_MOD,"UsePopups",0)) {
			if (!DBGetContactSettingByte(hcontact,"CList","Hidden",0)) {
				POPUPDATAEX ppd = {0};
				DBVARIANT dbv = {0};
				char szstamp[10];
				DWORD sett;
				sprintf(szstamp, "Col_%d",newStatus-ID_STATUS_OFFLINE);
				sett = DBGetContactSettingDword(NULL,S_MOD,szstamp,StatusColors15bits[newStatus-ID_STATUS_OFFLINE]);
				GetColorsFromDWord(&ppd.colorBack,&ppd.colorText,sett);
				ppd.lchContact = hcontact;
				ppd.lchIcon = LoadSkinnedProtoIcon(lpzProto, newStatus);
				strncpy(ppd.lpzContactName,ParseString(!DBGetContactSetting(NULL,S_MOD,"PopupStamp",&dbv)?dbv.pszVal:DEFAULT_POPUPSTAMP,hcontact,0),MAX_CONTACTNAME);
				DBFreeVariant(&dbv);
				strncpy(ppd.lpzText,ParseString(!DBGetContactSetting(NULL,S_MOD,"PopupStampText",&dbv)?dbv.pszVal:DEFAULT_POPUPSTAMPTEXT,hcontact,0),MAX_SECONDLINE);
				DBFreeVariant(&dbv);
				ppd.PluginWindowProc = (WNDPROC)PopupDlgProc;
				CallService(MS_POPUP_ADDPOPUPEX, (WPARAM)&ppd, 0);
			}
		}
	}
}

void myPlaySound(HANDLE hcontact, WORD newStatus, WORD oldStatus){
	if(CallService(MS_IGNORE_ISIGNORED,(WPARAM)hcontact,IGNOREEVENT_USERONLINE)) return;
	//oldStatus and hcontact are not used yet
	if (DBGetContactSettingByte(NULL,"Skin","UseSound",1)) {
		char * soundname=0;
		if ((newStatus==ID_STATUS_ONLINE) || (newStatus==ID_STATUS_FREECHAT)) soundname = "LastSeenTrackedStatusOnline";
		else if (newStatus==ID_STATUS_OFFLINE) soundname = "LastSeenTrackedStatusOffline";
		else if (oldStatus==ID_STATUS_OFFLINE) soundname = "LastSeenTrackedStatusFromOffline";
		else soundname = "LastSeenTrackedStatusChange";
		if (!DBGetContactSettingByte(NULL,"SkinSoundsOff",soundname,0)) {
			DBVARIANT dbv;
			if (!DBGetContactSetting(NULL,"SkinSounds",soundname,&dbv)) {
				PlaySoundA(dbv.pszVal, NULL, SND_ASYNC | SND_FILENAME | SND_NOWAIT);
				DBFreeVariant(&dbv);
}	}	}	}

//will give hContact position or zero
int isContactQueueActive(HANDLE hContact){
	int i = 0;
	if (!hContact) {
//		MessageBox(0,"Is myself in the queue: never","LastSeen-Mod",0);
		return 0;
	}
	for (i=1;i<contactQueueSize;i++) {
		if (contactQueue[i])
			if (contactQueue[i]->hContact==hContact) return i;
	}
	return 0;
}

//will add hContact to queue and will return position;
int addContactToQueue(HANDLE hContact){
	int i = 0;
	if (!hContact) {
//		MessageBox(0,"Adding myself to queue","LastSeen-Mod",0);
		return 0;
	}
	for (i=1;i<contactQueueSize;i++) {
		if (!contactQueue[i]) {
			contactQueue[i] = (logthread_info *)malloc(sizeof(logthread_info));
			contactQueue[i]->queueIndex = i;
			contactQueue[i]->hContact = hContact;
			return i;
		}
	}
	//no free space. Create some
	//MessageBox(0,"Creating more space","LastSeen-Mod",0);
	contactQueue = (logthread_info **)realloc(contactQueue,(contactQueueSize+16)*sizeof(logthread_info *));
	memset(&contactQueue[contactQueueSize],0, 16*sizeof(logthread_info *));
	i = contactQueueSize;
	contactQueue[i] = (logthread_info *)malloc(sizeof(logthread_info));
	contactQueue[i]->queueIndex = i;
	contactQueue[i]->hContact = hContact;
	contactQueueSize += 16;
	return i;
}

static DWORD __stdcall waitThread(logthread_info* infoParam)
{
	WORD prevStatus = DBGetContactSettingWord(infoParam->hContact,S_MOD,"StatusTriger",ID_STATUS_OFFLINE);
	Sleep(1500); // I hope in 1.5 second all the needed info will be set
	if (includeIdle)
		if (DBGetContactSettingDword(infoParam->hContact,infoParam->sProtoName,"IdleTS",0))
			infoParam->courStatus &=0x7FFF;

	if (infoParam->courStatus != prevStatus){
		DBWriteContactSettingWord(infoParam->hContact,S_MOD,"OldStatus",(WORD)(prevStatus|0x8000));
		if (includeIdle)
			DBWriteContactSettingByte(infoParam->hContact,S_MOD,"OldIdle",(BYTE)((prevStatus&0x8000)==0));

		DBWriteContactSettingWord(infoParam->hContact,S_MOD,"StatusTriger",infoParam->courStatus);
	}

	contactQueue[infoParam->queueIndex] = 0;
	free(infoParam);
	return 0;
}



int UpdateValues(WPARAM wparam,LPARAM lparam)
{
	DBCONTACTWRITESETTING *cws;
	BOOL isIdleEvent;
	// to make this code faster
	if (!wparam) return 0;
	cws=(DBCONTACTWRITESETTING *)lparam;
	//if(CallService(MS_IGNORE_ISIGNORED,(WPARAM)hContact,IGNOREEVENT_USERONLINE)) return 0;
	isIdleEvent = includeIdle?(strcmp(cws->szSetting,"IdleTS")==0):0;
	if (strcmp(cws->szSetting,"Status") && strcmp(cws->szSetting,"StatusTriger") && (isIdleEvent==0)) return 0;
	if (!strcmp(cws->szModule,S_MOD)) {
		//here we will come when Settings/SeenModule/StatusTriger is changed
		WORD prevStatus=DBGetContactSettingWord((HANDLE)wparam, S_MOD, "OldStatus", ID_STATUS_OFFLINE);
		if (includeIdle){
			if (DBGetContactSettingByte((HANDLE)wparam, S_MOD, "OldIdle", 0)) prevStatus &= 0x7FFF;
			else prevStatus |= 0x8000;
		}
		if ((cws->value.wVal|0x8000)<=ID_STATUS_OFFLINE)
		{
			char * proto;
			// avoid repeating the offline status
			if ((prevStatus|0x8000)<=ID_STATUS_OFFLINE)
				return 0;
			proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, wparam, 0);
			DBWriteContactSettingByte((HANDLE)wparam, S_MOD, "Offline", 1);
			{
				DWORD t;
				char *str = (char *)malloc(MAXMODULELABELLENGTH+9);
				mir_snprintf(str,MAXMODULELABELLENGTH+8,"OffTime-%s",proto);
				t = DBGetContactSettingDword(NULL,S_MOD,str,0);
				if (!t) t = time(NULL);
				free(str);
				DBWriteTimeTS(t, (HANDLE)wparam);
			}

			if (!DBGetContactSettingByte(NULL,S_MOD,"IgnoreOffline",1))
			{
				char * sProto;
				if(DBGetContactSettingByte(NULL,S_MOD,"FileOutput",0))
					FileWrite((HANDLE)wparam);

				if (CallProtoService(sProto = 
					(char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, wparam, 0),
					PS_GETSTATUS,0,0
					)>ID_STATUS_OFFLINE)	{
					myPlaySound((HANDLE)wparam, ID_STATUS_OFFLINE, prevStatus);
					if(DBGetContactSettingByte(NULL, S_MOD, "UsePopups", 0)) {
						ShowPopup((HANDLE)wparam, sProto, ID_STATUS_OFFLINE);
				}	}

				if(DBGetContactSettingByte(NULL, S_MOD, "KeepHistory", 0))
					HistoryWrite((HANDLE)wparam);

				if(DBGetContactSettingByte((HANDLE)wparam, S_MOD, "OnlineAlert", 0)) 
					ShowHistory((HANDLE)wparam, 1);
			}

		} else {

			if(cws->value.wVal==prevStatus && !DBGetContactSettingByte((HANDLE)wparam, S_MOD, "Offline", 0)) 
				return 0;

			DBWriteTimeTS(time(NULL), (HANDLE)wparam);

			//DBWriteContactSettingWord(hContact,S_MOD,"StatusTriger",(WORD)cws->value.wVal);

			if(DBGetContactSettingByte(NULL, S_MOD, "FileOutput", 0)) FileWrite((HANDLE)wparam);
			if (prevStatus != cws->value.wVal) myPlaySound((HANDLE)wparam, cws->value.wVal, prevStatus);
			if(DBGetContactSettingByte(NULL, S_MOD, "UsePopups", 0))
				if (prevStatus != cws->value.wVal) ShowPopup((HANDLE)wparam, (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, wparam, 0), cws->value.wVal|0x8000);

			if(DBGetContactSettingByte(NULL, S_MOD, "KeepHistory", 0)) HistoryWrite((HANDLE)wparam);
			if(DBGetContactSettingByte((HANDLE)wparam, S_MOD, "OnlineAlert", 0)) ShowHistory((HANDLE)wparam, 1);
			DBWriteContactSettingByte((HANDLE)wparam, S_MOD, "Offline", 0);
		}
	} else if (IsWatchedProtocol(cws->szModule)) {
		//here we will come when <User>/<module>/Status is changed or it is idle event and if <module> is watched
		if (CallProtoService(cws->szModule,PS_GETSTATUS,0,0)>ID_STATUS_OFFLINE){
			int index;
			if (!(index = isContactQueueActive((HANDLE)wparam))) {
				index = addContactToQueue((HANDLE)wparam);
				strncpy(contactQueue[index]->sProtoName,cws->szModule,MAXMODULELABELLENGTH);
	
				unsigned int dwThreadId;
				mir_forkthreadex((pThreadFuncEx)waitThread, contactQueue[index], &dwThreadId);
			}
			contactQueue[index]->courStatus = isIdleEvent ? DBGetContactSettingWord((HANDLE)wparam, cws->szModule, "Status", ID_STATUS_OFFLINE) : cws->value.wVal;
	}	}	

	return 0;
}

static DWORD __stdcall cleanThread(logthread_info* infoParam)
{
	Sleep(10000); // I hope in 10 secons all logged-in contacts will be listed

	HANDLE hcontact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST,0,0);
	while(hcontact != NULL) {
		char *contactProto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO,(WPARAM)hcontact,0);
		if (contactProto) {
			if ( !strncmp(infoParam->sProtoName, contactProto, MAXMODULELABELLENGTH)) {
				WORD oldStatus = DBGetContactSettingWord(hcontact,S_MOD,"StatusTriger",ID_STATUS_OFFLINE) | 0x8000;
				if (oldStatus > ID_STATUS_OFFLINE) {
					if (DBGetContactSettingWord(hcontact,contactProto,"Status",ID_STATUS_OFFLINE)==ID_STATUS_OFFLINE){
						DBWriteContactSettingWord(hcontact,S_MOD,"OldStatus",(WORD)(oldStatus|0x8000));
						if (includeIdle)DBWriteContactSettingByte(hcontact,S_MOD,"OldIdle",(BYTE)((oldStatus&0x8000)?0:1));
						DBWriteContactSettingWord(hcontact,S_MOD,"StatusTriger",ID_STATUS_OFFLINE);
					}
				}
			}
		}
		hcontact=(HANDLE)CallService(MS_DB_CONTACT_FINDNEXT,(WPARAM)hcontact,0);
	}

	char *str = (char *)malloc(MAXMODULELABELLENGTH+9);
	mir_snprintf(str,MAXMODULELABELLENGTH+8,"OffTime-%s",infoParam->sProtoName);
	DBDeleteContactSetting(NULL,S_MOD,str);
	free(str);

	free(infoParam);
	return 0;
}


int ModeChange(WPARAM wparam,LPARAM lparam)
{
	ACKDATA *ack;
	WORD isetting=0;

	ack=(ACKDATA *)lparam;

	if(ack->type!=ACKTYPE_STATUS || ack->result!=ACKRESULT_SUCCESS || ack->hContact!=NULL) return 0;
	courProtoName = (char *)ack->szModule;
	if (!IsWatchedProtocol(courProtoName) && strncmp(courProtoName,"MetaContacts",12)) 
	{
		//MessageBox(NULL,"Protocol not watched",courProtoName,0);
		return 0;
	}

	DBWriteTimeTS(time(NULL),NULL);

//	isetting=CallProtoService(ack->szModule,PS_GETSTATUS,0,0);
	isetting=(WORD)ack->lParam;
	if (isetting<ID_STATUS_OFFLINE) isetting = ID_STATUS_OFFLINE;
	if ((isetting>ID_STATUS_OFFLINE)&&((WORD)ack->hProcess<=ID_STATUS_OFFLINE)) {
		//we have just loged-in
		db_set_dw(NULL, "UserOnline", ack->szModule, GetTickCount());
		if (IsWatchedProtocol(ack->szModule)) {
			logthread_info *info;
			info = (logthread_info *)malloc(sizeof(logthread_info));
			strncpy(info->sProtoName,courProtoName,MAXMODULELABELLENGTH);
			info->hContact = 0;
			info->courStatus = 0;

			unsigned int dwThreadId;
			CloseHandle( mir_forkthreadex((pThreadFuncEx)cleanThread, info, &dwThreadId));
		}
	} else if ((isetting==ID_STATUS_OFFLINE)&&((WORD)ack->hProcess>ID_STATUS_OFFLINE)) {
		//we have just loged-off
		if (IsWatchedProtocol(ack->szModule)) {
			char *str = (char *)malloc(MAXMODULELABELLENGTH+9);
			time_t t;
			time(&t);
			mir_snprintf(str,MAXMODULELABELLENGTH+8,"OffTime-%s",ack->szModule);
			DBWriteContactSettingDword(NULL,S_MOD,str,t);
			free(str);
	}	}
	if (isetting==DBGetContactSettingWord(NULL,S_MOD,courProtoName,ID_STATUS_OFFLINE)) return 0;
	DBWriteContactSettingWord(NULL,S_MOD,courProtoName,isetting);

	// log "myself"
	if(DBGetContactSettingByte(NULL,S_MOD,"FileOutput",0))
		FileWrite(NULL);

//	if(isetting==ID_STATUS_OFFLINE) //this is removed 'cause I want other contacts to be logged only if the status changed while I was offline
//		SetOffline();

	courProtoName = NULL;

	return 0;
}

short int isDbZero(HANDLE hContact, const char *module_name, const char *setting_name){
	DBVARIANT dbv;
	if (!DBGetContactSetting(hContact, module_name, setting_name, &dbv)) {
		short int res = 0;
		switch (dbv.type) {
			case DBVT_BYTE: res=dbv.bVal==0; break;
			case DBVT_WORD: res=dbv.wVal==0; break;
			case DBVT_DWORD: res=dbv.dVal==0; break;
			case DBVT_BLOB: res=dbv.cpbVal==0; break;
			default: res=dbv.pszVal[0]==0; break;
		}
		DBFreeVariant(&dbv); 
		return res;
	} else return -1;
}

WCHAR *any_to_IdleNotidleUnknown(HANDLE hContact, const char *module_name, const char *setting_name, WCHAR *buff, int bufflen) {
	short int r = isDbZero(hContact, module_name, setting_name);
	if (r==-1){
		wcsncpy(buff, TranslateW(L"Unknown"), bufflen);
	} else {
		wcsncpy(buff, TranslateW(r?L"Not Idle":L"Idle"), bufflen);
	};
	buff[bufflen - 1] = 0;
	return buff;
}
WCHAR *any_to_Idle(HANDLE hContact, const char *module_name, const char *setting_name, WCHAR *buff, int bufflen) {
	if(isDbZero(hContact, module_name, setting_name)==0) { //DB setting is NOT zero and exists
		buff[0] = L'/';
		wcsncpy((WCHAR *)&buff[1], TranslateW(L"Idle"), bufflen-1);
	} else buff[0] = 0;
	buff[bufflen - 1] = 0;
	return buff;
}


/*int GetInfoAck(WPARAM wparam,LPARAM lparam)
{
	ACKDATA *ack;
	DWORD dwsetting=0;

	ack=(ACKDATA *)lparam;

	if(ack->type!=ACKTYPE_GETINFO || ack->hContact==NULL) return 0;
	if (((int)ack->hProcess-1)!=(int)ack->lParam) return 0;
	
	dwsetting=DBGetContactSettingDword(ack->hContact,ack->szModule,"IP",0);
	if(dwsetting)
		DBWriteContactSettingDword(ack->hContact,S_MOD,"IP",dwsetting);

	dwsetting=DBGetContactSettingDword(ack->hContact,ack->szModule,"RealIP",0);
	if(dwsetting)
		DBWriteContactSettingDword(ack->hContact,S_MOD,"RealIP",dwsetting);

	return 0;
}*/



/*void SetOffline(void)
{
	HANDLE hcontact=NULL;
	char * szProto;

	hcontact=(HANDLE)CallService(MS_DB_CONTACT_FINDFIRST,0,0);
	while(hcontact!=NULL)
	{
		szProto=(char *)CallService(MS_PROTO_GETCONTACTBASEPROTO,(WPARAM)hcontact,0);
		if (szProto != NULL && IsWatchedProtocol(szProto)) {	
			DBWriteContactSettingByte(hcontact,S_MOD,"Offline",1);
		}
		hcontact=(HANDLE)CallService(MS_DB_CONTACT_FINDNEXT,(WPARAM)hcontact,0);
	}
}*/