//     HTQC - a high-throughput sequencing quality control toolkit
//
//     This program is free software: you can redistribute it and/or modify
//     it under the terms of the GNU General Public License as published by
//     the Free Software Foundation, either version 3 of the License, or
//     (at your option) any later version.
//
//     This program is distributed in the hope that it will be useful,
//     but WITHOUT ANY WARRANTY; without even the implied warranty of
//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//     GNU General Public License for more details.
//
//     You should have received a copy of the GNU General Public License
//     along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include <vector>

#include "htqc/FastqStat.h"
#include "htio2/QualityUtil.h"

using namespace htio2;
using namespace std;

namespace htqc
{

FastqStat::FastqStat(bool mask, QualityEncode encode, HeaderFormat h_format, bool h_sra) :
header_parser(h_format, h_sra),
encode(encode),
observed_max_len(0),
qual_mask(mask),
read_quals(),
read_qual_counter(encode)
{
    get_quality_range(encode, qual_from, qual_to);
}

FastqStat::~FastqStat()
{
}

void FastqStat::record_seq(FastqSeq& seq)
{
    // validate length
    int len = seq.length();
    if (len > observed_max_len)
        try_expand_store(len);

    //
    // parse lane and tile
    //
    header_parser.parse(seq.id, seq.desc);

    //
    //
    // traverse through bases
    int valid_len = 0;
    float qual_sum = 0;

    for (int i = 0; i < len; i++)
    {
        // get quality
        int qual = decode_quality(seq.quality[i], encode);
        if (qual == 0)
        {
            printf("");
        }

        //base_qual_counter[i].record(qual);
        base_qual_counter.at(i).record(qual);

        // check mask
        if (qual_mask)
        {
            if (qual < 2)
            {
                cerr << "for quality score with mask, values below 2 are not used, but we got " << qual << " in " << seq.id << endl;
                throw runtime_error("invalid base quality");
            }
            else if (qual == 2)
            {
                base_compo_counter[i].mask++;
                continue;
            }
        }

        // add base quality to read quality
        valid_len++;
        qual_sum += qual;

        // record base composition
        switch (seq.seq[i])
        {
        case 'A':
        case 'a':
            base_compo_counter[i].a++;
            break;
        case 'T':
        case 't':
            base_compo_counter[i].t++;
            break;
        case 'G':
        case 'g':
            base_compo_counter[i].g++;
            break;
        case 'C':
        case 'c':
            base_compo_counter[i].c++;
            break;
        case 'N':
        case 'n':
            base_compo_counter[i].n++;
            break;
        }
    }

    // record length
    read_len_counter.at(valid_len)++;

    // calculate and record read quality
    float read_qual = qual_sum / len;
    read_quals.push_back(read_qual);
    read_qual_counter.record(int(read_qual));
    tile_read_qual_counter[header_parser.lane][header_parser.tile].record(int(read_qual));
}

FastqStat& FastqStat::operator+=(const htqc::FastqStat& other)
{
    if (observed_max_len < other.observed_max_len) try_expand_store(other.observed_max_len);
    if (encode != other.encode) throw runtime_error("encode not same");
    if (qual_mask != other.qual_mask) throw runtime_error("qual_mask not same");

    read_quals.reserve(read_quals.size() + other.read_quals.size());
    for (std::vector<float>::const_iterator it = other.read_quals.begin(); it != other.read_quals.end(); it++)
    {
        read_quals.push_back(*it);
    }

    for (int i = 0; i < observed_max_len; i++)
    {
        base_qual_counter[i] += other.base_qual_counter[i];
        base_compo_counter[i] += other.base_compo_counter[i];
    }

    read_qual_counter += other.read_qual_counter;

    for (int i = 0; i <= observed_max_len; i++)
        read_len_counter[i] += other.read_len_counter[i];

    for (
         map< int, map<int, TileQualCounter> >::const_iterator it_lane = other.tile_read_qual_counter.begin();
         it_lane != other.tile_read_qual_counter.end();
         it_lane++
         )
    {
        int lane = it_lane->first;
        const map<int, TileQualCounter>* lane_quals = &(it_lane->second);

        for (
             map<int, TileQualCounter>::const_iterator it_tile = lane_quals->begin();
             it_tile != lane_quals->end();
             it_tile++
             )
        {
            int tile = it_tile->first;
            tile_read_qual_counter[lane][tile] += it_tile->second;
        }
    }

    return *this;
}

void FastqStat::try_expand_store(size_t new_size)
{
    if (new_size > observed_max_len)
    {
        observed_max_len = new_size;
        base_qual_counter.resize(new_size, BaseQualCounter(encode));
        base_compo_counter.resize(new_size);
        read_len_counter.resize(new_size + 1, 0);
    }
}

} // namespace htqc
