#ifndef CONNECT_SERVICES__GRID_WORKER_APP_HPP
#define CONNECT_SERVICES__GRID_WORKER_APP_HPP


/*  $Id: grid_worker_app.hpp 595895 2019-10-29 17:07:52Z sadyrovr $
 * ===========================================================================
 *
 *                            PUBLIC DOMAIN NOTICE
 *               National Center for Biotechnology Information
 *
 *  This software/database is a "United States Government Work" under the
 *  terms of the United States Copyright Act.  It was written as part of
 *  the author's official duties as a United States Government employee and
 *  thus cannot be copyrighted.  This software/database is freely available
 *  to the public for use. The National Library of Medicine and the U.S.
 *  Government have not placed any restriction on its use or reproduction.
 *
 *  Although all reasonable efforts have been taken to ensure the accuracy
 *  and reliability of the software and data, the NLM and the U.S.
 *  Government do not and cannot warrant the performance or results that
 *  may be obtained by using this software or data. The NLM and the U.S.
 *  Government disclaim all warranties, express or implied, including
 *  warranties of performance, merchantability or fitness for any particular
 *  purpose.
 *
 *  Please cite the author in any work or product based on this material.
 *
 * ===========================================================================
 *
 * Authors:  Maxim Didenko, Anatoliy Kuznetsov, Dmitry Kazimirov
 *
 * File Description:
 *   NetSchedule Worker Node Framework Application.
 *
 */

/// @file grid_worker_app.hpp
/// NetSchedule worker node application.
///


#include <connect/services/grid_worker.hpp>
#include <corelib/ncbiapp.hpp>

BEGIN_NCBI_SCOPE

/// Listener of events generated by CGridWorkerNodeApp.
/// @see CGridWorkerApp::SetListener().
class NCBI_XCONNECT_EXPORT IGridWorkerNodeApp_Listener
{
public:
    /// Perform the necessary pre-init checks. Throw an exception
    /// if the worker node cannot be launched at this time.
    /// The method is called from the Init() method of the
    /// worker node application prior to daemonizing.
    virtual void OnInit(IWorkerNodeInitBaseContext*) {}

    /// @deprecated, use OnInit(IWorkerNodeInitBaseContext*) instead
    NCBI_DEPRECATED virtual void OnInit(CNcbiApplication*) final {}

    /// Notify that CGridWorkerNode::Run() is about to be executed.
    /// This method can be overridden to implement worker node-
    /// specific initialization.
    virtual void OnGridWorkerStart() = 0;

    /// Notify that CGridWorkerNode::Run() has just finished.
    virtual void OnGridWorkerStop() = 0;

    virtual ~IGridWorkerNodeApp_Listener() {}
};

/// An adapter class for IGridWorkerNodeApp_Listener.
class CGridWorkerNodeApp_Listener : public IGridWorkerNodeApp_Listener
{
    virtual void OnGridWorkerStart() {}
    virtual void OnGridWorkerStop() {}
};

/// Main Worker Node application
///
/// @note
/// Worker node application is parameterized using INI file settings.
/// Please read the sample ".ini" file for more information.
///
class NCBI_XCONNECT_EXPORT CGridWorkerApp : public CNcbiApplication
{
public:
    CGridWorkerApp(IWorkerNodeJobFactory* job_factory,
                   const SBuildInfo& build_info = NCBI_SBUILDINFO_DEFAULT());

    CGridWorkerApp(IWorkerNodeJobFactory* job_factory,
                   const CVersionInfo& version_info,
                   const SBuildInfo& build_info = NCBI_SBUILDINFO_DEFAULT());

    /// Register a listener of events of this class.
    ///
    /// @note
    /// This object takes ownership over the listener object
    /// and will automatically 'delete' it.
    ///
    /// @param listener A pointer to the listener object.
    ///   NULL can be passed to stop listening and reset the
    ///   internal pointer to the default handler.
    void SetListener(IGridWorkerNodeApp_Listener* listener);

    /// If you override this method, do call CGridWorkerApp::Init()
    /// from inside of your overriding method.
    virtual void Init(void);

    /// Do not override this method yourself! It includes all the Worker Node
    /// specific machinery. If you override it, do call CGridWorkerApp::Run()
    /// from inside your overriding method.
    virtual int Run(void);

    virtual void SetupArgDescriptions(CArgDescriptions* arg_desc);

    void RequestShutdown();

    CGridWorkerNode GetWorkerNode() const {return m_WorkerNode;}

private:
    void Construct(IWorkerNodeJobFactory* job_factory);

    CGridWorkerNode m_WorkerNode;

    CGridWorkerApp(const CGridWorkerApp&);
    CGridWorkerApp& operator=(const CGridWorkerApp&);
};

