/*
yaRelay.cpp

Yet Another Relay plugin. v.0.0.0.3
This plugin forwards all incoming messages to any contact.

Features:
 - Forwards all messages from any specified contact (or from all contacts)
 - Works only if your status is equals to specified (of set of statuses)
 - Could be specified any template for sent messages
 - Original message could be split up (by size)
 - Could be specified number of split parts to send
 - Incoming message could be marked as 'read' (optional)
 - Outgoing messages could be saved in history (optional)

(c)2005 Anar Ibragimoff (ai91@mail.ru)

*/

#include "stdafx.h"

#include "../../utils/mir_buffer.h"

CLIST_INTERFACE *pcli;
HINSTANCE hInst;
int hLangpack;

MCONTACT hForwardFrom, hForwardTo;
wchar_t tszForwardTemplate[MAXTEMPLATESIZE]; 
int iSplit, iSplitMaxSize, iSendParts, iMarkRead, iSendAndHistory, iForwardOnStatus;

LIST<MESSAGE_PROC> arMessageProcs(10, HandleKeySortT);

PLUGININFOEX pluginInfoEx = {
	sizeof(PLUGININFOEX),
	__PLUGIN_NAME,
	PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
	__DESCRIPTION,
	__AUTHOR,
	__AUTHOREMAIL,
	__COPYRIGHT,
	__AUTHORWEB,
	UNICODE_AWARE,
	// {01202E6A-C1B3-42E5-838A-3E497B31F38E}
	{0x1202e6a, 0xc1b3, 0x42e5, {0x83, 0x8a, 0x3e, 0x49, 0x7b, 0x31, 0xf3, 0x8e}}
};

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

/**
* Protocols acknowledgement
*/
int ProtoAck(WPARAM,LPARAM lparam)
{
	ACKDATA *pAck = (ACKDATA *)lparam;
	if (pAck->type != ACKTYPE_MESSAGE || pAck->result != ACKRESULT_SUCCESS)
		return 0;

	MESSAGE_PROC* p = arMessageProcs.find((MESSAGE_PROC*)&pAck->hProcess);
	if (p == nullptr)
		return 0;

	if (iSendAndHistory > 0){
		time_t ltime;
		time(&ltime);

		DBEVENTINFO dbei = {};
		dbei.szModule = "yaRelay";
		dbei.timestamp = ltime;
		dbei.flags = DBEF_SENT | DBEF_UTF;
		dbei.eventType = EVENTTYPE_MESSAGE;
		dbei.cbBlob = (DWORD)mir_strlen(p->msgText) + 1;
		dbei.pBlob = (PBYTE)p->msgText;
		db_event_add(hForwardTo, &dbei);
	}

	mir_free(p->msgText);
	arMessageProcs.remove(p);
	mir_free(p);
	return 0;
}

