/*
    Variables Plugin for Miranda-IM (www.miranda-im.org)
    Copyright 2003-2006 P. Boon

    This program is mir_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 "variables.h"
#include "parse_alias.h"

static CRITICAL_SECTION csAliasRegister;
static ALIASREGISTER *ar = NULL;
static unsigned int arCount = 0;

static ALIASREGISTER *searchAliasRegister(TCHAR *szAlias) {

	ALIASREGISTER *res;
	unsigned int i;

	res = NULL;
	if ((szAlias == NULL) || (_tcslen(szAlias) == 0)) {
		return NULL;
	}
	EnterCriticalSection(&csAliasRegister);
	for (i=0;i<arCount;i++) {
		if (!_tcscmp(ar[i].szAlias, szAlias)) {
			/* TODO: make a copy here? */
			res = &ar[i];
			LeaveCriticalSection(&csAliasRegister);
			return res;
		}
	}
	LeaveCriticalSection(&csAliasRegister);
	
	return NULL;
}

static TCHAR *replaceArguments(TCHAR *res, TCHAR *tArg, TCHAR *rArg) {

	unsigned int cur, ecur;

	if ( _tcslen(tArg) == 0)
		return res;

	cur = ecur = 0;
	while (*(res+cur) != _T('\0')) {
		if ((*(res+cur) == _T('(')) || (*(res+cur) == _T(','))) {
			ecur = ++cur;
			while ( (*(res+ecur) != _T(')')) && (*(res+ecur) != _T(','))) {
				ecur++;
			}
			if (((signed int)_tcslen(tArg) == (ecur-cur)) && (!_tcsncmp(tArg, res+cur, _tcslen(tArg)))) {
				if ( _tcslen(rArg) > _tcslen(tArg)) {
					res = (TCHAR*)mir_realloc(res, (_tcslen(res) + (_tcslen(rArg)-_tcslen(tArg)) + 1)*sizeof(TCHAR));
					if (res == NULL)
						return NULL;
				}
				MoveMemory(res+ecur+(_tcslen(rArg)-_tcslen(tArg)), res+ecur, (_tcslen(res+ecur)+1)*sizeof(TCHAR));
				_tcsncpy(res+cur, rArg, _tcslen(rArg));
			}
		}
		cur++;
	}

	return res;
}

static TCHAR *parseTranslateAlias(ARGUMENTSINFO *ai) {

	unsigned int i;
	TCHAR *res;
	ALIASREGISTER *areg;
	
	areg = searchAliasRegister(ai->targv[0]);
	if ((areg == NULL) || (areg->argc != ai->argc-1)) {
		return NULL;
	}
	res = mir_tstrdup(areg->szTranslation);
	for (i=0;i<areg->argc;i++) {
		res = replaceArguments(res, areg->argv[i], ai->targv[i+1]);
		if (res == NULL) {
			return NULL;
		}
	}

	return res;
}

