/*
 * client.c
 * serelc main() program and client-side logic.
 *
 * Copyright (C) Leni Mayo 2001-2002
 *
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License, version 2, as
 *	published by the Free Software Foundation.
 *	
 *	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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
#include <stdlib.h>
#include <string.h> /* for strerror() */
#include <errno.h>
#include <getopt.h>
#include "config.h"  /* generated by autoconf configure */
#include "command.h"
#include "common.h"
#include "clienthandle.h"
#include "log.h"

Handle eventHandle;  /* global event handle */
static const char *argv0 = NULL;

static void printUsage(FILE *fp, const char *argv0);
static Command * commandFromArgv(CommandType ct, int argc, int start_index, char *argv[]);
static void parseCommandLine(int argc, char *argv[], CommandType *pCt,
                   Command **ppCommand, int *pDebugLevel, const char **ppszLogName, LogOpenMode *pLogMode);

int main (int argc, char *argv[])
{
	Command *pCommand = NULL;
	int debuglevel = 0;
	const char *pszLogName = NULL;
	LogOpenMode logMode = eLogOpenModeAppend;
	CommandType ct;
	int ret;

	argv0 = getArgv0(argv, "serelc");

	commandInitialiseTables();

	parseEnvironment(&debuglevel, &pszLogName);

	parseCommandLine(argc, argv, &ct, &pCommand, &debuglevel, &pszLogName, &logMode);

	if (ct == eCommandTypeQuery)
	{
		CommandQuery *pC = (CommandQuery *) pCommand;
		int fd;

		fd = createAndOpenTmpFile(pC->fileChannel, sizeof(pC->fileChannel));

		if (fd == -1)
		{
			fprintf(stderr, "Unable to create file: \"%s\" - %s\n", pC->fileChannel, strerror(errno));

			return 1;
		}
		else
		{
			close(fd);
		}
	}

#ifdef TAKENOUT
	 printf("After parsing the command line:\nCommand = %s\ndebuglevel = %d, pszLogName = %s\n",
			commandAsString(pCommand), debuglevel, pszLogName ? pszLogName : "NULL");
#endif

	logInitialise(argv0, eLogContextTerminal, debuglevel, pszLogName, logMode);

	{
		/* create an initial entry in the log for easier tracability */
		int i;
	
		printLog(eLogLevelClient, "%d: ", getpid());

		for (i = 0; i < argc && argv[i] != NULL && argv[i][0] != '\0'; i++)
		{
			printLog(eLogLevelClient, "%s ", argv[i]);
		}

		printLog(eLogLevelClient, "\n");
	}

	ret = handleInitialise(&eventHandle);

	if (ret == 0)
	{
		HandleEventDefaultUserData ud;
		Message msg;

		ud.pCommand = pCommand;

		messageInitialise(&msg);
		messagePush(&msg, pCommand);

		ret = handleInitiate(&eventHandle, &msg, onEventDefault, &ud);

		if (ret == 0)
		{
			handleEventLoop(&eventHandle);
			ret = ud.retval;
		}
		else
		{
			printLog(eLogLevelError, "%d: handleInitiate() failed to send: %s\n", getpid(), commandAsString(pCommand));
		}

		handleCleanup(&eventHandle);

		free(pCommand);
		pCommand = NULL;
	}

	if (pCommand != NULL)
		free(pCommand); /* just to be tidy */

	printLog(eLogLevelClient, "%d: exit status: %d\n", getpid(), ret);

	return ret;
}

