root/src/apps/haikudepot/process/ProcessCoordinator.h
/*
 * Copyright 2018-2022, Andrew Lindesay <apl@lindesay.co.nz>.
 * All rights reserved. Distributed under the terms of the MIT License.
 */
#ifndef PROCESS_COORDINATOR_H
#define PROCESS_COORDINATOR_H

#include "ProcessCoordinator.h"

#include <ObjectList.h>

#include "AbstractProcess.h"
#include "AbstractProcessNode.h"
#include "ProcessListener.h"


class ProcessCoordinator;


/*! This class carries the state of the current process coordinator so that
    it can be dealt with atomically without having call back to the coordinator.
*/

class ProcessCoordinatorState : public BArchivable {
public:
                                                                ProcessCoordinatorState(BMessage* from);
                                                                ProcessCoordinatorState(
                                                                        const ProcessCoordinator*
                                                                                processCoordinator,
                                                                        float progress, const BString& message,
                                                                        bool isRunning, status_t errorStatus);
        virtual                                         ~ProcessCoordinatorState();

        const   BString                         ProcessCoordinatorIdentifier() const;
                        float                           Progress() const;
                        BString                         Message() const;
                        bool                            IsRunning() const;
                        status_t                        ErrorStatus() const;

                        status_t                        Archive(BMessage* into, bool deep = true) const;
private:
                        BString                         fProcessCoordinatorIdentifier;
                        float                           fProgress;
                        BString                         fMessage;
                        bool                            fIsRunning;
                        status_t                        fErrorStatus;
};


/*! Clients are able to subclass from this 'interface' in order to accept
    call-backs when a coordinator has exited; either through failure,
    stopping or completion.
*/

class ProcessCoordinatorListener {
public:

/*! Signals to the listener that the coordinator has changed in some way -
        for example, a process has started or stopped or even that the whole
        coordinator has finished.
*/

        virtual void                            CoordinatorChanged(
                                                                        ProcessCoordinatorState&
                                                                        processCoordinatorState) = 0;

};


/*!     Classes implementing this 'interface' are able to consume process
        coordinators.  This may be in order to run them.
*/

class ProcessCoordinatorConsumer {
public:
        virtual void                            Consume(ProcessCoordinator *item) = 0;
};


/*! It is possible to create a number of ProcessNodes (themselves associated
    with AbstractProcess-s) that may have dependencies (predecessors and
    successors) and then an instance of this class is able to coordinate the
    list of ProcessNode-s so that they are all completed in the correct order.
*/

class ProcessCoordinator : public ProcessListener {
public:
                                                                ProcessCoordinator(
                                                                        const char* name,
                                                                        BMessage* message = NULL);
        virtual                                         ~ProcessCoordinator();

        const   BString&                        Identifier() const;

                        void                            SetListener(
                                                                        ProcessCoordinatorListener *listener);

                        void                            AddNode(AbstractProcessNode* nodes);

                        void                            ProcessChanged();
                                // ProcessListener

                        bool                            IsRunning();

                        void                            Start();
                        void                            RequestStop();

                        status_t                        ErrorStatus();

                        float                           Progress();

                        BString                         LogReport();

        const   BString&                        Name() const;
                        BMessage*                       Message() const;

private:
                        void                            _CoordinateAndCallListener();
                        ProcessCoordinatorState
                                                                _Coordinate();
                        ProcessCoordinatorState
                                                                _CreateStatus();
                        BString                         _CreateStatusMessage();
                        int32                           _CountNodesCompleted();
                        void                            _StopSuccessorNodesToErroredOrStoppedNodes();
                        void                            _StopSuccessorNodes(AbstractProcessNode* node);

private:
                        BString                         fName;
                        BLocker                         fLock;
                        bool                            fCoordinateAndCallListenerRerun;
                        BLocker                         fCoordinateAndCallListenerRerunLock;
                        BObjectList<AbstractProcessNode>
                                                                fNodes;
                        ProcessCoordinatorListener*
                                                                fListener;
                        BMessage*                       fMessage;
                        bool                            fWasStopped;
                        BString                         fIdentifier;
};


#endif // PROCESS_COORDINATOR_H