/* $Id: entrez2_client.cpp 607107 2020-04-30 12:37:23Z grichenk $
 * ===========================================================================
 *
 *                            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:  Josh Cherry
 *
 * File Description:
 *   Entrez2 network client
 *
 * Remark:
 *   This code was originally generated by application DATATOOL
 *   using specifications from the data definition file
 *   'entrez2.asn'.
 */

// standard includes

// generated includes

#include <ncbi_pch.hpp>
#include <objects/entrez2/entrez2_client.hpp>
#include <objects/entrez2/Entrez2_boolean_element.hpp>
#include <objects/entrez2/Entrez2_boolean_exp.hpp>
#include <objects/entrez2/Entrez2_boolean_reply.hpp>
#include <objects/entrez2/Entrez2_db_id.hpp>
#include <objects/entrez2/Entrez2_eval_boolean.hpp>
#include <objects/entrez2/Entrez2_get_links.hpp>
#include <objects/entrez2/Entrez2_hier_query.hpp>
#include <objects/entrez2/Entrez2_id.hpp>
#include <objects/entrez2/Entrez2_id_list.hpp>
#include <objects/entrez2/Entrez2_limits.hpp>
#include <objects/entrez2/Entrez2_link_id.hpp>
#include <objects/entrez2/Entrez2_term_pos.hpp>
#include <objects/entrez2/Entrez2_term_query.hpp>

#include <algorithm>

// generated classes

BEGIN_NCBI_SCOPE

BEGIN_objects_SCOPE // namespace ncbi::objects::

// destructor
CEntrez2Client::~CEntrez2Client(void)
{
}


/// A simplified interface for getting neighbors (links)

/// This form just yields a vector of UIDs
void CEntrez2Client::GetNeighbors(TUid query_uid,
                                  const string& db,
                                  const string& link_type,
                                  vector<TUid>& neighbor_uids)
{
    vector<TUid> uids;
    uids.push_back(query_uid);
    GetNeighbors(uids, db, link_type, neighbor_uids);
}


/// This form just yields a vector of UIDs
void CEntrez2Client::GetNeighbors(const vector<TUid>& query_uids,
                                  const string& db,
                                  const string& link_type,
                                  vector<TUid>& neighbor_uids)
{
    // first retrieve the link_set
    CRef<CEntrez2_link_set> link_set;
    link_set = GetNeighbors(query_uids, db, link_type);
    
    // then extract the UIDs
    CEntrez2_id_list::TConstUidIterator it
        = link_set->GetIds().GetConstUidIterator();
    if (link_set->GetIds().IsSetNum()) {
        neighbor_uids.reserve(link_set->GetIds().GetNum());
    }
    for ( ;  !it.AtEnd();  ++it) {
        neighbor_uids.push_back(*it);
    }
}


/// This form returns the entire CEntrez2_link_set object,
/// which includes scores.
CRef<CEntrez2_link_set> CEntrez2Client::GetNeighbors(TUid query_uid,
                                                     const string& db,
                                                     const string& link_type)
{
    vector<TUid> uids;
    uids.push_back(query_uid);
    return GetNeighbors(uids, db, link_type);
}


/// This form returns the entire CEntrez2_link_set object,
/// which includes scores.
CRef<CEntrez2_link_set>
CEntrez2Client::GetNeighbors(const vector<TUid>& query_uids,
                             const string& db,
                             const string& link_type)
{
    CEntrez2_id_list uids;
    uids.SetDb() = CEntrez2_db_id(db);
    uids.AssignUids(query_uids);
    
    CEntrez2_get_links gl;
    gl.SetUids(uids);
    gl.SetLinktype().Set(db + "_" + link_type);

    CRef<CEntrez2_link_set> link_set = AskGet_links(gl);
    return link_set;
}


/// Retrieve counts of the various types of neighbors available
CRef<CEntrez2_link_count_list>
CEntrez2Client::GetNeighborCounts(TUid query_uid,
                                  const string& db)
{
    CEntrez2_id uid;
    uid.SetDb() = CEntrez2_db_id(db);
    uid.SetUid(ENTREZ_ID_FROM(TIntId, query_uid));
    return AskGet_link_counts(uid);
}


