#include <ncbi_pch.hpp>

#include <objects/pub/Pub.hpp>
#include <objects/pub/Pub_equiv.hpp>

#include <objects/biblio/Author.hpp>
#include <objects/biblio/Auth_list.hpp>
#include <objects/biblio/PubMedId.hpp>
#include <objects/biblio/Cit_art.hpp>
#include <objects/biblio/Cit_jour.hpp>
#include <objects/biblio/Cit_book.hpp>
#include <objects/biblio/Cit_proc.hpp>
#include <objects/biblio/Cit_let.hpp>
#include <objects/biblio/Cit_sub.hpp>
#include <objects/biblio/Cit_pat.hpp>
#include <objects/biblio/Cit_let.hpp>
#include <objects/biblio/Id_pat.hpp>
#include <objects/biblio/Cit_gen.hpp>
#include <objects/biblio/Imprint.hpp>
#include <objects/general/Date_std.hpp>
#include <objects/general/Name_std.hpp>
#include <objects/general/Person_id.hpp>
#include <objects/medline/Medline_entry.hpp>

#include <objects/pub/pub_macros.hpp>

BEGIN_NCBI_SCOPE

BEGIN_objects_SCOPE 

CPub::~CPub(void)
{
}

bool CPub::GetLabel(string*        label,
                    ELabelType     type,
                    TLabelFlags    flags,
                    ELabelVersion  version) const
{
    static const char* s_PubTypes[14] = {
        "Unknown",
        "Generic",
        "Submit",
        "Medline",
        "MUID",
        "Article",
        "Journal",
        "Book",
        "Proceedings",
        "Patent",
        "PatID",
        "Manuscript",
        "Equiv",
        "PMID" };

    if (!label) {
        return false;
    }

    int idx = static_cast<int>(Which());
    idx = idx >= 0 && idx < 14 ? idx : 0;

    if (type == eType) {

        *label += s_PubTypes[idx];
        return true;
    }

    if (type == eBoth) {

        *label += string(s_PubTypes[idx]) + ": ";
    }

    switch (Which()) {
    case e_Muid:
        *label += "NLM" + NStr::NumericToString(GetMuid());
        return true;
    case e_Pmid:
        *label += "PM" + NStr::NumericToString(GetPmid().Get());
        return true;
    case e_Equiv:
        return GetEquiv().GetLabel(label, flags, version);
    case e_Medline:
        return GetMedline().GetLabel(label, flags, version);
    case e_Article:
        return GetArticle().GetLabel(label, flags, version);
    case e_Journal:
        return GetJournal().GetLabel(label, flags, version);
    case e_Book:
        return GetBook().GetLabel(label, flags, version);
    case e_Proc:
        return GetProc().GetLabel(label, flags, version);
    case e_Man:
        return GetMan().GetLabel(label, flags, version);
    case e_Sub:
        return GetSub().GetLabel(label, flags, version);
    case e_Patent:
        return GetPatent().GetLabel(label, flags, version);
    case e_Pat_id:
        return GetPat_id().GetLabel(label, flags, version);
    case e_Gen:
        return GetGen().GetLabel(label, flags, version);
    default:
        return false;
    }
}

bool CPub::IsSetAuthors(void) const
{
    switch (Which()) {
    case CPub::e_Gen :
        return (GetGen().IsSetAuthors());
    case CPub::e_Sub :
        return (GetSub().IsSetAuthors());
    case CPub::e_Article :
        return (GetArticle().IsSetAuthors());
    case CPub::e_Book :
        return (GetBook().IsSetAuthors());
    case CPub::e_Proc :
        return (GetProc().IsSetBook() && GetProc().GetBook().IsSetAuthors());
    case CPub::e_Patent :
        return (GetPatent().IsSetAuthors());
    case CPub::e_Man :
        return (GetMan().IsSetCit() && GetMan().GetCit().IsSetAuthors());
    default :
      break;
  }

  return false;
}

const CAuth_list& CPub::GetAuthors (void) const
{
  switch (Which()) {
    case CPub::e_Gen :
        return (GetGen().GetAuthors());
    case CPub::e_Sub :
        return (GetSub().GetAuthors());
    case CPub::e_Article :
        return (GetArticle().GetAuthors());
    case CPub::e_Book :
        return (GetBook().GetAuthors());
    case CPub::e_Proc :
        return (GetProc().GetBook().GetAuthors());
    case CPub::e_Patent :
        return (GetPatent().GetAuthors());
    case CPub::e_Man :
        return (GetMan().GetCit().GetAuthors());
    default :
        NCBI_THROW(CSerialException, eNotImplemented,
                   "CPub::GetAuthors: unsupported entry type "
                   + SelectionName(Which()));
  }
}

CAuth_list& CPub::SetAuthors (void)
{
  switch (Which()) {
    case CPub::e_Gen :
        return (SetGen().SetAuthors());
    case CPub::e_Sub :
        return (SetSub().SetAuthors());
    case CPub::e_Article :
        return (SetArticle().SetAuthors());
    case CPub::e_Book :
        return (SetBook().SetAuthors());
    case CPub::e_Proc :
        return (SetProc().SetBook().SetAuthors());
    case CPub::e_Patent :
        return (SetPatent().SetAuthors());
    case CPub::e_Man :
        return (SetMan().SetCit().SetAuthors());
    default :
        NCBI_THROW(CSerialException, eNotImplemented,
                   "CPub::SetAuthors: unsupported entry type "
                   + SelectionName(Which()));
  }
}