/**
* New event was added into DB.
*/
static int MessageEventAdded(WPARAM hContact, LPARAM hDBEvent)
{
	// is the message sender accepted for forwarding
	if (hForwardFrom != 0 && hForwardFrom != hContact)
		return 0;

	// is receiver specified
	if (hForwardTo == 0)
		return 0;

	// don't reply to receiver
	if (hForwardTo == hContact)
		return 0;

	// is current status acceptable
	int statMask;
	switch(CallService(MS_CLIST_GETSTATUSMODE, 0, 0)){
		case ID_STATUS_OFFLINE:  statMask = STATUS_OFFLINE  ;break;
		case ID_STATUS_ONLINE:   statMask = STATUS_ONLINE   ;break;
		case ID_STATUS_AWAY:     statMask = STATUS_AWAY     ;break;
		case ID_STATUS_NA:       statMask = STATUS_NA       ;break;
		case ID_STATUS_OCCUPIED: statMask = STATUS_OCCUPIED ;break;   
		case ID_STATUS_DND:      statMask = STATUS_DND      ;break;
		case ID_STATUS_FREECHAT: statMask = STATUS_FREECHAT ;break;
		case ID_STATUS_INVISIBLE:statMask = STATUS_INVISIBLE;break;
		default: return 0;
	}
	if ((iForwardOnStatus & statMask) == 0)
		return 0;

	// receive message from DB
	DBEVENTINFO dbei = {};
	dbei.cbBlob = db_event_getBlobSize(hDBEvent);
	if (dbei.cbBlob == -1)
		return 0;

	dbei.pBlob = (unsigned char*)alloca(dbei.cbBlob);
	db_event_get(hDBEvent, &dbei);
	if (dbei.flags & DBEF_SENT || dbei.flags & DBEF_READ || (dbei.eventType != EVENTTYPE_MESSAGE))
		return 0;

	// get time and date
	time_t tTime = dbei.timestamp;
	tm *tm_time = gmtime(&tTime);

	// build a message
	Buffer<char> szUtfMsg;
	T2Utf szTemplate(tszForwardTemplate);
	for (char *p = szTemplate; *p; p++) {
		if (*p != '%') {
			szUtfMsg.append(*p);
			continue;
		}

		wchar_t buf[100];
		switch(*++p) {
		case 'u':
		case 'U':
			szUtfMsg.append(T2Utf(pcli->pfnGetContactDisplayName(hContact, 0)));
			break;

		case 'i':
		case 'I':
			{
				ptrW id(Contact_GetInfo(CNF_UNIQUEID, NULL));
				if (id != NULL)
					wcsncpy_s(buf, id, _TRUNCATE);
				else
					mir_snwprintf(buf, L"%p", hContact);
			}
			szUtfMsg.append(T2Utf(buf));
			break;

		case 't':
		case 'T':
			wcsftime(buf, 10, L"%H:%M", tm_time);
			szUtfMsg.append(T2Utf(buf));
			break;

		case 'd':
		case 'D':
			wcsftime(buf, 12, L"%d/%m/%Y", tm_time);
			szUtfMsg.append(T2Utf(buf));
			break;

		case 'm':
		case 'M':
			if (dbei.flags & DBEF_UTF)
				szUtfMsg.append((char*)dbei.pBlob, dbei.cbBlob);
			else
				szUtfMsg.append(ptrA(mir_utf8encode((char*)dbei.pBlob)));
			break;

		case '%':
			szUtfMsg.append('%');
			break;
		}
	}

	int iPartCount = 1;
	size_t cbMsgSize = szUtfMsg.len, cbPortion = cbMsgSize;
	if (iSplit > 0) {
		iPartCount = min(iSendParts, int((szUtfMsg.len + iSplitMaxSize) / iSplitMaxSize));
		cbPortion = iSplitMaxSize;
	}

	char *szBuf = szUtfMsg.str;
	for (int i=0; i < iPartCount; i++, szBuf += cbPortion) {
		char *szMsgPart = (char*)mir_alloc(cbPortion+1);
		strncpy(szMsgPart, szBuf, cbPortion);
		szMsgPart[cbPortion] = 0;

		HANDLE hMsgProc = (HANDLE)ProtoChainSend(hForwardTo, PSS_MESSAGE, 0, (LPARAM)szMsgPart);

		MESSAGE_PROC* msgProc = (MESSAGE_PROC*)mir_alloc(sizeof(MESSAGE_PROC));
		msgProc->hProcess = hMsgProc;
		msgProc->msgText = szMsgPart;
		msgProc->retryCount = 0;
		arMessageProcs.insert(msgProc);
	}

	// mark message as 'read'
	if (iMarkRead > 0)
		db_event_markRead(hContact, hDBEvent);
	return 0;
}

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

extern "C" int __declspec(dllexport) Load()
{
	mir_getLP(&pluginInfoEx);
	pcli = Clist_GetInterface();

	// Load plugin options from DB
	hForwardFrom = (MCONTACT)db_get_dw(NULL, "yaRelay", "ForwardFrom", 0);
	hForwardTo = (MCONTACT)db_get_dw(NULL, "yaRelay", "ForwardTo", 0);

	iForwardOnStatus = db_get_dw(NULL, "yaRelay", "ForwardOnStatus", STATUS_OFFLINE | STATUS_AWAY | STATUS_NA);

	wchar_t *szForwardTemplate = db_get_wsa(NULL, "yaRelay", "ForwardTemplate");
	if (szForwardTemplate){
		wcsncpy(tszForwardTemplate, szForwardTemplate, _countof(tszForwardTemplate));
		mir_free(szForwardTemplate);
	}
	else wcsncpy(tszForwardTemplate, L"%u: %m", MAXTEMPLATESIZE-1);

	iSplit          = db_get_dw(NULL, "yaRelay", "Split", 0);
	iSplitMaxSize   = db_get_dw(NULL, "yaRelay", "SplitMaxSize", 100);
	iSendParts      = db_get_dw(NULL, "yaRelay", "SendParts", 0);
	iMarkRead       = db_get_dw(NULL, "yaRelay", "MarkRead", 0);
	iSendAndHistory = db_get_dw(NULL, "yaRelay", "SendAndHistory", 1);

	// hook events
	HookEvent(ME_DB_EVENT_ADDED, MessageEventAdded);
	HookEvent(ME_OPT_INITIALISE, OptionsInit);
	HookEvent(ME_PROTO_ACK, ProtoAck);
	return 0;
}

extern "C" int __declspec(dllexport) Unload(void)
{
	return 0;
}