/// Query a db with a string, returning uids as integers
void CEntrez2Client::Query(const string& query,
                           const string& db,
                           vector<TUid>& result_uids,
                           size_t start,
                           size_t count,
                           TReply* reply)
{
    CRef<CEntrez2_boolean_element> bel(new CEntrez2_boolean_element);
    bel->SetStr(query);

    CEntrez2_boolean_exp bexp;
    bexp.SetDb().Set(db);
    bexp.SetExp().push_back(bel);

    // set some limits
    if (start > 0) {
        bexp.SetLimits().SetOffset_UIDs(start);
    }
    if (count > 0) {
        bexp.SetLimits().SetMax_UIDs(count);
    }

    CEntrez2_eval_boolean req;
    req.SetReturn_UIDs(true);
    req.SetQuery(bexp);

    CRef<CEntrez2_boolean_reply> bool_reply = AskEval_boolean(req, reply);
    
    // now extract the UIDs
    if (!bool_reply->GetUids().CanGetUids()) {
        // this happens when no matches were found
        return;
    }
    for (CEntrez2_id_list::TConstUidIterator it
             = bool_reply->GetUids().GetConstUidIterator();  
         !it.AtEnd();  ++it) {
        result_uids.push_back(*it);
    }
}


/// Given some uids, a database, and an entrez query string,
/// determine which of these uids match the query string.
///
/// Note: If a uid appears more than once in query_uids and
/// matches the query string, it may or may not appear more
/// more than once in the result.
void CEntrez2Client::FilterIds(const vector<TUid>& query_uids,
                               const string& db,
                               const string& query_string,
                               vector<TUid>& result_uids)
{
    const unsigned int kMaxIdsInQueryString = 2500;

    if (query_uids.empty()) {
        return;
    }

    if (query_uids.size() <= kMaxIdsInQueryString) {
        // Query with a big query string that includes
        // all the query_uids OR'd together
        string uids;
        ITERATE (vector<TUid>, uid, query_uids) {
            if ( !uids.empty() ) {
                uids += " OR ";
            }
            uids += NStr::NumericToString(*uid) + "[UID]";
        }

        string whole_query = "(" + query_string + ") AND (" + uids + ")";
        Query(whole_query, db, result_uids);
    } else {
        // Break query_uids into chunks <= kMaxIdsInQueryString
        vector<TUid> subset_query_uids;
        subset_query_uids.reserve(kMaxIdsInQueryString);
        for (size_t start = 0;  start < query_uids.size();
             start += kMaxIdsInQueryString) {
            subset_query_uids.clear();
            // end is one past the last index of interest
            size_t end =
                min(start + kMaxIdsInQueryString, query_uids.size());
            for (size_t i = start;  i < end;  ++i) {
                subset_query_uids.push_back(query_uids[i]);
            }
            // This relies on the fact that FilterIds appends to result
            FilterIds(subset_query_uids, db, query_string, result_uids);
        }
    }
}


CRef<CEntrez2_docsum_list>
CEntrez2Client::GetDocsums(const vector<TUid>& uids,
                           const string& db)
{
    CEntrez2_id_list ids;
    ids.AssignUids(uids);
    ids.SetDb().Set(db);
    return AskGet_docsum(ids);
}


/// Retrieve the docsums for a single UID
CRef<CEntrez2_docsum_list>
CEntrez2Client::GetDocsums(TUid uid, const string& db)
{
    vector<TUid> uids;
    uids.push_back(uid);
    return GetDocsums(uids, db);
}


string
CEntrez2Client::GetAffinity(const CEntrez2_request& request) const
{
    const CE2Request&     e2req = request.GetRequest();
    const CEntrez2_db_id* db    = 0;

    switch (e2req.Which()) {
    case CE2Request::e_Eval_boolean:
        db = &e2req.GetEval_boolean().GetQuery().GetDb();
        break;
    case CE2Request::e_Get_docsum:
        db = &e2req.GetGet_docsum().GetDb();
        break;
    case CE2Request::e_Get_term_pos:
        db = &e2req.GetGet_term_pos().GetDb();
        break;
    case CE2Request::e_Get_term_list:
        db = &e2req.GetGet_term_list().GetDb();
        break;
    case CE2Request::e_Get_term_hierarchy:
        db = &e2req.GetGet_term_hierarchy().GetDb();
        break;
    case CE2Request::e_Get_links:
        db = &e2req.GetGet_links().GetUids().GetDb();
        break;
    case CE2Request::e_Get_linked:
        db = &e2req.GetGet_linked().GetUids().GetDb();
        break;
    case CE2Request::e_Get_link_counts:
        db = &e2req.GetGet_link_counts().GetDb();
        break;
    default:
        break;
    }

    if (db  &&  !db->Get().empty()) {
        return "DB=" + db->Get();
    } else {
        return kEmptyStr;
    }
}


