//     HTIO - a high-throughput sequencing reads input/output library
//
//     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 "htio2/PlainFileHandle.h"
#include <cstring>
#include <cstdio>
#include <cerrno>
#include <stdexcept>

using namespace std;

namespace htio2
{

PlainFileHandle::PlainFileHandle(const std::string& file, IOMode mode) : auto_close(true)
{
    handle = fopen(file.c_str(), get_io_mode_str(mode));
    if (handle == 0)
    {
        const char* msg = strerror(errno);
        throw runtime_error(string(msg));
    }

    offset = ftell(handle);
    curr_chr = fgetc(handle);
}

PlainFileHandle::PlainFileHandle(FILE* handle, bool auto_close) : handle(handle), auto_close(auto_close)
{
    offset = ftell(handle);
    curr_chr = fgetc(handle);
}

PlainFileHandle::~PlainFileHandle()
{
    if (auto_close) fclose(handle);
}

bool PlainFileHandle::read_line(std::string& buffer)
{
    buffer.clear();
    if (curr_chr == EOF) return false;

    while (1)
    {
        switch (curr_chr)
        {
        case EOF:
            goto END;
        case LINE_END_CR:
            curr_chr = fgetc(handle);
            if (curr_chr == LINE_END_LF) curr_chr = fgetc(handle);
            goto END;
        case LINE_END_LF:
            curr_chr = fgetc(handle);
            goto END;
        default:
            buffer.push_back(curr_chr);
            curr_chr = fgetc(handle);
        }
    }

END:
    offset = ftell(handle) - 1;
    return true;
}

void PlainFileHandle::write_line(const std::string& content)
{
    fwrite(content.data(), 1, content.size(), handle);
    fputc('\n', handle);
    offset = ftell(handle);
}

void PlainFileHandle::seek(off_t offset, int whence)
{
    if (whence == SEEK_SET)
    {
        fseek(handle, offset, SEEK_SET);
        this->offset = offset;
    }
    else if (whence == SEEK_CUR)
    {
        fseek(handle, this->offset, SEEK_SET);
        fseek(handle, offset, SEEK_CUR);
        this->offset += offset;
    }
    else if (whence == SEEK_END)
    {
        fseek(handle, 0, SEEK_END);
        this->offset = ftell(handle) + offset;
        fseek(handle, offset, SEEK_END);
    }
    else
        throw runtime_error("invalid seek whence");

    curr_chr = fgetc(handle);
}

off_t PlainFileHandle::tell() const
{
    return offset;
}

} // namespace htio2