void CPub::GetTitles(
    TOneTitleRefVec & out_title,
    size_t iMaxToGet ) const
{

    if( iMaxToGet <= 0 ) {
        return;
    }

    switch( Which() ) {
    case CPub::e_not_set:
    case CPub::e_Medline:
    case CPub::e_Pmid:
    case CPub::e_Pat_id:

        break;
    case CPub::e_Gen:
        if( GetGen().IsSetTitle() ) {
            out_title.push_back( 
                xs_GetTitleFromPlainString(
                GetGen().GetTitle()) );
        }
        break;
    case CPub::e_Sub:
        if( GetSub().IsSetDescr() ) {
            out_title.push_back( 
                xs_GetTitleFromPlainString(
                GetSub().GetDescr()) );
        }
        break;
    case CPub::e_Article:
        if( GetArticle().IsSetTitle() && GetArticle().GetTitle().IsSet() ) {
            xs_AppendTitles( out_title, iMaxToGet, GetArticle().GetTitle() );
        }
        break;
    case CPub::e_Journal:
        if( GetJournal().IsSetTitle() ) {
            xs_AppendTitles( out_title, iMaxToGet, GetJournal().GetTitle() );
        }
        break;
    case CPub::e_Book:
        if( GetBook().IsSetTitle() ) {
            xs_AppendTitles( out_title, iMaxToGet, GetBook().GetTitle() );
        }
        break;
    case CPub::e_Proc:

        if( FIELD_CHAIN_OF_2_IS_SET(GetProc(), Book, Title) ) {
            xs_AppendTitles( out_title, iMaxToGet, 
                GetProc().GetBook().GetTitle() );
        }
        break;
    case CPub::e_Patent:
        if( GetPatent().IsSetTitle() ) {
            out_title.push_back( 
                xs_GetTitleFromPlainString(
                GetPatent().GetTitle()) );
        }
        break;
    case CPub::e_Man:
        if( FIELD_CHAIN_OF_2_IS_SET(GetMan(), Cit, Title) ) {
            xs_AppendTitles( out_title, iMaxToGet, 
                GetMan().GetCit().GetTitle() );
        }
        break;
    case CPub::e_Equiv:
        {
            size_t iMaxTitleSizeAllowed = ( out_title.size() + iMaxToGet );
            if( iMaxTitleSizeAllowed < out_title.size() ) {

                iMaxTitleSizeAllowed = 
                    std::numeric_limits<std::size_t>::max();
            }
            FOR_EACH_PUB_ON_PUBEQUIV(pub_it, GetEquiv()) {
                if( out_title.size() >= iMaxTitleSizeAllowed ) {
                    break;
                }

                (*pub_it)->GetTitles(out_title, 
                    (iMaxTitleSizeAllowed - out_title.size()) );
            }
        }
        break;
    default:
        NCBI_THROW(CException, eUnknown, "unhandled pub type");
    }
}

CPub::TOneTitleRef 
CPub::xs_GetTitleFromPlainString(const string & sTitle)
{
    CRef<CTitle::C_E> pRval( new CTitle::C_E );
    pRval->SetName( sTitle );
    return pRval;
}

void CPub::xs_AppendTitles( TOneTitleRefVec & out_title,
        size_t iMaxToGet, 
        const CTitle & in_title )
{
    if( iMaxToGet <= 0 ) {
        return;
    }

    if( ! in_title.IsSet() ) {
        return;
    }

    size_t iNumCopiedSoFar = 0;
    CTitle::Tdata::const_iterator src_it = in_title.Get().begin();
    CTitle::Tdata::const_iterator src_it_end = in_title.Get().end();
    for( ; src_it != src_it_end && iNumCopiedSoFar < iMaxToGet; 
        ++src_it, ++iNumCopiedSoFar) 
    {
        out_title.push_back( *src_it );
    }
}

typedef struct {
    string country;
    string number;
    string app_number;
    TEntrezId muid;
    TEntrezId pmid;
    CRef<CCit_art> art;
} SPubMatchInfo;

string s_GetTitleString(const CTitle::C_E& title) 
{
    string str = "";

    switch (title.Which()) {
        case CTitle::C_E::e_Name:
            str = title.GetName();
            break;
        case CTitle::C_E::e_Abr:
            str = title.GetAbr();
            break;
        case CTitle::C_E::e_Coden:
            str = title.GetCoden();
            break;
        case CTitle::C_E::e_Isbn:
            str = title.GetIsbn();
            break;
        case CTitle::C_E::e_Iso_jta:
            str = title.GetIso_jta();
            break;
        case CTitle::C_E::e_Issn:
            str = title.GetIssn();
            break;
        case CTitle::C_E::e_Jta:
            str = title.GetJta();
            break;
        case CTitle::C_E::e_Ml_jta:
            str = title.GetMl_jta();
            break;
        case CTitle::C_E::e_Trans:
            str = title.GetTrans();
            break;
        case CTitle::C_E::e_Tsub:
            str = title.GetTsub();
            break;
        case CTitle::C_E::e_not_set:
            break;
    }
    return str;
}

bool s_TitleMatch(const CTitle& title1, const CTitle& title2, CTitle::C_E::E_Choice title_type)
{
    string compare1 = "";
    string compare2 = "";

    ITERATE(CTitle::Tdata, it, title1.Get()) {
        if ((*it)->Which() == title_type) {
            compare1 = s_GetTitleString(**it);
            break;
        }
    }
    ITERATE(CTitle::Tdata, it, title2.Get()) {
        if ((*it)->Which() == title_type) {
            compare2 = s_GetTitleString(**it);
            break;
        }
    }
    if (!NStr::IsBlank(compare1) && NStr::EqualNocase(compare1, compare2)) {
        return true;
    } else {
        return false;
    }
}

#define FIELD(Obj,Field) (Obj.IsSet##Field() ? Obj.Get##Field() : "")
#define FIELD_MATCH(Obj1,Obj2,Field) (NStr::EqualNocase(FIELD(Obj1,Field), FIELD(Obj2,Field)))
#define INT_FIELD_MATCH(Obj1,Obj2,Field) ((Obj1.IsSet##Field() && Obj2.IsSet##Field() && Obj1.Get##Field() == Obj2.Get##Field()) || (!Obj1.IsSet##Field() && !Obj2.IsSet##Field()))

