/*
 * See the files COPYING and README for copyright information and how to reach 
 * the author.
 *
 *  $Id: process.c,v 1.3 2006/01/20 21:51:29 lordjaxom Exp $
 */

#include "process.h"
#include "shell.h"
#include "menuburn.h"
#include "poller.h"
#include "setup.h"
#include <vdr/tools.h>
#include <vdr/videodir.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdarg.h>

cBurnProcess::cBurnProcess(int NumSteps):
#if VDRVERSNUM >= 10300
		cThread("burn: subprocess watcher"),
#endif
		mActive(false),
		mBurning(false),
		mProgress(0),
		mBurnProgress(0),
		mDataDir(cString::sprintf("%s/.vdr-burn.XXXXXX", 
								  BurnParameters.DataPath)),
		mTempDir(cString::sprintf("%s/.vdr-burn.XXXXXX", 
								  BurnParameters.TempPath)),
		mLogFile(NULL),
		mNumSteps(NumSteps),
		mCurStep(0),
		mSubStep(-1),
		mNextStep(nsContinue)
{
	mkdtemp(const_cast<char*>(*mDataDir));
	mkdtemp(const_cast<char*>(*mTempDir));
}

cBurnProcess::~cBurnProcess() 
{
	Stop();
	Execute("rm -rf %s", *mTempDir);
	Execute("rm -rf %s", *mDataDir);
}

void cBurnProcess::Stop(void) 
{
	if (mActive) {
		mActive = false;
#if VDRVERSNUM < 10314
		WakeUp();
#endif
		Cancel(3);
	}
}

void cBurnProcess::SetProgress(int SubSteps, int Percent) 
{
	mProgress = mCurStep * 100 / mNumSteps 
			  + mSubStep * 100 / mNumSteps / SubSteps 
			  + Percent / mNumSteps / SubSteps;
	//printf("percent: %d %% progress: %d %%\n", Percent, mProgress);
}

void cBurnProcess::SetProgress(int SubSteps, int Subsub, int Subsubs, 
							   int Percent) 
{
	mProgress = mCurStep * 100 / mNumSteps 
			  + mSubStep * 100 / mNumSteps / SubSteps 
			  + Subsub * 100 / mNumSteps / SubSteps / Subsubs 
			  + Percent / mNumSteps / SubSteps / Subsubs;
}

void cBurnProcess::AddCmdline(const char *Command)
{
	cString fullcmd = cString::sprintf("nice -n19 %s", Command);
	
	cBurnShell *pipe = new cBurnShell;
	pipe->SetWorkdir(mDataDir);
	pipe->SetCommand(fullcmd);
	pipe->SetEnvironment(mEnvironment);
	
	mPipes.Add(pipe);
}

void cBurnProcess::SetLogFile(const char *FileName)
{
	if (mLogFile != NULL)
		fclose(mLogFile);

	if ((mLogFile = fopen(FileName, "w")) == NULL)
		esyslog("ERROR[burn]: couldn't open logfile %s (job could fail): %m",
				FileName);
}

void cBurnProcess::Execute(const char *Format, ...) 
{
	char *cmd;
	va_list ap;
	va_start(ap, Format);
	vasprintf(&cmd, Format, ap);
	va_end(ap);
	if (mLogFile) {
		fprintf(mLogFile, "++ executing /bin/sh -c '%s'\n", cmd);
		fflush(mLogFile);
	}
	isyslog("burn: executing /bin/sh -c '%s'\n", cmd);
	SystemExec(cmd);
	free(cmd);
}

bool cBurnProcess::Execute(cBurnPipe *Pipe)
{
	cBurnPipes  pipes;
	cBurnPoller poller;
	bool        active = true;

	if (!Pipe->Launch(mLogFile))
		return false;

	pipes.Add(Pipe);
	poller.Set(&pipes);
	while (active && pipes.Count()) {
		if (!poller.Poll(1000) ||
			!poller.Read(this)) {
			active = false;
			return false;
		}
	}
	return true;
}

bool cBurnProcess::HasNext(void) 
{
	mNextStep = nsContinue;
	++mSubStep;
	if (Continue(mCurStep, mSubStep)) {
		switch (mNextStep) {
			case nsNext:
				SetProgress(mSubStep + 1, 100);
				++mCurStep;
			case nsRetry:
				mSubStep = -1;
				break;

			case nsContinue:
				break;
		}
		return mCurStep < mNumSteps;
	}
	return false;
}

void cBurnProcess::PutLine(const char *Line) 
{
	if (mLogFile) {
		fprintf(mLogFile, "%s\n", Line);
		fflush(mLogFile);
	}
	Process(mCurStep, mSubStep, Line);
}

void cBurnProcess::Action(void) 
{
	mActive = true;

#if VDRVERSNUM < 10300
	setpriority(PRIO_PROCESS, 0, 19);
	isyslog("burn: subprocess watcher thread started (pid=%d)", getpid());
#else
	SetPriority(19);
#endif

	mStatus = rsNone;

	printf("enter loop\n");

	while (mActive && HasNext()) {
		cBurnPoller poller;
		cBurnPipe *pipe;

		printf("next %ld\n", time(NULL));

		if (mPipes.Count() == 0)
			continue;

		for (pipe = mPipes.First(); pipe != NULL; pipe = mPipes.Next(pipe)) {
			if (!pipe->Launch(mLogFile)) {
				mActive = false;
				break;
			}
		}
		poller.Set(&mPipes);

		while (mActive && mPipes.Count()) {
			if (!poller.Poll(1000) ||
				!poller.Read(this)) {
				mActive = false;
				break;
			}
		}
	}

	if (mActive) {
		mActive = false;
		mProgress = 100;
	} else
		mStatus = rsError;

	if (mLogFile) {
		fclose(mLogFile);
		mLogFile = NULL;
	}

	mPipes.Clear();

#if VDRVERSNUM < 10300
	isyslog("burn: subprocess watcher thread stopped (pid=%d)", getpid());
#endif

	cBurnMenuBurn::NextJob();
}

