#ifndef SYSTEM_HPP_
#define SYSTEM_HPP_
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// Name        :
// Author      : Avi
// Revision    : $Revision: #18 $ 
//
// Copyright 2009-2020 ECMWF.
// This software is licensed under the terms of the Apache Licence version 2.0 
// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
// In applying this licence, ECMWF does not waive the privileges and immunities 
// granted to it by virtue of its status as an intergovernmental organisation 
// nor does it submit to any jurisdiction. 
//
// Description : Works with class Signal
//
// When a child process terminates it does not disappear entirely.
// Instead it becomes a ‘zombie process’ which is no longer capable of executing,
// but which still has a PID and an entry in the process table.
// This is indicated by the state code Z in ps or top.
//
// The presence of a moderate number of zombie processes is not particularly harmful,
// but they add unnecessary clutter that can be confusing to the administrator.
// In extreme cases they could exhaust the number of available process table slots.
//
// For these reasons, well-behaved programs should ensure that zombie processes are removed in a timely manner.
// The process of eliminating zombie processes is known as ‘reaping’.
// The simplest method is to call wait, but this will block the parent process if the child has not yet terminated.
// Alternatives are to use waitpid to poll or SIGCHLD to reap asynchronously.
// The Class uses waitpid.
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8

#include <string>
#include "NodeFwd.hpp"

namespace ecf {

/// Job submission in ECF is a two phase step.
/// phase 1: Spawn of ECF_JOB_CMD
/// phase 2: Invocation of ECF_JOB_CMD, this creates the *real* job which communicates with the server
/// For TEST     :ECF_JOB_CMD = "%ECF_JOB% 1> %ECF_JOBOUT% 2>&1
/// 	this collapses phase 1 and 2, to a single step
/// For Operation:ECF_JOB_CMD = ecf_submit %USER% %REMOTE_HOST% %ECFJOB% %ECFJOBOUT%'
///   	This uses ecf_submit,This spawns the process to the different load levellers depending on OS, etc.
///   and hence involves at least 2 process.
/// This class handles phase 1, we capture the death of the child process
/// and hence this class will not in operations handle the death of the real job
/// created by ecf_submit.

class System {
private:
  System(const System&) = delete;
  const System& operator=(const System&) = delete;
public:
    static System* instance();

    /// Destroy the singleton. used in test only, to avoid valgrind issues
    static void destroy();

    /// Let the server set this. Typically only set once, however in test  can be many times
    /// Note:: In test the Defs file in the server can be cleared, i.e. for each new test
    ///        Hence we maintain a weak_ptr to the Defs.
    void setDefs(const defs_ptr& defs) { defs_ = defs;}

    // return true, if command can be spawned, else false.
    // For jobs, We can't store reference to Task*, as future functionality like
    // auto-migrate, etc, means we may end up pointing to garbage.
    // so instead we will store absNodePath. For other commands this can be empty
    enum CmdType { ECF_JOB_CMD, ECF_KILL_CMD, ECF_STATUS_CMD};
    bool spawn(CmdType,const std::string& cmdToSpawn,const std::string& absPath,std::string& errorMsg);

    // Handle children that have stopped,aborted or terminated, etc
    // The signal handler is kept as light as possible, since it is re-entrant.
    // So Signal handles stores the termination state which handled later
    // by processTerminatedChildren. Typically when we un-block SIGCHILD
    void processTerminatedChildren();

    /// returns the number of active process.
    /// for debug only
    int process() const;

private:
    ~System();
    System();

    /// Install signals that can catch signal from child process termination
    static void catchChildProcessTermination();

    // When a process terminates abnormally. This function is used to find the
    // associated task, and set it to the abort state.
    // Relies on the stored Defs ptr. which was set in the server
    void died( const std::string& absNodePath, CmdType, const std::string& reason);

    /// Does the real work of spawning children
    int sys(CmdType,const std::string& cmdToSpawn,const std::string& absPath,std::string& errorMsg);

    static std::string cmd_type(CmdType);
private:
    weak_defs_ptr  defs_;      // weak_ptr is an observer of a shared_ptr
    static System* instance_;
};

}
#endif
