#ifndef PREFETCH_MANAGER__HPP
#define PREFETCH_MANAGER__HPP

/*  $Id: prefetch_manager.hpp 574980 2018-11-21 14:24:48Z ucko $
* ===========================================================================
*
*                            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.
*
* ===========================================================================
*
* Author: Eugene Vasilchenko
*
* File Description:
*   Prefetch manager
*
*/

#include <corelib/ncbistd.hpp>
#include <corelib/ncbiobj.hpp>
#include <corelib/ncbithr.hpp>
#include <util/thread_pool.hpp>

BEGIN_NCBI_SCOPE
BEGIN_SCOPE(objects)

class CScope;

/** @addtogroup ObjectManagerCore
 *
 * @{
 */


class CPrefetchRequest;
class CPrefetchManager;
class CPrefetchManager_Impl;

struct SPrefetchTypes
{
    enum EState {
        eInvalid,   // no prefetch token available
        eQueued,    // placed in queue
        eStarted,   // moved from queue to processing
        eAdvanced,  // got new data while processing
        eCompleted, // finished processing successfully
        eCanceled,  // canceled by user request
        eFailed     // finished processing unsuccessfully
    };
    typedef EState EEvent;
    
    typedef int TPriority;
    typedef int TProgress;
};


class NCBI_XOBJMGR_EXPORT IPrefetchAction : public SPrefetchTypes
{
public:
    virtual ~IPrefetchAction(void);
    
    virtual bool Execute(CRef<CPrefetchRequest> token) = 0;
};


class NCBI_XOBJMGR_EXPORT IPrefetchActionSource
{
public:
    virtual ~IPrefetchActionSource(void);

    virtual CIRef<IPrefetchAction> GetNextAction(void) = 0;
};


class NCBI_XOBJMGR_EXPORT IPrefetchListener : public SPrefetchTypes
{
public:
    virtual ~IPrefetchListener(void);

    virtual void PrefetchNotify(CRef<CPrefetchRequest> token, EEvent event) = 0;
};


class NCBI_XOBJMGR_EXPORT CPrefetchManager :
    public CObject,
    public SPrefetchTypes
{
public:
    CPrefetchManager(void);
    explicit CPrefetchManager(unsigned max_threads,
                              CThread::TRunMode threads_mode = CThread::fRunDefault);
    ~CPrefetchManager(void);

    CRef<CPrefetchRequest> AddAction(TPriority priority,
                                     IPrefetchAction* action,
                                     IPrefetchListener* listener = 0);
    CRef<CPrefetchRequest> AddAction(IPrefetchAction* action,
                                     IPrefetchListener* listener = 0);

    // Checks if prefetch is active in current thread.
    // Throws CPrefetchCanceled exception if the current token is canceled.
    static bool IsActive(void);
    
    // Send cancel requests to all tasks, queued and executing
    void CancelAllTasks(void);

    // Clears manager queue and stops all worker threads.
    void Shutdown(void);
    
private:
    CRef<CPrefetchManager_Impl> m_Impl;

private:
    CPrefetchManager(const CPrefetchManager&);
    void operator=(const CPrefetchManager&);
};


/// This exception is used to report failed actions
class NCBI_XOBJMGR_EXPORT CPrefetchFailed : public CException
{
public:
    enum EErrCode {
        eFailed
    };
    virtual const char* GetErrCodeString(void) const override;
    NCBI_EXCEPTION_DEFAULT(CPrefetchFailed,CException);
};


/// This exception is used to interrupt actions canceled by user
class NCBI_XOBJMGR_EXPORT CPrefetchCanceled : public CException
{
public:
    enum EErrCode {
        eCanceled
    };
    virtual const char* GetErrCodeString(void) const override;
    NCBI_EXCEPTION_DEFAULT(CPrefetchCanceled,CException);
};


class NCBI_XOBJMGR_EXPORT CPrefetchSequence : public CObject
{
public:
    CPrefetchSequence(CPrefetchManager& manager,
                      IPrefetchActionSource* source,
                      size_t active_size = 10);
    ~CPrefetchSequence(void);
    
    /// Returns next action waiting for its result if necessary
    CRef<CPrefetchRequest> GetNextToken(void);

protected:
    void EnqueNextAction(void);

private:
    CRef<CPrefetchManager>          m_Manager;
    CIRef<IPrefetchActionSource>    m_Source;
    CMutex                          m_Mutex;
    list< CRef<CPrefetchRequest> >  m_ActiveTokens;
};


class NCBI_XOBJMGR_EXPORT CPrefetchRequest
    : public CThreadPool_Task,
      public SPrefetchTypes
{
public:
    CPrefetchRequest(CObjectFor<CMutex>* state_mutex,
                     IPrefetchAction* action,
                     IPrefetchListener* listener,
                     unsigned int priority);
    ~CPrefetchRequest(void);
    
    IPrefetchAction* GetAction(void) const
        {
            return m_Action.GetNCPointer();
        }

    IPrefetchListener* GetListener(void) const
        {
            return m_Listener.GetNCPointerOrNull();
        }
    void SetListener(IPrefetchListener* listener);
    
    EState GetState(void) const;

    // in one of final states: completed, failed, canceled 
    bool IsDone(void) const
        {
            return IsFinished();
        }

    TProgress GetProgress(void) const
        {
            return m_Progress;
        }
    TProgress SetProgress(TProgress progress);

    virtual EStatus Execute(void);

    virtual void OnStatusChange(EStatus /* old */);

private:
    friend class CPrefetchManager;
    friend class CPrefetchManager_Impl;

    // back references
    CRef<CObjectFor<CMutex> >   m_StateMutex;

    CIRef<IPrefetchAction>      m_Action;
    CIRef<IPrefetchListener>    m_Listener;
    TProgress                   m_Progress;
};


/* @} */


END_SCOPE(objects)
END_NCBI_SCOPE

#endif  // PREFETCH_MANAGER__HPP