inline void CGridWorkerApp::SetListener(IGridWorkerNodeApp_Listener* listener)
{
    m_WorkerNode.SetListener(listener);
}

// Define GRID_WORKER_APP_NAME for use by the logging system.
#ifndef GRID_WORKER_APP_NAME
#ifdef GRID_APP_NAME
#define GRID_WORKER_APP_NAME GRID_APP_NAME
#else
#define GRID_WORKER_APP_NAME NcbiEmptyString
#endif
#endif

#ifdef NCBI_BUILD_TAG
#   define GRID_WORKER_APP_BUILD_TAG NCBI_AS_STRING(NCBI_BUILD_TAG)
#else
#   define GRID_WORKER_APP_BUILD_TAG kEmptyStr
#endif

#ifdef NCBI_WORKERNODE_CLASS
#error \
    NCBI_WORKERNODE_CLASS is not supported anymore. \
    Replace NCBI_WORKERNODE_* with corresponding NCBI_WORKERNODE_*_DERIVED providing app class. \
    Please be aware that your app class is required to accept SBuildInfo and pass it to CGridWorkerApp.
#endif

#define NCBI_WORKERNODE_MAIN_IMPL(TFactory, CGridWorkerApp, Version, SetListener) \
    int main(int argc, const char* argv[])                                  \
    {                                                                       \
        GetDiagContext().SetOldPostFormat(false);                           \
        CGridWorkerApp app(new TFactory,                                    \
                CVersionInfo(#Version, NCBI_TEAMCITY_PROJECT_NAME_PROXY),   \
                NCBI_APP_SBUILDINFO_DEFAULT());                             \
        SetListener;                                                        \
        return app.AppMain(argc, argv, NULL, eDS_ToStdlog,                  \
                NcbiEmptyCStr, GRID_WORKER_APP_NAME);                       \
    }

#define NCBI_WORKERNODE_MAIN(TWorkerNodeJob, Version)                       \
    NCBI_WORKERNODE_MAIN_DERIVED(TWorkerNodeJob, CGridWorkerApp, Version)

#define NCBI_WORKERNODE_MAIN_DERIVED(TWorkerNodeJob, CGridWorkerApp, Version) \
    NCBI_DECLARE_WORKERNODE_FACTORY(TWorkerNodeJob, Version)               \
    NCBI_WORKERNODE_MAIN_IMPL(TWorkerNodeJob##Factory, CGridWorkerApp, Version, )

#define NCBI_WORKERNODE_MAIN_WITH_LISTENER(TWorkerNodeJob, Version, ListenerClass) \
    NCBI_WORKERNODE_MAIN_WITH_LISTENER_DERIVED(TWorkerNodeJob, CGridWorkerApp, Version, ListenerClass)

#define NCBI_WORKERNODE_MAIN_WITH_LISTENER_DERIVED(TWorkerNodeJob, CGridWorkerApp, Version, ListenerClass) \
    NCBI_DECLARE_WORKERNODE_FACTORY(TWorkerNodeJob, Version)               \
    NCBI_WORKERNODE_MAIN_IMPL(TWorkerNodeJob##Factory, CGridWorkerApp, Version, app.SetListener(new ListenerClass))

#define NCBI_WORKERNODE_MAIN_EX(TWorkerNodeJob, TWorkerNodeIdleTask, Version) \
    NCBI_WORKERNODE_MAIN_EX_DERIVED(TWorkerNodeJob, TWorkerNodeIdleTask, CGridWorkerApp, Version)

#define NCBI_WORKERNODE_MAIN_EX_DERIVED(TWorkerNodeJob, TWorkerNodeIdleTask, CGridWorkerApp, Version) \
    NCBI_DECLARE_WORKERNODE_FACTORY_EX(TWorkerNodeJob, TWorkerNodeIdleTask, Version) \
    NCBI_WORKERNODE_MAIN_IMPL(TWorkerNodeJob##FactoryEx, CGridWorkerApp, Version, )

#define NCBI_GRID_PKG_WORKER_NODE_MAIN(TWorkerNodeJob,                      \
        TWorkerNodeJobFactoryClass, ListenerClass)                          \
    int main(int argc, const char* argv[])                                  \
    {                                                                       \
        GRID_APP_CHECK_VERSION_ARGS();                                      \
        GetDiagContext().SetOldPostFormat(false);                           \
        CGridWorkerApp app(new TWorkerNodeJobFactoryClass);                 \
        app.SetListener(new ListenerClass);                                 \
        return app.AppMain(argc, argv, NULL, eDS_ToStdlog,                  \
                NcbiEmptyCStr, GRID_WORKER_APP_NAME);                       \
    }

/* @} */


END_NCBI_SCOPE

#endif //CONNECT_SERVICES__GRID_WORKER_APP_HPP
