/* $Id: Seq_loc_mix.cpp 361008 2012-04-27 17:47:52Z vasilche $
 * ===========================================================================
 *
 *                            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:
 *   .......
 *
 * Remark:
 *   This code was originally generated by application DATATOOL
 *   using specifications from the ASN data definition file
 *   'seqloc.asn'.
 */

#include <ncbi_pch.hpp>
#include <objects/seqloc/Seq_loc_mix.hpp>
#include <objects/seqloc/Seq_interval.hpp>
#include <objects/seqloc/Seq_point.hpp>
#include <objects/seqloc/Seq_loc.hpp>


BEGIN_NCBI_SCOPE
BEGIN_objects_SCOPE // namespace ncbi::objects::

CSeq_loc_mix::CSeq_loc_mix(void)
{
    return;
}


CSeq_loc_mix::~CSeq_loc_mix(void)
{
}

const CSeq_loc* CSeq_loc_mix::GetFirstLoc(ENullSegType null_seg) const
{
    const Tdata& arr = Get();
    ITERATE ( Tdata, it, arr ) {
        const CSeq_loc* ret = *it;
        if ( null_seg == eNullSegAllow || !ret->IsNull() ) {
            return ret;
        }
    }
    return 0;
}

const CSeq_loc* CSeq_loc_mix::GetLastLoc(ENullSegType null_seg) const
{
    const Tdata& arr = Get();
    REVERSE_ITERATE ( Tdata, it, arr ) {
        const CSeq_loc* ret = *it;
        if ( null_seg == eNullSegAllow || !ret->IsNull() ) {
            return ret;
        }
    }
    return 0;
}

CSeq_loc* CSeq_loc_mix::SetFirstLoc(ENullSegType null_seg)
{
    Tdata& arr = Set();
    NON_CONST_ITERATE ( Tdata, it, arr ) {
        CSeq_loc* ret = *it;
        if ( null_seg == eNullSegAllow || !ret->IsNull() ) {
            return ret;
        }
    }
    return 0;
}

CSeq_loc* CSeq_loc_mix::SetLastLoc(ENullSegType null_seg)
{
    Tdata& arr = Set();
    NON_CONST_REVERSE_ITERATE ( Tdata, it, arr ) {
        CSeq_loc* ret = *it;
        if ( null_seg == eNullSegAllow || !ret->IsNull() ) {
            return ret;
        }
    }
    return 0;
}

const CSeq_loc* CSeq_loc_mix::GetStartLoc(ESeqLocExtremes ext,
                                          ENullSegType null_seg) const
{
    return (ext == eExtreme_Positional  &&  IsReverseStrand()) ?
        GetLastLoc(null_seg): GetFirstLoc(null_seg);
}

const CSeq_loc* CSeq_loc_mix::GetStopLoc(ESeqLocExtremes ext,
                                         ENullSegType null_seg) const
{
    return (ext == eExtreme_Positional  &&  IsReverseStrand()) ?
        GetFirstLoc(null_seg): GetLastLoc(null_seg);
}

CSeq_loc* CSeq_loc_mix::SetStartLoc(ESeqLocExtremes ext,
                                    ENullSegType null_seg)
{
    return (ext == eExtreme_Positional  &&  IsReverseStrand()) ?
        SetLastLoc(null_seg): SetFirstLoc(null_seg);
}

CSeq_loc* CSeq_loc_mix::SetStopLoc(ESeqLocExtremes ext,
                                   ENullSegType null_seg)
{
    return (ext == eExtreme_Positional  &&  IsReverseStrand()) ?
        SetFirstLoc(null_seg): SetLastLoc(null_seg);
}

bool CSeq_loc_mix::IsPartialStart(ESeqLocExtremes ext) const
{
    if (!Get().empty()) {
        return GetStartLoc(ext)->IsPartialStart(ext);
    }
    return false;
}

bool CSeq_loc_mix::IsPartialStop(ESeqLocExtremes ext) const
{
    if (!Get().empty()) {
        return GetStopLoc(ext)->IsPartialStop(ext);
    }
    return false;
}


void CSeq_loc_mix::SetPartialStart(bool val, ESeqLocExtremes ext)
{
    if (IsPartialStart(ext) == val  ||  Set().empty()) {
        return;
    }
    SetStartLoc(ext)->SetPartialStart(val, ext);
}


void CSeq_loc_mix::SetPartialStop(bool val, ESeqLocExtremes ext)
{
    if (IsPartialStop(ext) == val  ||  Set().empty()) {
        return;
    }
    SetStopLoc(ext)->SetPartialStop(val, ext);
}


bool CSeq_loc_mix::IsTruncatedStart(ESeqLocExtremes ext) const
{
    if (!Get().empty()) {
        return GetStartLoc(ext)->IsTruncatedStart(ext);
    }
    return false;
}

bool CSeq_loc_mix::IsTruncatedStop(ESeqLocExtremes ext) const
{
    if (!Get().empty()) {
        return GetStopLoc(ext)->IsTruncatedStop(ext);
    }
    return false;
}


