#include "htqc/Primer.h"

#include "htio2/SimpleSeq.h"

using namespace std;

namespace htqc
{

Primer::Primer(const std::string& id, const std::string& seq, size_t k)
    : id(id)
    , seq_fwd(seq)
{
    htio2::revcom(seq_fwd, seq_rev);
    htio2::EncodedSeq enc_fwd;
    htio2::EncodedSeq enc_rev;
    htio2::encode_nt5(seq_fwd, enc_fwd);
    htio2::encode_nt5(seq_rev, enc_rev);
    htio2::gen_kmer_nt5(enc_fwd, klist_fwd, k);
    htio2::gen_kmer_nt5(enc_rev, klist_rev, k);
    htio2::summ_kmer_pos(klist_fwd, kpos_fwd);
    htio2::summ_kmer_pos(klist_rev, kpos_rev);
}

bool align_primer(const string& read, const vector<int16_t>& read_qual, const KmerList& seq_km_list,
                  const string& primer, const KmerList& primer_km_list, const KmerPosMap& primer_km_pos,
                  int cut_qual, int cut_mis_high, int cut_mis_total,
                  PosT& seq_start, PosT& seq_end,
                  PosT& primer_start, PosT& primer_end)
{
    const size_t len_seq = read.length();
    const size_t len_primer = primer.length();
    const size_t nk_seq = seq_km_list.size();

    bool success = false;

    // traverse sequence kmer position
    for (PosT i_seq_km = 0; i_seq_km < nk_seq; i_seq_km++)
    {
        const KmerT seq_km = seq_km_list[i_seq_km];
        pair<KmerPosMap::const_iterator, KmerPosMap::const_iterator> primer_km_pos_bound = primer_km_pos.equal_range(seq_km);

        // traverse primer kmer position
        for (KmerPosMap::const_iterator primer_km_pos_it = primer_km_pos_bound.first;
             primer_km_pos_it != primer_km_pos_bound.second;
             primer_km_pos_it++)
        {
            const PosT i_primer_km = primer_km_pos_it->second;

            const size_t len_seq_before = i_seq_km;
            const size_t len_primer_before = i_primer_km;

            // i_primer = i_seq + offset
            const PosT offset = i_primer_km - i_seq_km;

            // get alignment start position
            PosT i_seq_start = 0;
            PosT i_primer_start = 0;
            if (len_seq_before < len_primer_before)
                i_primer_start = i_seq_start + offset;
            else
                i_seq_start = i_primer_start - offset;

            // traverse bases
            PosT i_seq_end = i_seq_start;
            PosT i_primer_end = i_primer_start;

            uint16_t mis_high = 0;
            uint16_t mis_total = 0;

            while (1)
            {
                if (read[i_seq_end] != primer[i_primer_end])
                {
                    mis_total++;
                    if (read_qual[i_seq_end] >= cut_qual)
                        mis_high++;
                }

                i_seq_end++;
                i_primer_end++;
                if (i_seq_end == len_seq || i_primer_end == len_primer) break;
            }

            // check status
            if (mis_high <= cut_mis_high && mis_total <= cut_mis_total)
            {
                success = true;
                seq_start = i_seq_start;
                seq_end = i_seq_end - 1;
                primer_start = i_primer_start;
                primer_end = i_primer_end - 1;
                break;
            }
        }

        if (success) break;
    }

    return success;
}

bool Primer::align_fwd(const std::string &seq, const std::vector<int16_t> qual, const KmerList &seq_kmer,
                       int cut_qual, int cut_mis_high, int cut_mis_total,
                       PosT &seq_start, PosT &seq_end, PosT &primer_start, PosT &primer_end) const
{
    return align_primer(seq, qual, seq_kmer,
                        seq_fwd, klist_fwd, kpos_fwd,
                        cut_qual, cut_mis_high, cut_mis_total,
                        seq_start, seq_end,
                        primer_start, primer_end);
}

bool Primer::align_rev(const std::string &seq, const std::vector<int16_t> qual, const KmerList &seq_kmer,
                       int cut_qual, int cut_mis_high, int cut_mis_total,
                       PosT &seq_start, PosT &seq_end, PosT &primer_start, PosT &primer_end) const
{
    return align_primer(seq, qual, seq_kmer,
                        seq_rev, klist_rev, kpos_rev,
                        cut_qual, cut_mis_high, cut_mis_total,
                        seq_start, seq_end,
                        primer_start, primer_end);
}

PrimerGroup::PrimerGroup(const string &name): name(name) {}

} // namespace htqc