#ifdef NCBI_STRICT_GI

void CEntrez2Client::GetNeighbors(TGi query_uid,
                                  const string& db_from,
                                  const string& db_to,
                                  vector<TGi>& neighbor_uids)
{
    vector<TUid> vi_neighbor_uids;
    GetNeighbors(GI_TO(TUid, query_uid), db_from, db_to, vi_neighbor_uids);
    ITERATE(vector<TUid>, it, vi_neighbor_uids) {
        neighbor_uids.push_back(GI_FROM(TUid, *it));
    }
}

void CEntrez2Client::GetNeighbors(const vector<TGi>& query_uids,
                                  const string& db,
                                  const string& link_type,
                                  vector<TGi>& neighbor_uids)
{
    vector<TUid> vi_query_uids;
    ITERATE(vector<TGi>, it, query_uids) {
        vi_query_uids.push_back(GI_TO(TUid, *it));
    }
    vector<TUid> vi_neighbor_uids;
    GetNeighbors(vi_query_uids, db, link_type, vi_neighbor_uids);
    ITERATE(vector<TUid>, it, vi_neighbor_uids) {
        neighbor_uids.push_back(GI_FROM(TUid, *it));
    }
}

CRef<CEntrez2_link_set> CEntrez2Client::GetNeighbors(TGi query_uid,
                                                     const string& db_from,
                                                     const string& db_to)
{
    return GetNeighbors(GI_TO(TUid, query_uid), db_from, db_to);
}

CRef<CEntrez2_link_set> CEntrez2Client::GetNeighbors(const vector<TGi>& query_uids,
                                                     const string& db_from,
                                                     const string& db_to)
{
    vector<TUid> vi_query_uids;
    ITERATE(vector<TGi>, it, query_uids) {
        vi_query_uids.push_back(GI_TO(TUid, *it));
    }
    return GetNeighbors(vi_query_uids, db_from, db_to);
}

CRef<CEntrez2_link_count_list> CEntrez2Client::GetNeighborCounts(TGi query_uid,
                                                                 const string& db)
{
    return GetNeighborCounts(GI_TO(TUid, query_uid), db);
}

void CEntrez2Client::Query(const string& query,
                           const string& db,
                           vector<TGi>& result_uids,
                           size_t start_offs,
                           size_t count,
                           TReply* reply)
{
    vector<TUid> vi_result_uids;
    Query(query, db, vi_result_uids, start_offs, count, reply);
    ITERATE(vector<TUid>, it, vi_result_uids) {
        result_uids.push_back(GI_FROM(TUid, *it));
    }
}

void CEntrez2Client::FilterIds(const vector<TGi>& query_uids,
                               const string& db,
                               const string& query_string,
                               vector<TGi>& result_uids)
{
    vector<TUid> vi_query_uids;
    ITERATE(vector<TGi>, it, query_uids) {
        vi_query_uids.push_back(GI_TO(TUid, *it));
    }
    vector<TUid> vi_result_uids;
    FilterIds(vi_query_uids, db, query_string, vi_result_uids);
    ITERATE(vector<TUid>, it, vi_result_uids) {
        result_uids.push_back(GI_FROM(TUid, *it));
    }
}

CRef<CEntrez2_docsum_list> CEntrez2Client::GetDocsums(const vector<TGi>& uids,
                                                      const string& db)
{
    vector<TUid> vi_uids;
    ITERATE(vector<TGi>, it, uids) {
        vi_uids.push_back(GI_TO(TUid, *it));
    }
    return GetDocsums(vi_uids, db);
}

CRef<CEntrez2_docsum_list> CEntrez2Client::GetDocsums(TGi uid,
                                                      const string& db)
{
    return GetDocsums(GI_TO(TUid, uid), db);
}

#endif

END_objects_SCOPE // namespace ncbi::objects::

END_NCBI_SCOPE

/* Original file checksum: lines: 64, chars: 1896, CRC32: cd6a8df4 */
