/*
CmdLine plugin for Miranda IM

Copyright � 2007 Cristian Libotean

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"

LISTCOMMANDS ListCommands = NULL;

PCommand knownCommands = NULL;
int cKnownCommands = 0;

HMODULE hCmdLineDLL = NULL;

char *GetMirandaFolder(char *mimFolder, int size)
{
	strncpy_s(mimFolder, size, sdCmdLine->mimFolder, _TRUNCATE);
	return mimFolder;
}

int ConnectToMiranda()
{
	wchar_t tszPath[MAX_PATH];
	GetModuleFileName(NULL, tszPath, _countof(tszPath));
	wchar_t *p = wcsrchr(tszPath, '\\');
	if (p) p[1] = 0;

	wcsncat_s(tszPath, L"libs", _TRUNCATE);
	DWORD cbPath = (DWORD)wcslen(tszPath);

	DWORD cbSize = GetEnvironmentVariable(L"PATH", NULL, 0);
	wchar_t *ptszVal = new wchar_t[cbSize + MAX_PATH + 2];
	wcscpy(ptszVal, tszPath);
	wcscat(ptszVal, L";");
	GetEnvironmentVariable(L"PATH", ptszVal + cbPath + 1, cbSize);
	SetEnvironmentVariable(L"PATH", ptszVal);
	delete[] ptszVal;

	char pluginPath[1024];
	GetMirandaFolder(pluginPath, sizeof(pluginPath));
	mir_strcat(pluginPath, "\\plugins\\cmdline.dll");

	ListCommands = NULL;

	hCmdLineDLL = LoadLibraryA(pluginPath);

	int failure = 1;
	if (hCmdLineDLL)
		ListCommands = (LISTCOMMANDS) GetProcAddress(hCmdLineDLL, "ListCommands");

	if (ListCommands)
		failure = 0;

	return failure;
}

int DisconnectFromMiranda()
{
	return FreeLibrary(hCmdLineDLL);
}

int GetKnownCommands()
{
	ListCommands(&knownCommands, &cKnownCommands);
	return (knownCommands == NULL);
}

int DestroyKnownCommands()
{
	return 0;
}

PCommand GetCommand(char *command)
{
	int i;
	char lower[512];
	strncpy_s(lower, command, _TRUNCATE);
	_strlwr(lower);

	for (i = 0; i < cKnownCommands; i++)
		if (mir_strcmp(knownCommands[i].command, lower) == 0)
			return &knownCommands[i];

	//allow more parameters to trigger the help command - /h -h /? --help
	if ((mir_strcmp(lower, "/h") == 0) || (mir_strcmp(lower, "-h") == 0) || (mir_strcmp(lower, "/?") == 0) || (mir_strcmp(lower, "--help") == 0))
		for (i = 0; i < cKnownCommands; i++)
			if (knownCommands[i].ID == MIMCMD_HELP)
				return &knownCommands[i];

	return NULL;
}

void HandleHelpCommand(PCommand, char *argv[], int argc, PReply reply)
{
	CMStringA szReply;

	if (argc >= 3) {
		PCommand command = GetCommand(argv[2]);

		if (command) {
			reply->code = MIMRES_SUCCESS;
			szReply.Append(Translate(command->help));
		}
		else {
			reply->code = MIMRES_NOTFOUND;
			szReply.AppendFormat(Translate("No help for '%s'."), argv[2]);
		}
	}
	else {
		reply->code = MIMRES_SUCCESS;
		szReply.Append(Translate("Available commands: "));

		for (int i = 0; i < cKnownCommands - 1; i++) {
			szReply.Append(knownCommands[i].command);
			szReply.Append(", ");
		}
		szReply.Append(knownCommands[cKnownCommands-1].command);
		szReply.AppendChar('.');
	}
	strncpy_s(reply->message, szReply, _TRUNCATE);
}

PReply ParseCommand(char *argv[], int argc)
{
	PCommand command = GetCommand(argv[1]);
	if (!command)
		return NULL;

	PReply reply = &sdCmdLine->reply;
	if (command->ID == MIMCMD_HELP)
		HandleHelpCommand(command, argv, argc, reply);
	else
		ProcessConsoleCommand(command, argv, argc, reply);

	return reply;
}

void FillSharedDataStruct(PCommand command, char *arguments[], int count)
{
	for (int i = 0; i < count; i++)
		strncpy_s(sdCmdLine->arguments[i], ARGUMENT_SIZE, arguments[i], _TRUNCATE);

	sdCmdLine->cArguments = count;
	sdCmdLine->command = *command;
	*sdCmdLine->reply.message = 0;
	sdCmdLine->reply.code =-1;
}

void ProcessConsoleCommand(PCommand command, char *arguments[], int count, PReply reply)
{
	const HANDLE events[] = { heServerDone, heServerClose, heServerBufferFull };
	const int cEvents = sizeof(events) / sizeof(events[0]);

	if (WaitForSingleObject(hmClient, INFINITE) == WAIT_OBJECT_0) {//got the mutex, we're the only one who can talk to miranda now
		FillSharedDataStruct(command, arguments, count);
		SetEvent(heServerExec); //tell Miranda to process the request

		int done = FALSE;
		while (!done) {
			// wait until server either finished processing or miranda was closed
			switch (WaitForMultipleObjects(cEvents, events, FALSE, INFINITE)) {
			case WAIT_OBJECT_0: //done event
				done = TRUE;
				break; //nothing to do

			case WAIT_OBJECT_0 + 1: //close event
			default:
				mir_strcpy(sdCmdLine->reply.message, Translate("Miranda has been closed or an error has occurred while waiting for the result, could not process request."));
				done = TRUE;
				break;

			case WAIT_OBJECT_0 + 2: //buffer full event
				lpprintf("%s", reply->message);
				break;
			}
		}

		reply->code = sdCmdLine->reply.code;
		strncpy_s(reply->message, sdCmdLine->reply.message, _TRUNCATE);

		ReleaseMutex(hmClient); //let other possible clients talk to the server
	}
	else {
		reply->code = -1;
		*reply->message = 0;
	}
}