/**
 * 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 "U2AssemblyUtils.h"

#include <U2Core/TextUtils.h>
#include <U2Core/U2OpStatus.h>

namespace U2 {

const int U2AssemblyUtils::MAX_COVERAGE_VECTOR_SIZE = 1000 * 1000;

U2CigarOp U2AssemblyUtils::char2Cigar(char c, QString& err) {
    char cu = TextUtils::UPPER_CASE_MAP[c];
    switch (cu) {
        case 'D':
            return U2CigarOp_D;  // deleted
        case 'I':
            return U2CigarOp_I;  // inserted
        case 'H':
            return U2CigarOp_H;  // hard-clipped
        case 'M':
            return U2CigarOp_M;  // matched
        case 'N':
            return U2CigarOp_N;  // skipped
        case 'P':
            return U2CigarOp_P;  // padded
        case 'S':
            return U2CigarOp_S;  // soft-clipped
        case '=':
            return U2CigarOp_EQ;  // sequence match
        case 'X':
            return U2CigarOp_X;  // sequence mismatch
    }
    err = tr("Invalid CIGAR op: '%1'!").arg(c);
    return U2CigarOp_Invalid;
}

char U2AssemblyUtils::cigar2Char(U2CigarOp op) {
    char c;
    switch (op) {
        case U2CigarOp_D:
            c = 'D';
            break;
        case U2CigarOp_I:
            c = 'I';
            break;
        case U2CigarOp_H:
            c = 'H';
            break;
        case U2CigarOp_M:
            c = 'M';
            break;
        case U2CigarOp_N:
            c = 'N';
            break;
        case U2CigarOp_P:
            c = 'P';
            break;
        case U2CigarOp_S:
            c = 'S';
            break;
        case U2CigarOp_EQ:
            c = '=';
            break;
        case U2CigarOp_X:
            c = 'X';
            break;
        default:
            assert(0);
            c = '?';
    }
    return c;
}

QList<U2CigarToken> U2AssemblyUtils::parseCigar(const QByteArray& cigarString, QString& err) {
    QList<U2CigarToken> result;
    int pos = 0, len = cigarString.length();
    const char* cigar = cigarString.constData();
    int count = 0;
    // count numbers
    while (pos < len) {
        char c = cigar[pos++];
        if (c >= '0' && c <= '9') {
            int n = c - '0';
            count = count * 10 + n;
            continue;
        }
        U2CigarOp op = char2Cigar(c, err);
        if (!err.isEmpty()) {
            break;
        }
        result.append(U2CigarToken(op, count));
        count = 0;
    }
    return result;
}

QByteArray U2AssemblyUtils::cigar2String(const QList<U2CigarToken>& cigar) {
    QByteArray res;
    foreach (const U2CigarToken& t, cigar) {
        if (t.op != U2CigarOp_Invalid) {
            res = res + QByteArray::number(t.count) + cigar2Char(t.op);
        }
    }
    return res;
}

qint64 U2AssemblyUtils::getEffectiveReadLength(const U2AssemblyRead& read) {
    return read->readSequence.length() + getCigarExtraLength(read->cigar);
}

qint64 U2AssemblyUtils::getCigarExtraLength(const QList<U2CigarToken>& cigar) {
    qint64 res = 0;
    foreach (const U2CigarToken& t, cigar) {
        switch (t.op) {
            case U2CigarOp_I:
            case U2CigarOp_S:
                res -= t.count;
                break;
            case U2CigarOp_D:
            case U2CigarOp_N:
                res += t.count;
                break;
            default:;
        }
    }
    return res;
}

QByteArray U2AssemblyUtils::serializeCoverageStat(const U2AssemblyCoverageStat& coverageStat) {
    QByteArray data;
    for (int index = 0; index < coverageStat.size(); index++) {
        for (int i = 0; i < 4; i++) {
            data.append((char)(coverageStat[index] >> (i * 8)));
        }
    }
    return data;
}

void U2AssemblyUtils::deserializeCoverageStat(QByteArray data, U2AssemblyCoverageStat& res, U2OpStatus& os) {
    res.clear();
    if (!data.isEmpty() && 0 == (data.size() % 4)) {
        for (int index = 0; index < data.size() / 4; index++) {
            int value = 0;
            for (int i = 0; i < 4; i++) {
                value |= ((int)data[index * 4 + i] & 0xff) << (i * 8);
            }
            res.append(value);
        }
    } else {
        os.setError("Invalid attribute size");
    }
}

}  // namespace U2