#define S_MATCH(Obj1,Obj2,Field) ((!Obj1.IsSet##Field() && !Obj2.IsSet##Field()) || (Obj1.IsSet##Field() && Obj2.IsSet##Field() && s_Match##Field(Obj1.Get##Field(),Obj2.Get##Field())))
#define S_MATCH_A(Obj1,Obj2,Field) ((!Obj1.IsSet##Field() && !Obj2.IsSet##Field()) || (Obj1.IsSet##Field() && Obj2.IsSet##Field() && Obj1.Get##Field().SameCitation(Obj2.Get##Field())))

bool s_MatchDate(const CDate& date1, const CDate& date2)
{
    return date1.Equals(date2);
}

bool s_MatchImp(const CImprint& imp1, const CImprint& imp2)
{
    if (!S_MATCH(imp1, imp2, Date)) {
        return false;
    }

    if (!FIELD_MATCH(imp1, imp2, Volume) ||
        !FIELD_MATCH(imp1, imp2, Issue) ||
        !FIELD_MATCH(imp1, imp2, Pages) ||
        !FIELD_MATCH(imp1, imp2, Section) ||
        !FIELD_MATCH(imp1, imp2, Part_sup) ||
        !FIELD_MATCH(imp1, imp2, Language)) {
        return false;
    }

    if (imp1.IsSetCprt() && imp2.IsSetCprt() && !imp1.GetCprt().Equals(imp2.GetCprt())) {
        return false;
    }
    return true;
}

static const CTitle::C_E::E_Choice s_MatchJournalTypes[] = {
    CTitle::C_E::e_Iso_jta,
    CTitle::C_E::e_Ml_jta,
    CTitle::C_E::e_Coden,
    CTitle::C_E::e_Issn,
    CTitle::C_E::e_Name,
    CTitle::C_E::e_Jta,
    CTitle::C_E::e_not_set
};

bool s_MatchTitle(const CTitle& title1, const CTitle& title2)
{
    bool match_title = false;

    for (size_t i = 0; !match_title && s_MatchJournalTypes[i] != CTitle::C_E::e_not_set; i++) {
        match_title = s_TitleMatch(title1, title2, s_MatchJournalTypes[i]);
    }

    return match_title;
}

bool s_JournalMatch(const CCit_jour& jour1, const CCit_jour& jour2)
{

    if (!S_MATCH(jour1, jour2, Title)) {
        return false;
    }

    return S_MATCH(jour1, jour2, Imp);
}

bool s_MatchBook(const CCit_book& book1, const CCit_book& book2)
{
    if (!S_MATCH_A(book1, book2, Authors)) {
        return false;
    }

    if (!S_MATCH(book1, book2, Imp)) {
        return false;
    }

    if ((!book1.IsSetTitle() && book2.IsSetTitle()) ||
        (book1.IsSetTitle() && !book2.IsSetTitle()) ||
        !s_TitleMatch(book1.GetTitle(), book2.GetTitle(), CTitle::C_E::e_Name)) {
        return false;
    }

    return true;
}

bool s_ProcMatch(const CCit_proc& book1, const CCit_proc& book2)
{
    return S_MATCH(book1, book2, Book);
}

bool s_ManMatch(const CCit_let& book1, const CCit_let& book2)
{
    return ((!book1.IsSetCit() && !book2.IsSetCit()) ||
            (book1.IsSetCit() && book2.IsSetCit() && s_MatchBook(book1.GetCit(), book2.GetCit())));
}

bool s_CitArtMatch(const CCit_art& art1, const CCit_art& art2)
{
    if (!art1.IsSetFrom() || !art2.IsSetFrom()) {
        return false;
    }
    if (art1.GetFrom().Which() != art2.GetFrom().Which()) {
        return false;
    }

    if (art1.GetFrom().IsJournal()) {

        if (!s_JournalMatch(art1.GetFrom().GetJournal(), art2.GetFrom().GetJournal())) {
            return false;
        }
    } else if (art1.GetFrom().IsBook()) {

        if (!s_MatchBook(art1.GetFrom().GetBook(), art2.GetFrom().GetBook())) {
            return false;
        }
    } else if (art1.GetFrom().IsProc()) {

        if (!s_ProcMatch(art1.GetFrom().GetProc(), art2.GetFrom().GetProc())) {
            return false;
        }
    }

    if (!S_MATCH_A(art1, art2, Authors)) {
        return false;
    }

    if (!art1.IsSetTitle() && !art2.IsSetTitle()) {

    } else if (!art1.IsSetTitle() || !art2.IsSetTitle()) {
        return false;
    } else if (!s_TitleMatch(art1.GetTitle(), art2.GetTitle(), CTitle::C_E::e_Name)) {
        return false;
    }

    return true;
}

bool s_CitGenMatch(const CCit_gen& gen1, const CCit_gen& gen2)
{
    if (!FIELD_MATCH(gen1, gen2, Volume) ||
        !FIELD_MATCH(gen1, gen2, Issue) ||
        !FIELD_MATCH(gen1, gen2, Pages) ||
        !FIELD_MATCH(gen1, gen2, Title) ||
        !FIELD_MATCH(gen1, gen2, Cit)) {
        return false;
    }
    if (!S_MATCH_A(gen1, gen2, Authors)) {
        return false;
    }

    if (!INT_FIELD_MATCH(gen1, gen2, Muid) ||
        !INT_FIELD_MATCH(gen1, gen2, Serial_number)) {
        return false;
    }

    if (!gen1.IsSetJournal() && !gen2.IsSetJournal()) {

    } else if (!gen1.IsSetJournal() || !gen2.IsSetJournal()) {
        return false;
    } else if (!s_MatchTitle(gen1.GetJournal(), gen2.GetJournal())) {
        return false;
    }

    if (!S_MATCH(gen1, gen2, Date)) {
        return false;
    }

	return true;

}

