#include "config.h"

// Implementation
#include "Thread.hpp"

// STDC++
#include <cassert>
#include <cstdlib>

// Local
#include "Time.hpp"
#include "Text.hpp"

namespace UI {
namespace Util {

void fd2DevNull(int const & fd, mode_t const & mode)
{
	Sys::close(fd);
	int const newFd(Sys::open("/dev/null", mode));
	if (newFd != fd)
	{
		Sys::dup2(newFd, fd);
		Sys::close(newFd);
	}
}

void daemonize(bool closeStdFds,
               bool changeToRootdir,
               bool resetUMask)
{
#ifndef WIN32
	switch (Sys::fork())
	{
	case 0:
		// We are in the new child
		if (closeStdFds)
		{
			fd2DevNull(0, O_RDONLY);
			fd2DevNull(1, O_WRONLY);
			fd2DevNull(2, O_WRONLY);
		}
		if (changeToRootdir) { Sys::chdir("/"); }
		if (resetUMask)      { Sys::umask(0); }
		Sys::setsid();
		break;
	default:
		// Old pid, silently leave
		std::exit(0);
	}
#endif
}

#ifndef WIN32
ProcessThread::ProcessThread()
	:pid_(-1)
	,status_(NotRunYet_)
{}

ProcessThread::~ProcessThread() {}

pid_t ProcessThread::getPID() const
{
	return pid_;
}

void ProcessThread::checkStatus(bool wait)
{
	if (status_ == Running_)
	{
		assert(pid_ != -1);

		// Process is running, but might be finished (zombie)
		int status;
		switch (pid_t pid = Sys::waitpid(pid_, &status, wait ? 0 : WNOHANG))
		{
		case 0: // No child available; still running, nothing to do
			break;
		default: // Child exited; status_ becomes the process exit status
			assert(pid == pid_);
			if (WIFEXITED(status))  // Child exited normally
			{
				status_ = WEXITSTATUS(status);
			}
			else if (WIFSIGNALED(status))
			{
				status_ = KilledByUncatchedSignal_;
			}
			else
			{
				status_ = WaitpidErr_;
			}
			pid_ = -1;
		}
	}
}

int ProcessThread::getStatus() const
{
	return status_;
}

bool ProcessThread::isRunning()
{
	checkStatus(false);
	return (status_ == Running_);
}

int ProcessThread::wait()
{
	checkStatus(true);
	return status_;
}


void ForkThread::start()
{
	if (status_ != Running_) 	// Just be happy if we are already running
	{
		switch (int pid = Sys::fork())
		{
		case 0: // We are in the new child
			// We do not share context, but at least local getPID() should work ... ;(
			// getStatus() wont work, but is not needed in the child really ...
			pid_ = Sys::getpid();
			std::exit(run());
			break;
		default: // Ok, father process
			status_ = Running_;
			pid_ = pid;
		}
	}
}
#endif

}}
