/* 
 * File:   PairedEndIO.cpp
 * Author: yangxi
 * 
 * Created on March 27, 2014, 9:15 AM
 */

#include "htio2/PairedEndIO.h"

#include "htio2/Error.h"
#include "htio2/FastqIO.h"
#include "htio2/FastaIO.h"

using namespace std;

namespace htio2
{

PairedEndIO::PairedEndIO(const std::string& file,
                         IOMode mode,
                         Strand strand_r1_in_file,
                         Strand strand_r2_in_file,
                         CompressFormat comp,
                         SeqFormat format)
: fh1(0)
, fh2(0)
, interleave(true)
, strand_r1_in_file(strand_r1_in_file)
, strand_r2_in_file(strand_r2_in_file)
{
    switch (format)
    {
    case FORMAT_FASTQ:
        fh1 = new FastqIO(file, mode, comp);
        break;
    case FORMAT_FASTA:
        fh1 = new FastaIO(file, mode, comp);
        break;
    default:
        fh1 = SeqIO::New(file, mode, comp);
        break;
    }
}

PairedEndIO::PairedEndIO(const std::string& file1,
                         const std::string& file2,
                         IOMode mode,
                         Strand strand_r1_in_file,
                         Strand strand_r2_in_file,
                         CompressFormat comp,
                         SeqFormat format)
: fh1(0)
, fh2(0)
, interleave(false)
, strand_r1_in_file(strand_r1_in_file)
, strand_r2_in_file(strand_r2_in_file)
{
    switch (format)
    {
    case FORMAT_FASTQ:
        fh1 = new FastqIO(file1, mode, comp);
        fh2 = new FastqIO(file2, mode, comp);
        break;
    case FORMAT_FASTA:
        fh1 = new FastaIO(file1, mode, comp);
        fh2 = new FastaIO(file2, mode, comp);
        break;
    default:
        fh1 = SeqIO::New(file1, mode, comp);
        fh2 = SeqIO::New(file2, mode, comp);
        break;
    }
}

PairedEndIO::~PairedEndIO()
{
    delete fh1;
    if (fh2) delete fh2;
}

bool PairedEndIO::next_pair(FastqSeq& seq1,
                            FastqSeq& seq2,
                            Strand strand_r1,
                            Strand strand_r2)
{
    if (strand_r1 == strand_r1_in_file)
    {
        if (strand_r2 == strand_r2_in_file)
        {
            return _read_next_pair(seq1, seq2);
        }
        else
        {
            bool re = _read_next_pair(seq1, tmp2);
            if (re)
                tmp2.revcom(seq2);
            return re;
        }
    }
    else
    {
        if (strand_r2 == strand_r2_in_file)
        {
            bool re = _read_next_pair(tmp1, seq2);
            if (re)
                tmp1.revcom(seq1);
            return re;
        }
        else
        {
            bool re = _read_next_pair(tmp1, tmp2);
            if (re)
            {
                tmp1.revcom(seq1);
                tmp2.revcom(seq2);
            }
            return re;
        }
    }
}

void PairedEndIO::write_pair(const FastqSeq& seq1,
                             const FastqSeq& seq2,
                             Strand strand_r1,
                             Strand strand_r2)
{
    if (strand_r1 == strand_r1_in_file)
    {
        if (strand_r2 == strand_r2_in_file)
        {
            _write_pair(seq1, seq2);
        }
        else
        {
            seq2.revcom(tmp2);
            _write_pair(seq1, tmp2);
        }
    }
    else
    {
        if (strand_r2 == strand_r2_in_file)
        {
            seq1.revcom(tmp1);
            _write_pair(tmp1, seq2);
        }
        else
        {
            seq1.revcom(tmp1);
            seq2.revcom(tmp2);
            _write_pair(tmp1, tmp2);
        }
    }
}

bool PairedEndIO::_read_next_pair(FastqSeq& seq1, FastqSeq& seq2)
{
    if (interleave)
    {
        bool re1 = fh1->next_seq(seq1);
        bool re2 = fh1->next_seq(seq2);
        if (re1)
        {
            if (re2)
                return true;
            else
                throw InvalidFileContent("incomplete interleaved sequence file", fh1->tell());
        }
        else
            return false;
    }
    else
    {
        bool re1 = fh1->next_seq(seq1);
        bool re2 = fh2->next_seq(seq2);
        if (re1)
        {
            if (re2)
                return true;
            else
                throw InvalidFileContent("paired-end from two files: file 2 ended, but file 1 not", fh1->tell());
        }
        else
        {
            if (re2)
                throw InvalidFileContent("paired-end from two files: file 1 ended, but file 2 not", fh2->tell());
            else
                return false;
        }
    }
}

void PairedEndIO::_write_pair(const FastqSeq& seq1, const FastqSeq& seq2)
{
    if (interleave)
    {
        fh1->write_seq(seq1);
        fh1->write_seq(seq2);
    }
    else
    {
        fh1->write_seq(seq1);
        fh2->write_seq(seq2);
    }
}

} // namespace htio2