bool s_CitSubMatch(const CCit_sub& sub1, const CCit_sub& sub2)
{
    if (!S_MATCH_A(sub1, sub2, Authors)) {
        return false;
    }

    bool rval = false;

    if (sub1.IsSetImp() && sub2.IsSetImp()) {
        rval = s_MatchImp(sub1.GetImp(), sub2.GetImp());
    } else {

        CConstRef<CDate> date1(NULL);
        CConstRef<CDate> date2(NULL);
        if (sub1.IsSetImp() && sub1.GetImp().IsSetDate()) {
            date1.Reset(&(sub1.GetImp().GetDate()));
        } else if (sub1.IsSetDate()) {
            date1.Reset(&(sub1.GetDate()));
        }
        if (sub2.IsSetImp() && sub2.GetImp().IsSetDate()) {
            date2.Reset(&(sub2.GetImp().GetDate()));
        } else if (sub2.IsSetDate()) {
            date2.Reset(&(sub2.GetDate()));
        }
        if (!date1 && !date2) {
            rval = true;
        } else if (!date1 || !date2) {
            rval = false;
        } else {
            rval = date1->Equals(*date2);
        }
	}
    return rval;
}

bool s_MatchInfoMatches(const SPubMatchInfo& match1, const SPubMatchInfo& match2)
{
    if (match1.muid > ZERO_ENTREZ_ID && match2.muid > ZERO_ENTREZ_ID) {
        if (match1.muid == match2.muid) {
            return true;
        } 
    }

    if (match1.pmid > ZERO_ENTREZ_ID && match2.pmid > ZERO_ENTREZ_ID) {
        if (match1.pmid == match2.pmid) {
            return true;
        }
    }

    if (!NStr::IsBlank(match1.country) && !NStr::IsBlank(match2.country)) {
        if (NStr::EqualNocase(match1.country, match2.country) && 
            NStr::EqualNocase(match1.number, match2.number) &&
            NStr::EqualNocase(match1.app_number, match2.app_number)) {
            return true;
        }
    }

    if (match1.art && match2.art) {
        if (s_CitArtMatch(*(match1.art), *(match2.art))) {
            return true;
        }
    }

    return false;
}

void s_GetPubMatchInfo(const CCit_pat& patent, SPubMatchInfo& match)
{
    if (patent.IsSetCountry()) {
        match.country = patent.GetCountry();
    }
    if (patent.IsSetNumber()) {
        match.number = patent.GetNumber();
    }
    if (patent.IsSetApp_number()) {
        match.app_number = patent.GetApp_number();
    }
}

void s_GetPubMatchInfo(const CId_pat& patent, SPubMatchInfo& match)
{
    if (patent.IsSetCountry()) {
        match.country = patent.GetCountry();
    }
    if (patent.IsSetId()) {
        if (patent.GetId().IsNumber()) {
            match.number = patent.GetId().GetNumber();
        } else if (patent.GetId().IsApp_number()) {
            match.app_number = patent.GetId().GetApp_number();
        }
    }
}

void s_GetPubMatchInfo(const CPub& pub, SPubMatchInfo& match)
{
    match.muid = ZERO_ENTREZ_ID;
    match.pmid = ZERO_ENTREZ_ID;
    match.country = "";
    match.number = "";
    match.app_number = "";

    switch (pub.Which()) {
        case CPub::e_Pmid:
            match.pmid = pub.GetPmid();
            break;
        case CPub::e_Muid:
            match.muid = pub.GetMuid();
            break;
        case CPub::e_Patent:
            s_GetPubMatchInfo(pub.GetPatent(), match);
            break;
        case CPub::e_Pat_id:
            s_GetPubMatchInfo(pub.GetPat_id(), match);
            break;
        case CPub::e_Article:
            match.art = new CCit_art();
            match.art->Assign(pub.GetArticle());
            break;
        default:
            break;
    }
}

bool CPub::SameCitation(const CPub& other) const
{
    if (Which() == CPub::e_Equiv) {
        if (other.Which() == CPub::e_Equiv) {
            return GetEquiv().SameCitation(other.GetEquiv());
        } else {
            return GetEquiv().SameCitation(other);
        }
    }
    SPubMatchInfo match1, match2;

    s_GetPubMatchInfo(*this, match1);
    s_GetPubMatchInfo(other, match2);
    if (s_MatchInfoMatches(match1, match2)) {
        return true;
    }

    if (Which() != other.Which()) {
        return false;
    }

    bool rval = false;

    switch (Which()) {
        case CPub::e_Gen:
            rval = s_CitGenMatch(GetGen(), other.GetGen());
            break;
        case CPub::e_Sub:
            rval = s_CitSubMatch(GetSub(), other.GetSub());
            break;
        case CPub::e_Journal:
            rval = s_JournalMatch(GetJournal(), other.GetJournal());
            break;
        case CPub::e_Book:
            rval = s_MatchBook(GetBook(), other.GetBook());
            break;
        case CPub::e_Proc:
            rval = s_ProcMatch(GetProc(), other.GetProc());
            break;
        case CPub::e_Man:
            rval = s_ManMatch(GetMan(), other.GetMan());
            break;
        default:
            break;
    }
	return rval;
}

END_objects_SCOPE 

END_NCBI_SCOPE
/* $Id: Pub.cpp 607097 2020-04-30 12:32:13Z 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:  .......
 *
 * File Description:
 *   .......
 *
 * Remark:
 *   This code was originally generated by application DATATOOL
 *   using specifications from the ASN data definition file
 *   'pub.asn'.
 */

#include <ncbi_pch.hpp>

#include <objects/pub/Pub.hpp>
#include <objects/pub/Pub_equiv.hpp>