static int addToAliasRegister(TCHAR *szAlias, unsigned int argc, TCHAR** argv, TCHAR *szTranslation) {

	unsigned int i, j;

	if (szAlias == NULL || szTranslation == NULL || _tcslen(szAlias) == 0 )
		return -1;

	EnterCriticalSection(&csAliasRegister);
	for (i=0;i<arCount;i++) {
		if (!_tcscmp(ar[i].szAlias, szAlias)) {
			mir_free(ar[i].szTranslation);
			ar[i].szTranslation = mir_tstrdup(szTranslation);
			for (j=0;j<ar[i].argc;j++) {
				if (ar[i].argv[j] != NULL) {
					mir_free(ar[i].argv[j]);
				}
			}
			ar[i].argc = argc;
			ar[i].argv = ( TCHAR** )mir_realloc(ar[i].argv, argc * sizeof(TCHAR*));
			if (ar[i].argv == NULL) {
				LeaveCriticalSection(&csAliasRegister);
				return -1;
			}
			for (j=0;j<argc;j++) {
				if (argv[j] != NULL)
					ar[i].argv[j] = mir_tstrdup(argv[j]);
				else
					ar[i].argv[j] = NULL;
			}
			LeaveCriticalSection(&csAliasRegister);
			return 0;
		}
	}
	ar = ( ALIASREGISTER* )mir_realloc(ar, (arCount+1)*sizeof(ALIASREGISTER));
	if (ar == NULL) {
		LeaveCriticalSection(&csAliasRegister);
		return -1;
	}
	ar[arCount].szAlias = mir_tstrdup(szAlias);
	ar[arCount].szTranslation = mir_tstrdup(szTranslation);
	ar[arCount].argc = argc;
	ar[arCount].argv = ( TCHAR** )mir_alloc(argc * sizeof(TCHAR*));
	if (ar[arCount].argv == NULL) {
		LeaveCriticalSection(&csAliasRegister);
		return -1;
	}
	for (j=0;j<ar[arCount].argc;j++) {
		if (argv[j] != NULL)
			ar[arCount].argv[j] = mir_tstrdup(argv[j]);
		else
			ar[arCount].argv[j] = NULL;
	}
	arCount += 1;
	LeaveCriticalSection(&csAliasRegister);

	return 0;
}

static TCHAR *parseAddAlias(ARGUMENTSINFO *ai) {

	int res;
	int argc, i;
	TCHAR *cur, *alias, **argv, *szArgs;
	char *szHelp, *szArgsA;

	if (ai->argc != 3)
		return NULL;

	cur = ai->targv[1];
	while (isValidTokenChar(*cur))
		cur++;

	alias = (TCHAR*)mir_calloc(((cur-ai->targv[1])+1)*sizeof(TCHAR));
	if (alias == NULL)
		return NULL;

	_tcsncpy(alias, ai->targv[1], (cur-ai->targv[1]));
	getArguments(cur, &argv, &argc);
	deRegisterToken(alias);
	addToAliasRegister(alias, argc, argv, ai->targv[2]);
	szArgs = NULL;
	for (i=0;i<argc;i++) {
		if (i == 0)
			szArgs = (TCHAR*)mir_calloc(( _tcslen(argv[i])+2)*sizeof(TCHAR));
		else
			szArgs = (TCHAR*)mir_realloc(szArgs, (_tcslen(szArgs) + _tcslen(argv[i]) + 2)*sizeof(TCHAR));

		_tcscat(szArgs, argv[i]);
		if (i != argc-1)
			_tcscat(szArgs, _T(","));
	}
	if ((szArgs != NULL) && (argc > 0)) {

		szArgsA = mir_t2a(szArgs);

		szHelp = ( char* )mir_alloc(32 + strlen(szArgsA));
		memset(szHelp, '\0', 32 + strlen(szArgsA));
		sprintf(szHelp, "Alias\t(%s)\tuser defined", szArgsA);
		res = registerIntToken(alias, parseTranslateAlias, TRF_FUNCTION|TRF_UNPARSEDARGS, szHelp);
		mir_free(szArgsA);
	}
	else {
		szHelp = ( char* )mir_alloc(32);
		memset(szHelp, '\0', 32);
		sprintf(szHelp, "Alias\t\tuser defined");
		res = registerIntToken(alias, parseTranslateAlias, TRF_FIELD|TRF_UNPARSEDARGS, szHelp);
	}
	mir_free(szArgs);
	mir_free(szHelp);
	
	return res==0?mir_tstrdup(_T("")):NULL;
}

int registerAliasTokens()
{
	registerIntToken(_T(ADDALIAS), parseAddAlias, TRF_FUNCTION|TRF_UNPARSEDARGS, "Variables\t(x,y)\tstores y as alias named x");//TRF_UNPARSEDARGS);
	InitializeCriticalSection(&csAliasRegister);
	return 0;
}

void unregisterAliasTokens()
{
	DeleteCriticalSection(&csAliasRegister);
}