void CSeq_loc_mix::SetTruncatedStart(bool val, ESeqLocExtremes ext)
{
    if (IsTruncatedStart(ext) == val  ||  Set().empty()) {
        return;
    }
    SetStartLoc(ext)->SetTruncatedStart(val, ext);
}


void CSeq_loc_mix::SetTruncatedStop(bool val, ESeqLocExtremes ext)
{
    if (IsTruncatedStop(ext) == val  ||  Set().empty()) {
        return;
    }
    SetStopLoc(ext)->SetTruncatedStop(val, ext);
}


bool CSeq_loc_mix::IsSetStrand(EIsSetStrand flag) const
{
    ITERATE(Tdata, it, Get()) {
        switch (flag) {
        case eIsSetStrand_Any:
            if ( (*it)->GetStrand() ) return true;
            break;
        case eIsSetStrand_All:
            if ( !(*it)->GetStrand() ) return false;
            break;
        }
    }
    return flag == eIsSetStrand_Any ? false : true;
}


ENa_strand CSeq_loc_mix::GetStrand(void) const
{
    ENa_strand strand = eNa_strand_unknown;
    bool strand_set = false;
    const CSeq_id* id = NULL;
    ITERATE(Tdata, it, Get()) {
        if ((*it)->IsNull()  ||  (*it)->IsEmpty()) {
            continue;
        }

        // check fro multiple IDs
        const CSeq_id* iid = (*it)->GetId();
        if (iid == NULL) {
            return eNa_strand_other;
        }
        if (id == NULL) {
            id = iid;
        } else {
            if (id->Compare(*iid) != CSeq_id::e_YES) {
                return eNa_strand_other;
            }
        }

        ENa_strand istrand = (*it)->GetStrand();
        if (strand == eNa_strand_unknown  &&  istrand == eNa_strand_plus) {
            strand = istrand;
            strand_set = true;
        } else if (strand == eNa_strand_plus  &&  istrand == eNa_strand_unknown) {
        } else if (!strand_set) {
            strand = istrand;
            strand_set = true;
        } else if (istrand != strand) {
            return eNa_strand_other;
        }
    }
    return strand;
}


TSeqPos CSeq_loc_mix::GetStart(ESeqLocExtremes ext) const
{
    if (!Get().empty()) {
        return GetStartLoc(ext, eNullSegSkip)->GetStart(ext);
    }
    return kInvalidSeqPos;
}


TSeqPos CSeq_loc_mix::GetStop(ESeqLocExtremes ext) const
{
    if (!Get().empty()) {
        return GetStopLoc(ext, eNullSegSkip)->GetStop(ext);
    }
    return kInvalidSeqPos;
}


void CSeq_loc_mix::AddSeqLoc(const CSeq_loc& other)
{
    if ( !other.IsMix() ) {
        CRef<CSeq_loc> loc(new CSeq_loc);
        loc->Assign(other);
        Set().push_back(loc);
    } else {
        // "flatten" the other seq-loc
        ITERATE(CSeq_loc_mix::Tdata, li, other.GetMix().Get()) {
            AddSeqLoc(**li);
        }
    }
}


void CSeq_loc_mix::AddSeqLoc(CSeq_loc& other)
{
    if ( !other.IsMix() ) {
        CRef<CSeq_loc> loc(&other);
        Set().push_back(loc);
    } else {
        // "flatten" the other seq-loc
        NON_CONST_ITERATE(CSeq_loc_mix::Tdata, li, other.SetMix().Set()) {
            AddSeqLoc(**li);
        }
    }
}


void CSeq_loc_mix::AddInterval(const CSeq_id& id, TSeqPos from, TSeqPos to,
                               ENa_strand strand)
{
    CRef<CSeq_loc> loc(new CSeq_loc);
    if (from == to) {
        CSeq_point& pnt = loc->SetPnt();
        pnt.SetPoint(from);
        pnt.SetId().Assign(id);
        if (strand != eNa_strand_unknown) {
            pnt.SetStrand(strand);
        }
    } else {
        CSeq_interval& ival = loc->SetInt();
        ival.SetFrom(from);
        ival.SetTo(to);
        ival.SetId().Assign(id);
        if (strand != eNa_strand_unknown) {
            ival.SetStrand(strand);
        }
    }
    Set().push_back(loc);
}


void CSeq_loc_mix::SetStrand(ENa_strand strand)
{
    NON_CONST_ITERATE (Tdata, it, Set()) {
        (*it)->SetStrand(strand);
    }
}


void CSeq_loc_mix::ResetStrand()
{
    NON_CONST_ITERATE (Tdata, it, Set()) {
        (*it)->ResetStrand();
    }
}


void CSeq_loc_mix::FlipStrand(void)
{
    NON_CONST_ITERATE (Tdata, it, Set()) {
        (*it)->FlipStrand();
    }
}

END_objects_SCOPE // namespace ncbi::objects::
END_NCBI_SCOPE