#include <objects/biblio/Author.hpp>
#include <objects/biblio/Auth_list.hpp>
#include <objects/biblio/PubMedId.hpp>
#include <objects/biblio/Cit_art.hpp>
#include <objects/biblio/Cit_jour.hpp>
#include <objects/biblio/Cit_book.hpp>
#include <objects/biblio/Cit_proc.hpp>
#include <objects/biblio/Cit_let.hpp>
#include <objects/biblio/Cit_sub.hpp>
#include <objects/biblio/Cit_pat.hpp>
#include <objects/biblio/Cit_let.hpp>
#include <objects/biblio/Id_pat.hpp>
#include <objects/biblio/Cit_gen.hpp>
#include <objects/biblio/Imprint.hpp>
#include <objects/general/Date_std.hpp>
#include <objects/general/Name_std.hpp>
#include <objects/general/Person_id.hpp>
#include <objects/medline/Medline_entry.hpp>

#include <objects/pub/pub_macros.hpp>

// generated classes

BEGIN_NCBI_SCOPE

BEGIN_objects_SCOPE // namespace ncbi::objects::

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


// Appends a label to "label"
bool CPub::GetLabel(string*        label,
                    ELabelType     type,
                    TLabelFlags    flags,
                    ELabelVersion  version) const
{
    static const char* s_PubTypes[14] = {
        "Unknown",
        "Generic",
        "Submit",
        "Medline",
        "MUID",
        "Article",
        "Journal",
        "Book",
        "Proceedings",
        "Patent",
        "PatID",
        "Manuscript",
        "Equiv",
        "PMID" };

    // Check that label exists
    if (!label) {
        return false;
    }

    // Get the index into the s_PubTypes array corresponding to pub type
    int idx = static_cast<int>(Which());
    idx = idx >= 0 && idx < 14 ? idx : 0;

    if (type == eType) {
        // Append pub type to label and return
        *label += s_PubTypes[idx];
        return true;
    }

    if (type == eBoth) {
        // Append pub type to label
        *label += string(s_PubTypes[idx]) + ": ";
    }

    switch (Which()) {
    case e_Muid:
        *label += "NLM" + NStr::NumericToString(GetMuid());
        return true;
    case e_Pmid:
        *label += "PM" + NStr::NumericToString(GetPmid().Get());
        return true;
    case e_Equiv:
        return GetEquiv().GetLabel(label, flags, version);
    case e_Medline:
        return GetMedline().GetLabel(label, flags, version);
    case e_Article:
        return GetArticle().GetLabel(label, flags, version);
    case e_Journal:
        return GetJournal().GetLabel(label, flags, version);
    case e_Book:
        return GetBook().GetLabel(label, flags, version);
    case e_Proc:
        return GetProc().GetLabel(label, flags, version);
    case e_Man:
        return GetMan().GetLabel(label, flags, version);
    case e_Sub:
        return GetSub().GetLabel(label, flags, version);
    case e_Patent:
        return GetPatent().GetLabel(label, flags, version);
    case e_Pat_id:
        return GetPat_id().GetLabel(label, flags, version);
    case e_Gen:
        return GetGen().GetLabel(label, flags, version);
    default:
        return false;
    }
}


bool CPub::IsSetAuthors(void) const
{
    switch (Which()) {
    case CPub::e_Gen :
        return (GetGen().IsSetAuthors());
    case CPub::e_Sub :
        return (GetSub().IsSetAuthors());
    case CPub::e_Article :
        return (GetArticle().IsSetAuthors());
    case CPub::e_Book :
        return (GetBook().IsSetAuthors());
    case CPub::e_Proc :
        return (GetProc().IsSetBook() && GetProc().GetBook().IsSetAuthors());
    case CPub::e_Patent :
        return (GetPatent().IsSetAuthors());
    case CPub::e_Man :
        return (GetMan().IsSetCit() && GetMan().GetCit().IsSetAuthors());
    default :
      break;
  }
  
  return false;
}

const CAuth_list& CPub::GetAuthors (void) const
{
  switch (Which()) {
    case CPub::e_Gen :
        return (GetGen().GetAuthors());
    case CPub::e_Sub :
        return (GetSub().GetAuthors());
    case CPub::e_Article :
        return (GetArticle().GetAuthors());
    case CPub::e_Book :
        return (GetBook().GetAuthors());
    case CPub::e_Proc :
        return (GetProc().GetBook().GetAuthors());
    case CPub::e_Patent :
        return (GetPatent().GetAuthors());
    case CPub::e_Man :
        return (GetMan().GetCit().GetAuthors());
    default :
        NCBI_THROW(CSerialException, eNotImplemented,
                   "CPub::GetAuthors: unsupported entry type "
                   + SelectionName(Which()));
  }
}

CAuth_list& CPub::SetAuthors (void)
{
  switch (Which()) {
    case CPub::e_Gen :
        return (SetGen().SetAuthors());
    case CPub::e_Sub :
        return (SetSub().SetAuthors());
    case CPub::e_Article :
        return (SetArticle().SetAuthors());
    case CPub::e_Book :
        return (SetBook().SetAuthors());
    case CPub::e_Proc :
        return (SetProc().SetBook().SetAuthors());
    case CPub::e_Patent :
        return (SetPatent().SetAuthors());
    case CPub::e_Man :
        return (SetMan().SetCit().SetAuthors());
    default :
        NCBI_THROW(CSerialException, eNotImplemented,
                   "CPub::SetAuthors: unsupported entry type "
                   + SelectionName(Which()));
  }
}

