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

#include <QDomElement>
#include <QFile>

#include <U2Core/L10n.h>

#include "HmmerSearchTaskTest.h"
#include "utils/OutputCollector.h"

namespace U2 {

/****************************************
 * GTest_UHMM3Phmmer
 ****************************************/

const QString GTest_UHMM3Phmmer::QUERY_FILENAME_TAG = "query";
const QString GTest_UHMM3Phmmer::DB_FILENAME_TAG = "db";

const QString GTest_UHMM3Phmmer::GAP_OPEN_PROBAB_OPTION_TAG = "popen";
const QString GTest_UHMM3Phmmer::GAP_EXTEND_PROBAB_OPTION_TAG = "pextend";

const QString GTest_UHMM3Phmmer::OUTPUT_DIR_TAG = "outputDir";

const double BAD_DOUBLE_OPTION = -1.0;

static void setDoubleOption(double& to, const QString& str, TaskStateInfo& ti) {
    if (str.isEmpty()) {
        return;
    }
    bool ok = false;
    to = str.toDouble(&ok);
    if (!ok) {
        to = BAD_DOUBLE_OPTION;
        ti.setError(QString("cannot_parse_double_from: %1").arg(str));
    }
}

void GTest_UHMM3Phmmer::init(XMLTestFormat*, const QDomElement& el) {
    phmmerTask = nullptr;
    queryFilename = el.attribute(QUERY_FILENAME_TAG);
    dbFilename = el.attribute(DB_FILENAME_TAG);

    setSearchTaskSettings(searchSettings, el, stateInfo);
    searchSettings.annotationTable = nullptr;

    setDoubleOption(searchSettings.popen, el.attribute(GAP_OPEN_PROBAB_OPTION_TAG), stateInfo);

    setDoubleOption(searchSettings.pextend, el.attribute(GAP_EXTEND_PROBAB_OPTION_TAG), stateInfo);

    outputDir = el.attribute(OUTPUT_DIR_TAG);

    if (queryFilename.isEmpty()) {
        stateInfo.setError(L10N::badArgument("query sequence filename"));
        return;
    }
    queryFilename = env->getVar("COMMON_DATA_DIR") + "/" + queryFilename;

    searchSettings.querySequenceUrl = queryFilename;
}

void GTest_UHMM3Phmmer::setAndCheckArgs() {
    assert(!stateInfo.hasError());

    if (dbFilename.isEmpty()) {
        stateInfo.setError(L10N::badArgument("db sequence filename"));
        return;
    }
    dbFilename = env->getVar("COMMON_DATA_DIR") + "/" + dbFilename;

    if (outputDir.isEmpty()) {
        stateInfo.setError("output_dir_is_empty");
        return;
    }

    outputDir = env->getVar("TEMP_DATA_DIR") + "/" + outputDir;
}

static void setDoubleOption(double& num, const QDomElement& el, const QString& optionName, TaskStateInfo& si) {
    if (si.hasError()) {
        return;
    }
    QString numStr = el.attribute(optionName);
    if (numStr.isEmpty()) {
        return;
    }
    bool ok = false;
    double ret = numStr.toDouble(&ok);
    if (!ok) {
        si.setError(QString("cannot_parse_double_number_from %1. Option: %2").arg(numStr).arg(optionName));
        return;
    }
    num = ret;
}

static void setBooleanOption(bool& ret, const QDomElement& el, const QString& opName, TaskStateInfo& si) {
    if (si.hasError()) {
        return;
    }
    QString str = el.attribute(opName).toLower();
    if (!str.isEmpty() && "n" != str && "no" != str) {
        ret = true;
    } else {
        ret = false;
    }
}

static void setIntegerOption(int& num, const QDomElement& el, const QString& optionName, TaskStateInfo& si) {
    if (si.hasError()) {
        return;
    }
    QString numStr = el.attribute(optionName);
    if (numStr.isEmpty()) {
        return;
    }

    bool ok = false;
    int ret = numStr.toInt(&ok);
    if (!ok) {
        si.setError(QString("cannot_parse_integer_number_from %1. Option: %2").arg(numStr).arg(optionName));
        return;
    }
    num = ret;
}

void GTest_UHMM3Phmmer::setSearchTaskSettings(PhmmerSearchSettings& settings, const QDomElement& el, TaskStateInfo& si) {
    setDoubleOption(settings.e, el, GTest_UHMM3Search::SEQ_E_OPTION_TAG, si);
    setDoubleOption(settings.t, el, GTest_UHMM3Search::SEQ_T_OPTION_TAG, si);
    setDoubleOption(settings.z, el, GTest_UHMM3Search::Z_OPTION_TAG, si);
    setDoubleOption(settings.f1, el, GTest_UHMM3Search::F1_OPTION_TAG, si);
    setDoubleOption(settings.f2, el, GTest_UHMM3Search::F2_OPTION_TAG, si);
    setDoubleOption(settings.f3, el, GTest_UHMM3Search::F3_OPTION_TAG, si);
    setDoubleOption(settings.domE, el, GTest_UHMM3Search::DOM_E_OPTION_TAG, si);
    setDoubleOption(settings.domT, el, GTest_UHMM3Search::DOM_T_OPTION_TAG, si);
    setDoubleOption(settings.domZ, el, GTest_UHMM3Search::DOM_Z_OPTION_TAG, si);

    setDoubleOption(settings.eft, el, "Eft", si);
    setIntegerOption(settings.efl, el, "EfL", si);
    setIntegerOption(settings.efn, el, "EfN", si);
    setIntegerOption(settings.evl, el, "EvL", si);
    setIntegerOption(settings.evn, el, "EvN", si);

    setBooleanOption(settings.doMax, el, GTest_UHMM3Search::MAX_OPTION_TAG, si);
    setBooleanOption(settings.noBiasFilter, el, GTest_UHMM3Search::NOBIAS_OPTION_TAG, si);
    setBooleanOption(settings.noNull2, el, GTest_UHMM3Search::NONULL2_OPTION_TAG, si);

    setIntegerOption(settings.seed, el, GTest_UHMM3Search::SEED_OPTION_TAG, si);
}

void GTest_UHMM3Phmmer::prepare() {
    assert(!hasError() && phmmerTask == nullptr);
    setAndCheckArgs();
    if (hasError()) {
        return;
    }
    searchSettings.workingDir = outputDir;
    searchSettings.targetSequenceUrl = dbFilename;
    searchSettings.querySequenceUrl = queryFilename;
    phmmerTask = new PhmmerSearchTask(searchSettings);
    phmmerTask->addListeners(QList<ExternalToolListener*>() << new OutputCollector());
    addSubTask(phmmerTask);
}

QList<Task*> GTest_UHMM3Phmmer::onSubTaskFinished(Task* subTask) {
    QList<Task*> res;
    if (subTask == phmmerTask) {
        auto collector = dynamic_cast<OutputCollector*>(phmmerTask->getListener(0));
        if (collector != nullptr) {
            QString hmmSearchLog = collector->getLog();
            // TODO: check non empty log and file existence after writing
            QFile file(searchSettings.workingDir + "/output.txt");
            file.open(QIODevice::WriteOnly);
            file.write(hmmSearchLog.toLatin1());
            file.close();
            delete collector;
        }
    }
    return res;
}

Task::ReportResult GTest_UHMM3Phmmer::report() {
    return ReportResult_Finished;
}

/****************************************
 * GTest_UHMM3PhmmerCompare
 ****************************************/
const QString GTest_UHMM3PhmmerCompare::ACTUAL_OUT_FILE_TAG = "actualOut";
const QString GTest_UHMM3PhmmerCompare::TRUE_OUT_FILE_TAG = "trueOut";

void GTest_UHMM3PhmmerCompare::init(XMLTestFormat*, const QDomElement& el) {
    trueOutFilename = el.attribute(TRUE_OUT_FILE_TAG);
    actualOutFilename = el.attribute(ACTUAL_OUT_FILE_TAG);
}

void GTest_UHMM3PhmmerCompare::setAndCheckArgs() {
    if (trueOutFilename.isEmpty()) {
        stateInfo.setError(L10N::badArgument("true out filename"));
        return;
    }
    trueOutFilename = env->getVar("COMMON_DATA_DIR") + "/" + trueOutFilename;

    if (actualOutFilename.isEmpty()) {
        stateInfo.setError("actual_out_filename_is_empty");
        return;
    }
    actualOutFilename = env->getVar("TEMP_DATA_DIR") + "/" + actualOutFilename;
}

Task::ReportResult GTest_UHMM3PhmmerCompare::report() {
    assert(!hasError());
    setAndCheckArgs();
    if (hasError()) {
        return ReportResult_Finished;
    }

    UHMM3SearchResult trueRes;
    UHMM3SearchResult actualRes;
    try {
        trueRes = GTest_UHMM3SearchCompare::getSearchResultFromOutput(trueOutFilename);
        actualRes = GTest_UHMM3SearchCompare::getSearchResultFromOutput(actualOutFilename);
    } catch (const QString& ex) {
        stateInfo.setError(ex);
    } catch (...) {
        stateInfo.setError("undefined_error_occurred");
    }

    if (hasError()) {
        return ReportResult_Finished;
    }

    GTest_UHMM3SearchCompare::generalCompareResults(actualRes, trueRes, stateInfo);

    return ReportResult_Finished;
}

}  // namespace U2
