#include "process.h"
#include "common.h"
#include <vdr/tools.h>
#include <vdr/thread.h>
#include <vdr/config.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>

static const char *DEFAULT_VERSION = "n/a";
static const char *DEFAULT_DESCRIPTION = "Not available";

cPerlProcess::cPerlProcess(void):
		mPid(-1),
		mPath(NULL),
		mArgc(0),
		mArgv(NULL),
		mPipe(NULL),
		mRunning(false),
		mMainMenuEntry(NULL),
		mVersion(strdup(DEFAULT_VERSION)),
		mDescription(strdup(DEFAULT_DESCRIPTION)),
		mObject(NULL)
{
}

cPerlProcess::~cPerlProcess()
{
	if (mPid > 0) {
		int pid = 0, count = 0;
		kill(mPid, SIGTERM);
		while (count < 40 && (pid = waitpid(mPid, NULL, WNOHANG)) == 0) {
			usleep(50000);
			++count;
		}

		if (pid == -1)
			esyslog("ERROR: Couldn't terminate perl scripting host: %m");
		else if (pid == 0) {
			esyslog("ERROR: perl scripting host hangs, killing...");
			kill(mPid, SIGKILL);
		}
	}

	if (mPipe != NULL)
		fclose(mPipe);

	free(mMainMenuEntry);
	free(mVersion);
	free(mDescription);
	free(mPath);
}

void cPerlProcess::ProcessArgs(int argc, char *argv[])
{
	mArgc = argc;
	mArgv = new char*[mArgc + 2]; // one slot for pipe descriptor
	for (int i = 0; i < mArgc; ++i)
		mArgv[i] = strdup(argv[i]);
	mArgv[mArgc] = NULL; // pipe descriptor here
	mArgv[mArgc + 1] = NULL;
}

void cPerlProcess::Start(void)
{
	int piped[2];

	if (socketpair(AF_UNIX, SOCK_STREAM, 0, piped) == -1) {
		esyslog("ERROR: Couldn't spawn perl scripting host: %m");
		return;
	}
	asprintf(&mArgv[mArgc], "%d", piped[1]);
	
	mPid = fork();
	if (mPid == -1) {
		esyslog("ERROR: Couldn't spawn perl scripting host: %m");
		return;
	}

	if (mPid == 0) {
		int flags = fcntl(piped[1], F_GETFD);
		fcntl(piped[1], F_SETFD, flags & ~FD_CLOEXEC);
		close(STDIN_FILENO);
		close(STDOUT_FILENO);
		for (int i = STDERR_FILENO + 1; i < getdtablesize(); ++i) {
			if (i != piped[1])
				close(i);
		}
		chdir(mPath);
		execvp("vdr-perl", mArgv);
		esyslog("ERROR: Couldn't startup perl scripting host: %m");
		_exit(127);
	}

	mPipe = fdopen(piped[0], "r+");
	SetPipe(mPipe);
	//setvbuf(mPipe, NULL, _IONBF, 0);
	close(piped[1]);

	ReadAction("QR");
}

void cPerlProcess::HandleAction(const char *Action)
{
	char action = Action[0];
	const char *opts = skipspace(Action + 1);

	switch (action) {
	case 'Q': mRunning = false; break;

	case 'R': mRunning = true; break;

	case 'P': SetParameter(opts); break;

	case 'c': FinishCallback(opts); break;

	default:  cPerlComm::HandleAction(Action);
	}
}

void cPerlProcess::SetParameter(const char *Opts)
{
	char **params = SplitParameters(Opts, 2);

	SET_PARAM_STRING("MainMenuEntry", mMainMenuEntry, NULL);
	SET_PARAM_STRING("Version", mVersion, DEFAULT_VERSION);
	SET_PARAM_STRING("Description", mDescription, DEFAULT_DESCRIPTION);

	FreeParameters(params, 2);
}

void cPerlProcess::FinishCallback(const char *Opts)
{
	if (*Opts == '\0')
		return;

	char *ptr;
	if ((ptr = strchr(Opts, '=')) == NULL) {
		esyslog("ERROR: Communication with scripting host broken at %s:%d",
		        __FILE__, __LINE__);
		return;
	}

	mObject = (cOsdObject*)strtoul(ptr + 1, NULL, 0);
}

cOsdObject *cPerlProcess::MainMenuAction(void)
{
	mObject = NULL;

	SendAction("C MainMenuAction");
	ReadAction("c");

	return (cOsdObject*)mObject;
}