void CPub::GetTitles(
    TOneTitleRefVec & out_title,
    size_t iMaxToGet ) const
{
    // this "if" lets us assume below this point that 
    // we have room for at least one 
    if( iMaxToGet <= 0 ) {
        return;
    }

    switch( Which() ) {
    case CPub::e_not_set:
    case CPub::e_Medline:
    case CPub::e_Pmid:
    case CPub::e_Pat_id:
        // these types don't have titles, so nothing to do
        break;
    case CPub::e_Gen:
        if( GetGen().IsSetTitle() ) {
            out_title.push_back( 
                xs_GetTitleFromPlainString(
                GetGen().GetTitle()) );
        }
        break;
    case CPub::e_Sub:
        if( GetSub().IsSetDescr() ) {
            out_title.push_back( 
                xs_GetTitleFromPlainString(
                GetSub().GetDescr()) );
        }
        break;
    case CPub::e_Article:
        if( GetArticle().IsSetTitle() && GetArticle().GetTitle().IsSet() ) {
            xs_AppendTitles( out_title, iMaxToGet, GetArticle().GetTitle() );
        }
        break;
    case CPub::e_Journal:
        if( GetJournal().IsSetTitle() ) {
            xs_AppendTitles( out_title, iMaxToGet, GetJournal().GetTitle() );
        }
        break;
    case CPub::e_Book:
        if( GetBook().IsSetTitle() ) {
            xs_AppendTitles( out_title, iMaxToGet, GetBook().GetTitle() );
        }
        break;
    case CPub::e_Proc:
        // what to do here?  It has a book and meeting
        // It's not entirely clear if this is the best course of action
        if( FIELD_CHAIN_OF_2_IS_SET(GetProc(), Book, Title) ) {
            xs_AppendTitles( out_title, iMaxToGet, 
                GetProc().GetBook().GetTitle() );
        }
        break;
    case CPub::e_Patent:
        if( GetPatent().IsSetTitle() ) {
            out_title.push_back( 
                xs_GetTitleFromPlainString(
                GetPatent().GetTitle()) );
        }
        break;
    case CPub::e_Man:
        if( FIELD_CHAIN_OF_2_IS_SET(GetMan(), Cit, Title) ) {
            xs_AppendTitles( out_title, iMaxToGet, 
                GetMan().GetCit().GetTitle() );
        }
        break;
    case CPub::e_Equiv:
        {
            size_t iMaxTitleSizeAllowed = ( out_title.size() + iMaxToGet );
            if( iMaxTitleSizeAllowed < out_title.size() ) {
                // integer overflowed
                iMaxTitleSizeAllowed = 
                    std::numeric_limits<std::size_t>::max();
            }
            FOR_EACH_PUB_ON_PUBEQUIV(pub_it, GetEquiv()) {
                if( out_title.size() >= iMaxTitleSizeAllowed ) {
                    break;
                }

                // dig down recursively
                (*pub_it)->GetTitles(out_title, 
                    (iMaxTitleSizeAllowed - out_title.size()) );
            }
        }
        break;
    default:
        NCBI_THROW(CException, eUnknown, "unhandled pub type");
    }
}

// static
CPub::TOneTitleRef 
CPub::xs_GetTitleFromPlainString(const string & sTitle)
{
    CRef<CTitle::C_E> pRval( new CTitle::C_E );
    pRval->SetName( sTitle );
    return pRval;
}

// static 
void CPub::xs_AppendTitles( TOneTitleRefVec & out_title,
        size_t iMaxToGet, 
        const CTitle & in_title )
{
    if( iMaxToGet <= 0 ) {
        return;
    }

    if( ! in_title.IsSet() ) {
        return;
    }

    size_t iNumCopiedSoFar = 0;
    CTitle::Tdata::const_iterator src_it = in_title.Get().begin();
    CTitle::Tdata::const_iterator src_it_end = in_title.Get().end();
    for( ; src_it != src_it_end && iNumCopiedSoFar < iMaxToGet; 
        ++src_it, ++iNumCopiedSoFar) 
    {
        out_title.push_back( *src_it );
    }
}


typedef struct {
    string country;
    string number;
    string app_number;
    TEntrezId muid;
    TEntrezId pmid;
    CRef<CCit_art> art;
} SPubMatchInfo;


string s_GetTitleString(const CTitle::C_E& title) 
{
    string str = "";

    switch (title.Which()) {
        case CTitle::C_E::e_Name:
            str = title.GetName();
            break;
        case CTitle::C_E::e_Abr:
            str = title.GetAbr();
            break;
        case CTitle::C_E::e_Coden:
            str = title.GetCoden();
            break;
        case CTitle::C_E::e_Isbn:
            str = title.GetIsbn();
            break;
        case CTitle::C_E::e_Iso_jta:
            str = title.GetIso_jta();
            break;
        case CTitle::C_E::e_Issn:
            str = title.GetIssn();
            break;
        case CTitle::C_E::e_Jta:
            str = title.GetJta();
            break;
        case CTitle::C_E::e_Ml_jta:
            str = title.GetMl_jta();
            break;
        case CTitle::C_E::e_Trans:
            str = title.GetTrans();
            break;
        case CTitle::C_E::e_Tsub:
            str = title.GetTsub();
            break;
        case CTitle::C_E::e_not_set:
            break;
    }
    return str;
}


bool s_TitleMatch(const CTitle& title1, const CTitle& title2, CTitle::C_E::E_Choice title_type)
{
    string compare1 = "";
    string compare2 = "";

    ITERATE(CTitle::Tdata, it, title1.Get()) {
        if ((*it)->Which() == title_type) {
            compare1 = s_GetTitleString(**it);
            break;
        }
    }
    ITERATE(CTitle::Tdata, it, title2.Get()) {
        if ((*it)->Which() == title_type) {
            compare2 = s_GetTitleString(**it);
            break;
        }
    }
    if (!NStr::IsBlank(compare1) && NStr::EqualNocase(compare1, compare2)) {
        return true;
    } else {
        return false;
    }
}


#define FIELD(Obj,Field) (Obj.IsSet##Field() ? Obj.Get##Field() : "")
#define FIELD_MATCH(Obj1,Obj2,Field) (NStr::EqualNocase(FIELD(Obj1,Field), FIELD(Obj2,Field)))
#define INT_FIELD_MATCH(Obj1,Obj2,Field) ((Obj1.IsSet##Field() && Obj2.IsSet##Field() && Obj1.Get##Field() == Obj2.Get##Field()) || (!Obj1.IsSet##Field() && !Obj2.IsSet##Field()))

