#ifndef ALGO_BLAST_BLASTINPUT___BLAST_SCOPE_SRC__HPP
#define ALGO_BLAST_BLASTINPUT___BLAST_SCOPE_SRC__HPP

/*  $Id: blast_scope_src.hpp 515237 2016-09-29 12:32:30Z camacho $
 * ===========================================================================
 *
 *                            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:  Christiam Camacho
 *
 * ===========================================================================
 */

/** @file blast_scope_src.hpp
 * Declares CBlastScopeSource class to create properly configured CScope
 * objects to invoke the BLAST database data loader first and fall back on to
 * the Genbank data loader if the former fails (this is configurable at
 * runtime).
 */

#include <algo/blast/core/blast_export.h>
#include <objmgr/object_manager.hpp>
#include <objtools/data_loaders/blastdb/bdbloader.hpp>

BEGIN_NCBI_SCOPE
BEGIN_SCOPE(objects)
    class CScope;
END_SCOPE(objects)
BEGIN_SCOPE(blast)

/// Configuration structure for the CBlastScopeSource
/// @note the choice of data loaders to use to configure the scope as well as
/// the BLAST database(s) to search if the BLAST database data loader is used
/// can be configured in the NCBI configuration file, BLAST section.
/// For details, refer to the section 'Configuring BLAST' in the BLAST+ user
/// manual
struct NCBI_BLASTINPUT_EXPORT SDataLoaderConfig {

    /// Default protein BLAST database to use for the BLAST DB data loaders
    static const char* kDefaultProteinBlastDb;
    /// Default nucleotide BLAST database to use for the BLAST DB data loaders
    static const char* kDefaultNucleotideBlastDb;

    /// Configuration options for the BlastScopeSource
    enum EConfigOpts {
        /// Use the local BLAST database loader first, if this fails, use the
        /// remote BLAST database data loader
        eUseBlastDbDataLoader = (0x1 << 0),
        /// Use the Genbank data loader
        eUseGenbankDataLoader = (0x1 << 1),
        /// Do not add any data loaders
        eUseNoDataLoaders     = (0x1 << 2),
        eDefault = (eUseBlastDbDataLoader | eUseGenbankDataLoader)
    };

    /// Constructor which relies on the configuration file to set the BLAST
    /// database to use 
    /// @param load_proteins is this object going to load/read proteins only
    /// [in]
    /// @param options configuration options [in]
    SDataLoaderConfig(bool load_proteins, EConfigOpts options = eDefault)
    {
        x_Init(options, kEmptyStr, load_proteins);
    }

    /// Constructor which allows the specification of a BLAST database to use
    /// to initialize the BLAST DB data loader, without the option to override
    /// this at runtime via the configuration file
    /// @param dbname name of BLAST database [in]
    /// @param protein_data is this object going to load/read proteins only
    /// [in]
    /// @param options configuration options [in]
    SDataLoaderConfig(const string& dbname, bool protein_data,
                      EConfigOpts options = eDefault)
    {
        x_Init(options, dbname, protein_data);
    }

    /// Configures the BLAST database data loader to optimize the retrieval of
    /// *entire* large sequences.
    /// @note This option only has effect upon a BLAST database data loader's
    /// first initialization. If this setting should change on the same BLAST
    /// database, the data loader must be revoked from the object manager (this
    /// can be achieved with CBlastScopeSource::RevokeBlastDbDataLoader()).
    /// @param value TRUE to turn on, FALSE to turn off
    void OptimizeForWholeLargeSequenceRetrieval(bool value = true) {
        m_UseFixedSizeSlices = !value;
    }

    /// Determine whether either of the data loaders should be used
    bool UseDataLoaders() const { return m_UseBlastDbs || m_UseGenbank; }

    /// Use the BLAST database data loaders
    bool m_UseBlastDbs;
    /// Name of the BLAST database to use (non-empty if m_UseBlastDbs is true)
    string m_BlastDbName;

    /// Is this intended to load protein sequences
    bool m_IsLoadingProteins;

    /// Use the Genbank data loader
    bool m_UseGenbank;

