#include "htio2/SeqIO.h"
#include "htio2/Error.h"
#include "htio2/FormatUtil.h"
#include "htio2/GzipFileHandle.h"
#include "htio2/PlainFileHandle.h"
#include "htio2/FastqIO.h"
#include "htio2/FastaIO.h"
#include <zlib.h>
#include <stdexcept>

using namespace std;

namespace htio2
{

SeqIO::SeqIO(const std::string& file, IOMode mode, CompressFormat comp)
{
    if (comp == COMPRESS_UNKNOWN)
        comp = guess_compress_by_name(file);

    if (comp == COMPRESS_GZIP)
        handle = new GzipFileHandle(file.c_str(), mode);
    else if (comp == COMPRESS_PLAIN)
        handle = new PlainFileHandle(file.c_str(), mode);
    else
        throw InvalidCompressFormat(comp);
}

SeqIO::SeqIO(FileHandle* handle)
: handle(handle)
{
}

SeqIO::SeqIO(FILE* handle, bool auto_close)
: handle(new PlainFileHandle(handle, auto_close))
{
}

SeqIO::SeqIO(gzFile handle, bool auto_close)
: handle(new GzipFileHandle(handle, auto_close))
{
}

SeqIO* SeqIO::New(const std::string& file, IOMode mode, CompressFormat comp)
{
    SeqFormat format = htio2::FORMAT_UNKNOWN;
    if (mode == htio2::READ)
        format = guess_format_by_name_and_content(file, comp);
    else
        format = guess_format_by_name(file);

    if (format == FORMAT_FASTQ)
        return new FastqIO(file, mode, comp);
    else if (format == FORMAT_FASTA)
        return new FastaIO(file, mode, comp);
    else
        throw InvalidSeqFormat(format);
}

SeqIO* SeqIO::New(FileHandle* handle)
{
    SeqFormat format = guess_format_by_content(*handle);
    switch (format)
    {
    case FORMAT_FASTA:
        return new FastaIO(handle);
    case FORMAT_FASTQ:
        return new FastqIO(handle);
    default:
        throw InvalidSeqFormat(format);
    }
}

SeqIO* SeqIO::New(FILE* handle, bool auto_close)
{
    return SeqIO::New(new PlainFileHandle(handle, auto_close));
}

SeqIO* SeqIO::New(gzFile handle, bool auto_close)
{
    return SeqIO::New(new GzipFileHandle(handle, auto_close));
}

SeqIO::~SeqIO()
{
    delete handle;
}

void SeqIO::seek(off_t offset, int whence)
{
    handle->seek(offset, whence);
}

off_t SeqIO::tell() const
{
    return handle->tell();
}

} // namespace htio2