#define S_MATCH(Obj1,Obj2,Field) ((!Obj1.IsSet##Field() && !Obj2.IsSet##Field()) || (Obj1.IsSet##Field() && Obj2.IsSet##Field() && s_Match##Field(Obj1.Get##Field(),Obj2.Get##Field())))
#define S_MATCH_A(Obj1,Obj2,Field) ((!Obj1.IsSet##Field() && !Obj2.IsSet##Field()) || (Obj1.IsSet##Field() && Obj2.IsSet##Field() && Obj1.Get##Field().SameCitation(Obj2.Get##Field())))

bool s_MatchDate(const CDate& date1, const CDate& date2)
{
    return date1.Equals(date2);
}


bool s_MatchImp(const CImprint& imp1, const CImprint& imp2)
{
    if (!S_MATCH(imp1, imp2, Date)) {
        return false;
    }

    if (!FIELD_MATCH(imp1, imp2, Volume) ||
        !FIELD_MATCH(imp1, imp2, Issue) ||
        !FIELD_MATCH(imp1, imp2, Pages) ||
        !FIELD_MATCH(imp1, imp2, Section) ||
        !FIELD_MATCH(imp1, imp2, Part_sup) ||
        !FIELD_MATCH(imp1, imp2, Language)) {
        return false;
    }

    if (imp1.IsSetCprt() && imp2.IsSetCprt() && !imp1.GetCprt().Equals(imp2.GetCprt())) {
        return false;
    }
    return true;
}


// try to match one of these title types
static const CTitle::C_E::E_Choice s_MatchJournalTypes[] = {
    CTitle::C_E::e_Iso_jta,
    CTitle::C_E::e_Ml_jta,
    CTitle::C_E::e_Coden,
    CTitle::C_E::e_Issn,
    CTitle::C_E::e_Name,
    CTitle::C_E::e_Jta,
    CTitle::C_E::e_not_set
};


bool s_MatchTitle(const CTitle& title1, const CTitle& title2)
{
    bool match_title = false;

    for (size_t i = 0; !match_title && s_MatchJournalTypes[i] != CTitle::C_E::e_not_set; i++) {
        match_title = s_TitleMatch(title1, title2, s_MatchJournalTypes[i]);
    }

    return match_title;
}


bool s_JournalMatch(const CCit_jour& jour1, const CCit_jour& jour2)
{
    // look for matching title
    if (!S_MATCH(jour1, jour2, Title)) {
        return false;
    }
  
    return S_MATCH(jour1, jour2, Imp);
}


bool s_MatchBook(const CCit_book& book1, const CCit_book& book2)
{
    if (!S_MATCH_A(book1, book2, Authors)) {
        return false;
    }

    if (!S_MATCH(book1, book2, Imp)) {
        return false;
    }

    if ((!book1.IsSetTitle() && book2.IsSetTitle()) ||
        (book1.IsSetTitle() && !book2.IsSetTitle()) ||
        !s_TitleMatch(book1.GetTitle(), book2.GetTitle(), CTitle::C_E::e_Name)) {
        return false;
    }

    return true;
}


bool s_ProcMatch(const CCit_proc& book1, const CCit_proc& book2)
{
    return S_MATCH(book1, book2, Book);
}


bool s_ManMatch(const CCit_let& book1, const CCit_let& book2)
{
    return ((!book1.IsSetCit() && !book2.IsSetCit()) ||
            (book1.IsSetCit() && book2.IsSetCit() && s_MatchBook(book1.GetCit(), book2.GetCit())));
}


bool s_CitArtMatch(const CCit_art& art1, const CCit_art& art2)
{
    if (!art1.IsSetFrom() || !art2.IsSetFrom()) {
        return false;
    }
    if (art1.GetFrom().Which() != art2.GetFrom().Which()) {
        return false;
    }

    if (art1.GetFrom().IsJournal()) {
        // make sure journals match
        if (!s_JournalMatch(art1.GetFrom().GetJournal(), art2.GetFrom().GetJournal())) {
            return false;
        }
    } else if (art1.GetFrom().IsBook()) {
        // make sure books match
        if (!s_MatchBook(art1.GetFrom().GetBook(), art2.GetFrom().GetBook())) {
            return false;
        }
    } else if (art1.GetFrom().IsProc()) {
        // make sure proceedings match
        if (!s_ProcMatch(art1.GetFrom().GetProc(), art2.GetFrom().GetProc())) {
            return false;
        }
    }

    if (!S_MATCH_A(art1, art2, Authors)) {
        return false;
    }

    if (!art1.IsSetTitle() && !art2.IsSetTitle()) {
        // ok, no title for either
    } else if (!art1.IsSetTitle() || !art2.IsSetTitle()) {
        return false;
    } else if (!s_TitleMatch(art1.GetTitle(), art2.GetTitle(), CTitle::C_E::e_Name)) {
        return false;
    }

    return true;
}


bool s_CitGenMatch(const CCit_gen& gen1, const CCit_gen& gen2)
{
    if (!FIELD_MATCH(gen1, gen2, Volume) ||
        !FIELD_MATCH(gen1, gen2, Issue) ||
        !FIELD_MATCH(gen1, gen2, Pages) ||
        !FIELD_MATCH(gen1, gen2, Title) ||
        !FIELD_MATCH(gen1, gen2, Cit)) {
        return false;
    }
    if (!S_MATCH_A(gen1, gen2, Authors)) {
        return false;
    }

    if (!INT_FIELD_MATCH(gen1, gen2, Muid) ||
        !INT_FIELD_MATCH(gen1, gen2, Serial_number)) {
        return false;
    }

    if (!gen1.IsSetJournal() && !gen2.IsSetJournal()) {
        // both empty, ok
    } else if (!gen1.IsSetJournal() || !gen2.IsSetJournal()) {
        return false;
    } else if (!s_MatchTitle(gen1.GetJournal(), gen2.GetJournal())) {
        return false;
    }

    if (!S_MATCH(gen1, gen2, Date)) {
        return false;
    }

	return true;

}


