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

	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 "stdafx.h"

struct ALIASREGISTER
{
	wchar_t *szAlias;
	unsigned int argc;
	wchar_t **argv;
	wchar_t *szTranslation;
};

static LIST<ALIASREGISTER> arAliases(5);
static mir_cs csAliasRegister;

static ALIASREGISTER* searchAliasRegister(wchar_t *szAlias)
{
	if (szAlias == nullptr || *szAlias == 0)
		return nullptr;

	mir_cslock lck(csAliasRegister);
	for (auto &it : arAliases)
		if (!mir_wstrcmp(it->szAlias, szAlias))
			return it;

	return nullptr;
}

static wchar_t* replaceArguments(wchar_t *res, wchar_t *tArg, wchar_t *rArg)
{
	if (mir_wstrlen(tArg) == 0)
		return res;

	unsigned int cur = 0, ecur = 0;
	while (*(res + cur) != 0) {
		if ((*(res + cur) == '(') || (*(res + cur) == ',')) {
			ecur = ++cur;
			while ((*(res + ecur) != ')') && (*(res + ecur) != ','))
				ecur++;

			if (((signed int)mir_wstrlen(tArg) == (ecur - cur)) && (!wcsncmp(tArg, res + cur, mir_wstrlen(tArg)))) {
				if (mir_wstrlen(rArg) > mir_wstrlen(tArg)) {
					res = (wchar_t*)mir_realloc(res, (mir_wstrlen(res) + (mir_wstrlen(rArg) - mir_wstrlen(tArg)) + 1) * sizeof(wchar_t));
					if (res == nullptr)
						return nullptr;
				}
				memmove(res + ecur + (mir_wstrlen(rArg) - mir_wstrlen(tArg)), res + ecur, (mir_wstrlen(res + ecur) + 1) * sizeof(wchar_t));
				wcsncpy(res + cur, rArg, mir_wstrlen(rArg));
			}
		}
		cur++;
	}

	return res;
}

static wchar_t* parseTranslateAlias(ARGUMENTSINFO *ai)
{
	ALIASREGISTER *areg = searchAliasRegister(ai->argv.w[0]);
	if (areg == nullptr || areg->argc != ai->argc - 1)
		return nullptr;

	wchar_t *res = mir_wstrdup(areg->szTranslation);
	for (unsigned i = 0; i < areg->argc; i++) {
		res = replaceArguments(res, areg->argv[i], ai->argv.w[i + 1]);
		if (res == nullptr)
			return nullptr;
	}

	return res;
}

static int addToAliasRegister(wchar_t *szAlias, unsigned int argc, wchar_t **argv, wchar_t *szTranslation)
{
	if (szAlias == nullptr || szTranslation == nullptr || mir_wstrlen(szAlias) == 0)
		return -1;

	mir_cslock lck(csAliasRegister);
	for (auto &p : arAliases) {
		if (mir_wstrcmp(p->szAlias, szAlias))
			continue;

		mir_free(p->szTranslation);
		p->szTranslation = mir_wstrdup(szTranslation);
		for (unsigned j = 0; j < p->argc; j++)
			mir_free(p->argv[j]);

		p->argc = argc;
		p->argv = (wchar_t**)mir_realloc(p->argv, argc * sizeof(wchar_t*));
		if (p->argv == nullptr)
			return -1;

		for (unsigned j = 0; j < argc; j++) {
			if (argv[j] != nullptr)
				p->argv[j] = mir_wstrdup(argv[j]);
			else
				p->argv[j] = nullptr;
		}
		return 0;
	}
	wchar_t **pargv = (wchar_t**)mir_alloc(argc * sizeof(wchar_t*));
	if (pargv == nullptr)
		return -1;

	ALIASREGISTER *p = new ALIASREGISTER;
	p->szAlias = mir_wstrdup(szAlias);
	p->szTranslation = mir_wstrdup(szTranslation);
	p->argc = argc;
	p->argv = pargv;

	for (unsigned j = 0; j < p->argc; j++) {
		if (argv[j] != nullptr)
			p->argv[j] = mir_wstrdup(argv[j]);
		else
			p->argv[j] = nullptr;
	}
	arAliases.insert(p);
	return 0;
}

static wchar_t* parseAddAlias(ARGUMENTSINFO *ai)
{
	if (ai->argc != 3)
		return nullptr;

	char *szHelp, *szArgsA;
	wchar_t *cur = ai->argv.w[1];
	while (isValidTokenChar(*cur))
		cur++;

	ptrW alias(mir_wstrndup(ai->argv.w[1], cur - ai->argv.w[1]));

	TArgList argv;
	getArguments(cur, argv);

	deRegisterToken(alias);
	addToAliasRegister(alias, argv.getCount(), argv.getArray(), ai->argv.w[2]);
	wchar_t *szArgs = nullptr;
	for (int i = 0; i < argv.getCount(); i++) {
		if (i == 0)
			szArgs = (wchar_t*)mir_calloc((mir_wstrlen(argv[i]) + 2) * sizeof(wchar_t));
		else
			szArgs = (wchar_t*)mir_realloc(szArgs, (mir_wstrlen(szArgs) + mir_wstrlen(argv[i]) + 2) * sizeof(wchar_t));

		mir_wstrcat(szArgs, argv[i]);
		if (i != argv.getCount() - 1)
			mir_wstrcat(szArgs, L",");
	}
	int res;
	if (szArgs != nullptr && argv.getCount() > 0) {
		szArgsA = mir_u2a(szArgs);

		size_t size = 32 + mir_strlen(szArgsA);
		szHelp = (char *)mir_alloc(size);
		memset(szHelp, '\0', 32 + mir_strlen(szArgsA));
		mir_snprintf(szHelp, size, LPGEN("Alias") "\t(%s)\t" LPGEN("user defined"), szArgsA);
		res = registerIntToken(alias, parseTranslateAlias, TRF_FUNCTION | TRF_UNPARSEDARGS, szHelp);
		mir_free(szArgsA);
	}
	else {
		szHelp = (char*)mir_alloc(32);
		memset(szHelp, '\0', 32);
		mir_snprintf(szHelp, 32, LPGEN("Alias") "\t\t" LPGEN("user defined"));
		res = registerIntToken(alias, parseTranslateAlias, TRF_FIELD | TRF_UNPARSEDARGS, szHelp);
	}
	mir_free(szArgs);
	mir_free(szHelp);
	argv.destroy();
	return (res == 0) ? mir_wstrdup(L"") : nullptr;
}

void registerAliasTokens()
{
	registerIntToken(ADDALIAS, parseAddAlias, TRF_FUNCTION | TRF_UNPARSEDARGS, LPGEN("Variables") "\t(x,y)\t" LPGEN("stores y as alias named x"));//TRF_UNPARSEDARGS);
}

void unregisterAliasTokens()
{
	for (auto &p : arAliases) {
		for (unsigned j = 0; j < p->argc; j++)
			mir_free(p->argv[j]);
		mir_free(p->argv);
		mir_free(p->szAlias);
		mir_free(p->szTranslation);
		delete p;
	}
}