    /// Equality operator
    bool operator==(const SDataLoaderConfig& rhs) const;
    /// Inequality operator
    bool operator!=(const SDataLoaderConfig& rhs) const;

    /// Argument to configure BLAST database data loader
    bool m_UseFixedSizeSlices;
private:
    /// Initialization method
    /// @param options configuration options [in]
    /// @param dbname name of BLAST database [in]
    /// @param load_proteins is this object going to load/read proteins only
    /// [in]
    void x_Init(EConfigOpts options, const string& dbname, bool load_proteins);

    /// Load the DATA_LOADERS configuration value from the config file
    void x_LoadDataLoadersConfig(const CNcbiRegistry& registry);

    /// Load the BLAST database configured to search for the blastdb
    /// DATA_LOADERS option from the config file
    void x_LoadBlastDbDataLoaderConfig(const CNcbiRegistry& registry);
};


/// Class whose purpose is to create CScope objects which have data loaders
/// added with different priorities, so that the BLAST database data loader is
/// used first, then the Genbank data loader. By default, this object tries to 
/// initialize the local BLAST database data loader first, if this cannot be
/// initialized, the remote BLAST database loader is initialized; then the
/// Genbank data loader is initialized.
/// The selection of data loaders can be configured via the SDataLoaderConfig
/// object and the DATA_LOADERS entry of the BLAST section of an NCBI
/// configuration file, the latter setting trumping the selection of the
/// SDataLoaderConfig object.
/// @note all data loaders are registered as non-default data loaders
class NCBI_BLASTINPUT_EXPORT CBlastScopeSource : public CObject 
{
public:
    /// Convenience typedef
    typedef CBlastDbDataLoader::EDbType EDbType;

    /// Constructor which only registers the Genbank data loader
    /// @param load_proteins is this object going to load/read proteins only
    /// [in]
    CBlastScopeSource(bool load_proteins = true,
                      CObjectManager* objmgr = NULL);

    /// Constructor with explicit data loader configuration object
    CBlastScopeSource(const SDataLoaderConfig& config,
                      CObjectManager* objmgr = NULL);

    /// Constructor which registers the specified BLAST database handle and 
    /// Genbank data loaders
    CBlastScopeSource(CRef<CSeqDB> db_handle,
                      CObjectManager* objmgr = NULL);

    /// Create a new, properly configured CScope
    CRef<objects::CScope> NewScope();

    /// Add the data loader configured in the object to the provided scope.
    /// Use when the scope already has some sequences loaded in it
    /// @param scope scope to add the data loaders to [in|out]
    /// @throw Null pointer exception if scope is NULL
    void AddDataLoaders(CRef<objects::CScope> scope);

    /// Removes the BLAST database data loader from the object manager.
    void RevokeBlastDbDataLoader();

    /// Retrieves the BLAST database data loader name initialized by this
    /// instance
    string GetBlastDbLoaderName() const { return m_BlastDbLoaderName; }

private:
    /// Our reference to the object manager
    CRef<objects::CObjectManager> m_ObjMgr;
    /// The configuration for this object
    SDataLoaderConfig m_Config;
    /// Name of the BLAST database data loader
    string m_BlastDbLoaderName;
    /// Name of the Genbank data loader
    string m_GbLoaderName;

    /// Initializes the BLAST database data loader
    /// @param dbname name of the BLAST database [in]
    /// @param dbtype molecule type of the database above [in]
    void x_InitBlastDatabaseDataLoader(const string& dbname, EDbType dbtype);
    /// Initializes the BLAST database data loader
    /// @param db_handle Handle to a BLAST database [in]
    void x_InitBlastDatabaseDataLoader(CRef<CSeqDB> db_handle);
    /// Initialize the Genbank data loader
    void x_InitGenbankDataLoader();

    /// Data loader priority for BLAST database data loader (if multiple BLAST
    /// database data loaders are registered, they are added in decreasing
    /// priority)
    static const int kBlastDbLoaderPriority = 80;
    /// Data loader priority for Genbank data loader
    static const int kGenbankLoaderPriority = 99;
};

END_SCOPE(blast)
END_NCBI_SCOPE

#endif /* ALGO_BLAST_BLASTINPUT___BLAST_SCOPE_SRC__HPP */
