/**
 * UGENE - Integrated Bioinformatics Tools.
 * Copyright (C) 2008-2024 UniPro <ugene@unipro.ru>
 * http://ugene.net
 *
 * 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 2
 * 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 */

#include "IOAdapter.h"

#include <U2Core/TextUtils.h>

namespace U2 {

const IOAdapterId BaseIOAdapters::LOCAL_FILE("local_file");
const IOAdapterId BaseIOAdapters::GZIPPED_LOCAL_FILE("local_file_gzip");
const IOAdapterId BaseIOAdapters::HTTP_FILE("http_file");
const IOAdapterId BaseIOAdapters::GZIPPED_HTTP_FILE("http_file_gzip");
const IOAdapterId BaseIOAdapters::VFS_FILE("memory_buffer");
const IOAdapterId BaseIOAdapters::STRING("string");

qint64 IOAdapter::readUntil(char* buf, qint64 maxSize, const QBitArray& readTerminators, TerminatorHandling th, bool* terminatorFound) {
    const qint64 CHUNK = (qint64)1024;
    const char *chunk_start, *start = buf, *end = buf + maxSize;

    bool found = false;
    qint64 len;
    qint64 termsSkipped = 0;
    do {
        chunk_start = buf;
        len = readBlock(buf, qMin(CHUNK, (qint64)end - (qint64)buf));
        if (len == -1) {
            // error
            return -1;
        }
        if (len < CHUNK) {
            // last chunk, no more data or buffer space
            end = buf + len;
        }
        const char* buf_end = buf + len;
        for (; buf < buf_end; buf++) {
            // loop exit invariant: buf is positioned after last accepted char
            if (readTerminators[(uchar)*buf]) {
                found = true;
                if (th == Term_Exclude) {
                    break;
                } else if (Term_Skip == th) {
                    termsSkipped++;
                }
            } else if (found) {
                assert(Term_Include == th || Term_Skip == th);
                break;
            }
        }
    } while (!found && buf < end);

    if (found) {
        bool b = skip((qint64)buf - (qint64)chunk_start - len);
        assert(b);  // Cannot put back unused data;
        Q_UNUSED(b);
    }

    if (terminatorFound != nullptr) {
        *terminatorFound = found;
    }

    return (qint64)buf - (qint64)start - termsSkipped;
}

bool IOAdapter::isEof() {
    char ch;
    qint64 ret = readBlock(&ch, 1);
    if (ret == 1) {
        skip(-ret);
    }
    return 0 == ret;
}

qint64 IOAdapter::readLine(char* buff, qint64 maxSize, bool* terminatorFound /* = 0*/) {
    bool terminatorFlagStub = false;
    if (!terminatorFound) {
        terminatorFound = &terminatorFlagStub;
    }
    qint64 len = readUntil(buff, maxSize, TextUtils::LINE_BREAKS, Term_Exclude, terminatorFound);
    if (*terminatorFound) {
        char ch;
        bool isGetCharOk = getChar(&ch);
        SAFE_POINT(isGetCharOk, L10N::internalError("IOAdapter::readLine::getChar"), len);
        if (ch == '\r') {
            // may be Windows EOL \r\n
            isGetCharOk = getChar(&ch);
            if (isGetCharOk && ch != '\n') {
                skip(-1);
            }
        } else {
            SAFE_POINT(ch == '\n', L10N::internalError("IOAdapter::readLine char is not '\\n'"), len);
        }
    }
    return len;
}

void IOAdapter::cutByteOrderMarks(char* data, QString& errorString, qint64& length) {
    QByteArray dataByteArray(data, length);
    qint64 newCached = TextUtils::cutByteOrderMarks(data, errorString, length);
    QByteArray newDatatByteArray(data);
    if (dataByteArray != newDatatByteArray) {
        length = newCached;
    }
}

}  // namespace U2