Command * commandFromArgv(CommandType ct, int argc, int start_index, char *argv[])
{
	Command *pReturn = NULL;
	pid_t pid, pidReply;

	pidReply = getpid();

	if ((ct == eCommandTypeRegisterNeed) ||
	    (ct == eCommandTypeRegisterProvide) ||
	    (ct == eCommandTypeDeregister))
	{
		CommandRegister *p;
		/* parse for: service_name
		*/
		
		if (argv[start_index] != NULL)
		{
			p = malloc(sizeof(CommandRegister));

			commandInitialise(&(p->cmd), ct, pidReply);
			p->pid = getppid();
			snprintf(p->name, sizeof(p->name), "%s", argv[start_index]);

			pReturn = (Command *) p;
		}
	}
	else if (ct == eCommandTypeStatusChange)
	{
		CommandStatusChange *p;
		int status;
		/* parse for: statuschange pid exitstatus
		*/
		if (argv[start_index] != NULL && argv[start_index + 1] != NULL)
		{
			pid = atoi(argv[start_index]);
			status = atoi(argv[start_index + 1]);
			
			if ((pid > 1))
			{
				p = malloc(sizeof(CommandStatusChange));

				commandInitialise(&(p->cmd), ct, pidReply);
				p->pid = pid;
				p->status = status == 0 ? ePidStatusSuccess : ePidStatusFailure;

				pReturn = (Command *) p;
			}
		}
	}
	else if (ct == eCommandTypeServerControl)
	{
		CommandServerControl *p;
		ServerControlCode code;

		/* parse for: servercontrol stop
		*/
		if (argv[start_index] != NULL && strcmp(argv[start_index], "stop") == 0)
		{
			code = eServerControlCodeStop;

			p = malloc(sizeof(CommandStatusChange));

			commandInitialise(&(p->cmd), ct, pidReply);
			p->code = code;

			pReturn = (Command *) p;
		}
	}
	else if (ct == eCommandTypeQuery)
	{
		CommandQuery *p;
		QueryCode    code;

		/* parse for: state name
		*/
		if (argv[start_index] != NULL && strcmp(argv[start_index], "state") == 0
		                              && argv[start_index + 1] != NULL)
		{
			code = eQueryCodeStatus;

			p = malloc(sizeof(CommandQuery));

			commandInitialise(&(p->cmd), ct, pidReply);
			p->code = code;
			strcpy(p->name, argv[start_index + 1]);

			pReturn = (Command *) p;
		}
		else if (argv[start_index] != NULL && strcmp(argv[start_index], "graph") == 0)
		{
			int i;
			const char *pszFileNameAdd = "";
			const char *pszGraphId = "";

			code = eQueryCodeGraph;

			p = malloc(sizeof(CommandQuery));

			commandInitialise(&(p->cmd), ct, pidReply);
			p->code = code;

			for (i = start_index + 1; i < argc; i+= 2)
			{
				if (argv[i] && strcmp(argv[i], "add") == 0)
				{
					if (argv[i + 1])
					{
						pszFileNameAdd = argv[i + 1];
					}
					else
					{
						printUsage(stderr, argv0);
					}
				}
				else if (argv[i] && strcmp(argv[i], "id") == 0)
				{
					if (argv[i + 1])
					{
						pszGraphId = argv[i + 1];
					}
					else
					{
						printUsage(stderr, argv0);
					}
				}
			}

			strcpy(p->fileAdd, pszFileNameAdd);
			strcpy(p->name, pszGraphId);
			
			pReturn = (Command *) p;
		}
	}

	return pReturn;
}

void parseCommandLine(int argc, char *argv[], CommandType *pct,
          Command **ppCommand, int *pDebugLevel, const char **ppszLogName,
		  LogOpenMode *pLogMode)
{
	int c;
	int option_index = 0;
	static struct option long_options[] =
	{
		{"help",         no_argument,       NULL, 'h'},
		{"version",      no_argument,       NULL, 'v'},
		{"debuglevel",   required_argument, NULL, 'd'},
		{"log",          required_argument, NULL, 'l'},
		{"log-append",   no_argument,       NULL, 'a'},
		{"log-truncate", no_argument,       NULL, 't'},
		{"provide",      no_argument,       NULL,  0 },
		{"unprovide",    no_argument,       NULL,  0 },
		{"need",         no_argument,       NULL,  0 },
		{"statuschange", no_argument,       NULL,  0 },
		{"control",      no_argument,       NULL,  0 },
		{"query",        no_argument,       NULL,  0 },
		{0, 0, 0, 0}
	};

	assert(pct && ppCommand && pDebugLevel && ppszLogName);

	*pct = eCommandTypeMax;
	*ppCommand = NULL;

	while ((c = getopt_long (argc, argv, "ad:hl:tv", long_options, &option_index)) != -1)
	{
		switch(c)
		{
		case 0:
			*pct = commandTypeFromString(long_options[option_index].name);

			if (*pct == eCommandTypeMax)
			{
				printUsage(stdout, argv0);
				exit(0);
			}
			break;

		case 'a':
			*pLogMode = eLogOpenModeAppend;
			break;
		case 'd':
			*pDebugLevel = atoi(optarg);
			break;
		case 'h':
			printUsage(stdout, argv0);
			exit(0);
		case 'l':
			*ppszLogName = optarg;
			break;
		case 't':
			*pLogMode = eLogOpenModeTruncate;
			break;
		case 'v':
			printVersion(stdout, PACKAGE, VERSION);
			exit(0);
		default:
			printUsage(stderr, argv0);
			exit(1);
		}
	}

	if (*pct != eCommandTypeMax)
	{
		*ppCommand = commandFromArgv(*pct, argc, optind, argv);
	}

	if (*ppCommand == NULL)
	{
		printUsage(stderr, argv0);
		exit(1);
	}
}

void printUsage(FILE *fp, const char *argv0)
{
	assert(fp);
	/* argv0 isn't used here, but it might be! */

	fprintf(fp, "Usage:\n");
	fprintf(fp, "  %s [general_options] options args\n", argv0);
	fprintf(fp, "\n");
	fprintf(fp, "  [general_options]\n");
	printCommonCmdlineOptions(fp);
	fprintf(fp, "\n");
	fprintf(fp, "  options args\n");
	fprintf(fp, "   --provide      name        Tell daemon that ppid is starting service \"name\"\n");
	fprintf(fp, "   --unprovide    name        Tell daemon that ppid is stopping service \"name\"\n");
	fprintf(fp, "   --need         name        Tell daemon that ppid needs \"name\" to continue\n");
	fprintf(fp, "   --statuschange pid status  Tell daemon the exit status of pid\n");
	fprintf(fp, "   --control      stop        Stop the daemon\n");
	fprintf(fp, "   --query        state name  Output the fsm state of a service\n");
	fprintf(fp, "   --query        graph [..]  Output daemon state as an xml/rdf document\n");
}