bool s_CitSubMatch(const CCit_sub& sub1, const CCit_sub& sub2)
{
    if (!S_MATCH_A(sub1, sub2, Authors)) {
        return false;
    }

    bool rval = false;

    if (sub1.IsSetImp() && sub2.IsSetImp()) {
        rval = s_MatchImp(sub1.GetImp(), sub2.GetImp());
    } else {
        // compare date from imprint or in sub
        CConstRef<CDate> date1(NULL);
        CConstRef<CDate> date2(NULL);
        if (sub1.IsSetImp() && sub1.GetImp().IsSetDate()) {
            date1.Reset(&(sub1.GetImp().GetDate()));
        } else if (sub1.IsSetDate()) {
            date1.Reset(&(sub1.GetDate()));
        }
        if (sub2.IsSetImp() && sub2.GetImp().IsSetDate()) {
            date2.Reset(&(sub2.GetImp().GetDate()));
        } else if (sub2.IsSetDate()) {
            date2.Reset(&(sub2.GetDate()));
        }
        if (!date1 && !date2) {
            rval = true;
        } else if (!date1 || !date2) {
            rval = false;
        } else {
            rval = date1->Equals(*date2);
        }
	}
    return rval;
}


bool s_MatchInfoMatches(const SPubMatchInfo& match1, const SPubMatchInfo& match2)
{
    if (match1.muid > ZERO_ENTREZ_ID && match2.muid > ZERO_ENTREZ_ID) {
        if (match1.muid == match2.muid) {
            return true;
        } 
    }

    if (match1.pmid > ZERO_ENTREZ_ID && match2.pmid > ZERO_ENTREZ_ID) {
        if (match1.pmid == match2.pmid) {
            return true;
        }
    }

    if (!NStr::IsBlank(match1.country) && !NStr::IsBlank(match2.country)) {
        if (NStr::EqualNocase(match1.country, match2.country) && 
            NStr::EqualNocase(match1.number, match2.number) &&
            NStr::EqualNocase(match1.app_number, match2.app_number)) {
            return true;
        }
    }

    if (match1.art && match2.art) {
        if (s_CitArtMatch(*(match1.art), *(match2.art))) {
            return true;
        }
    }

    return false;
}


void s_GetPubMatchInfo(const CCit_pat& patent, SPubMatchInfo& match)
{
    if (patent.IsSetCountry()) {
        match.country = patent.GetCountry();
    }
    if (patent.IsSetNumber()) {
        match.number = patent.GetNumber();
    }
    if (patent.IsSetApp_number()) {
        match.app_number = patent.GetApp_number();
    }
}


void s_GetPubMatchInfo(const CId_pat& patent, SPubMatchInfo& match)
{
    if (patent.IsSetCountry()) {
        match.country = patent.GetCountry();
    }
    if (patent.IsSetId()) {
        if (patent.GetId().IsNumber()) {
            match.number = patent.GetId().GetNumber();
        } else if (patent.GetId().IsApp_number()) {
            match.app_number = patent.GetId().GetApp_number();
        }
    }
}


void s_GetPubMatchInfo(const CPub& pub, SPubMatchInfo& match)
{
    match.muid = ZERO_ENTREZ_ID;
    match.pmid = ZERO_ENTREZ_ID;
    match.country = "";
    match.number = "";
    match.app_number = "";

    switch (pub.Which()) {
        case CPub::e_Pmid:
            match.pmid = pub.GetPmid();
            break;
        case CPub::e_Muid:
            match.muid = pub.GetMuid();
            break;
        case CPub::e_Patent:
            s_GetPubMatchInfo(pub.GetPatent(), match);
            break;
        case CPub::e_Pat_id:
            s_GetPubMatchInfo(pub.GetPat_id(), match);
            break;
        case CPub::e_Article:
            match.art = new CCit_art();
            match.art->Assign(pub.GetArticle());
            break;
        default:
            break;
    }
}


bool CPub::SameCitation(const CPub& other) const
{
    if (Which() == CPub::e_Equiv) {
        if (other.Which() == CPub::e_Equiv) {
            return GetEquiv().SameCitation(other.GetEquiv());
        } else {
            return GetEquiv().SameCitation(other);
        }
    }
    SPubMatchInfo match1, match2;

    s_GetPubMatchInfo(*this, match1);
    s_GetPubMatchInfo(other, match2);
    if (s_MatchInfoMatches(match1, match2)) {
        return true;
    }
    // false if different pub types
    if (Which() != other.Which()) {
        return false;
    }

    bool rval = false;

    switch (Which()) {
        case CPub::e_Gen:
            rval = s_CitGenMatch(GetGen(), other.GetGen());
            break;
        case CPub::e_Sub:
            rval = s_CitSubMatch(GetSub(), other.GetSub());
            break;
        case CPub::e_Journal:
            rval = s_JournalMatch(GetJournal(), other.GetJournal());
            break;
        case CPub::e_Book:
            rval = s_MatchBook(GetBook(), other.GetBook());
            break;
        case CPub::e_Proc:
            rval = s_ProcMatch(GetProc(), other.GetProc());
            break;
        case CPub::e_Man:
            rval = s_ManMatch(GetMan(), other.GetMan());
            break;
        default:
            break;
    }
	return rval;
}


END_objects_SCOPE // namespace ncbi::objects::

END_NCBI_SCOPE

/* Original file checksum: lines: 61, chars: 1862, CRC32: d27bbf43 */
