/**
 * @file
 * @brief Thread, descriptors and sockets.
 */
#ifndef UI_UTIL_THREAD_HPP
#define UI_UTIL_THREAD_HPP

// C++ libraries
#include <ui-utilcpp/Exception.hpp>
#include <ui-utilcpp/Sys.hpp>

namespace UI {
namespace Util {

/** @brief Close fd and reopen on "/dev/null" using mode. */
void fd2DevNull(int const & fd, mode_t const & mode);

/** @brief Use fork(2) to daemonize current program in a new session id.
 *
 * @param closeStdFds Whether to close fd 0,1 and 2 (and re-open them to /dev/null). This is recommended for all production daemons.
 * @param changeToRootdir Will do a chdir(2) to "/". This is to avoid all errors due to a later remove/change of the cwd.
 * @param resetUMask Resets umask to "0".
 * @see fork(2), setsid(2), chdir(2), umask(2).
 */
void daemonize(bool closeStdFds=true,
               bool changeToRootdir=true,
               bool resetUMask=true);

/**
 * @example Threads.cpp
 * Example on how to employ "ProcessThread" threads.
 */

/** @brief Simple encapsulation for any process based threading system.
 *
 * You need to overload "start" and "run".
 */
#ifndef WIN32
class ProcessThread
{
public:
	/** @brief Reserved exit status'. Do not use these as return codes in your run()-functions.
	 *
	 * Program exit status' are always between (including) 0..255; to be able to use
	 * the exit status directly in programs (e.g. for std::exit()), we reserve some
	 * values here (rather than using out-of-range values).
	 */
	enum ReservedStatus
	{
		NotRunYet_ = 255,
		Running_ = 254,
		WaitpidErr_ = 253,
		KilledByUncatchedSignal_ = 252
	};

	/** */
	ProcessThread();
	virtual ~ProcessThread();

	/** @brief Start fork "thread". */
	virtual void start() = 0;

	/** Check if "thread" is running. */
	bool isRunning();

	/** @brief Get PID of running "thread". */
	pid_t getPID() const;

	/** @brief Wait for this "thread", and return the process' exit status.
	 *
	 * You must use this method to avoid zombies, or ignore all SIGCHLDs in the parent.
	 */
	int wait();

	/** @brief Get status of last run. */
	int getStatus() const;

protected:
	/** Process id of thread: Must be set properly by overloaded start method. */
	pid_t pid_;
	/** Status of thread: Must be set properly by overloaded start method. */
	int status_;

	/** Virtual function of thread's task. */
	virtual int run() = 0;

private:
	/** Internal function to check status using waitpid(2) */
	void checkStatus(bool wait=false);
};

/**
 * @class ForkThread
 * @brief Simple encapsulation of fork(2) using common thread syntax.
 * @attention This is not threading -- i.e. the "thread" does not share any execution
 *            context with its caller; it just uses "common thread class nomenclature", hence the name.
 */
class ForkThread: public ProcessThread
{
public:
	/** @brief Spawn thread. */
	void start();
};
#endif

}}
#endif
