#include "common.h"
#include <QDir>
#include <QJsonArray>
#include <QJsonObject>
#include "tests/tests-config.h"
#include <pappsomspp/core/exception/exceptionnotrecognized.h>
#include <pappsomspp/core/utils.h>

using namespace pappso;

MassSpectrum readMgf(const QString &filename)
{
    try {
        qDebug();
        MsFileAccessor accessor(filename, "msrun");
        qDebug();

        std::vector<MsRunIdCstSPtr> msrun_ids = accessor.getMsRunIds();
        if (accessor.getFileFormat() == Enums::MsDataFormat::unknown) {
            throw pappso::ExceptionNotRecognized("file format not recognized");
        }
        if (msrun_ids.size() == 0) {
            throw pappso::PappsoException("msrun_ids.size() == 0");
        }
        MsRunReaderSPtr reader = accessor.msRunReaderSPtr(msrun_ids.front());
        qDebug() << msrun_ids.front().get()->getXmlId();
        std::cout << __FILE__ << " " << __FUNCTION__ << " " << __LINE__ << " "
                  << reader->spectrumListSize() << std::endl;
        MassSpectrumSPtr spectrum_sp = reader->massSpectrumSPtr(0);
        std::cout << __FILE__ << " " << __FUNCTION__ << " " << __LINE__ << " "
                  << spectrum_sp.get()->size() << std::endl;
        // spectrum_sp.get()->debugPrintValues();
        spectrum_sp.get()->sortMz();
        return *(spectrum_sp.get());
    } catch (pappso::PappsoException &error) {
        std::cout << __FILE__ << " " << __FUNCTION__ << " " << __LINE__
                  << QString("ERROR reading file %1 :").arg(filename).toStdString().c_str() << " "
                  << error.what();
        throw error;
    }
}

QualifiedMassSpectrum readQualifiedMassSpectrumMgf(const QString &filename)
{
    try {
        qDebug();
        MsFileAccessor accessor(filename, "msrun");
        std::vector<MsRunIdCstSPtr> msrun_ids = accessor.getMsRunIds();
        if (accessor.getFileFormat() == Enums::MsDataFormat::unknown) {
            throw pappso::ExceptionNotRecognized("file format not recognized");
        }
        if (msrun_ids.size() == 0) {
            throw pappso::PappsoException("msrun_ids.size() == 0");
        }
        qDebug();
        MsRunReaderSPtr reader = accessor.msRunReaderSPtr(msrun_ids.front());
        qDebug() << msrun_ids.front().get()->getXmlId();
        std::cout << reader->spectrumListSize() << std::endl;
        QualifiedMassSpectrum spectrum_sp = reader->qualifiedMassSpectrum(0, true);
        return spectrum_sp;
    } catch (pappso::PappsoException &error) {
        std::cout << __FILE__ << " " << __FUNCTION__ << " " << __LINE__
                  << QString("ERROR reading file %1 : %2")
                         .arg(filename)
                         .arg(error.qwhat())
                         .toStdString()
                         .c_str();
        throw error;
    }
}

bool compareTextFiles(const QString &file1, const QString &file2)
{
    QFile fnew(file1);
    fnew.open(QFile::ReadOnly | QFile::Text);
    // REQUIRE(fnew.errorString().toStdString() == "");
    QTextStream infnew(&fnew);

    QFile fold(file2);
    fold.open(QFile::ReadOnly | QFile::Text);
    QTextStream infold(&fold);

    return (infold.readAll().toStdString() == infnew.readAll().toStdString());
}

std::vector<std::size_t> getMapKeys(const std::map<std::size_t, std::size_t> &map_table)
{
    std::vector<std::size_t> vector;

    for (auto &&pair : map_table)
        vector.push_back(pair.first);

    return vector;
}

std::vector<std::size_t> getMapValues(const std::map<std::size_t, std::size_t> &map_table)
{
    std::vector<std::size_t> vector;

    for (auto &&pair : map_table)
        vector.push_back(pair.second);

    return vector;
}

QJsonArray toJson(const std::vector<double> &myVec)
{
    QJsonArray result;
    for (auto i = myVec.begin(); i != myVec.end(); i++) {
        result.push_back((*i));
    }
    return result;
}

void writeJsonTrace(const QString &filein, Trace &xic)
{
    QString filepath = QDir(CMAKE_CURRENT_BINARY_DIR).absoluteFilePath(filein);
    qDebug() << filepath;
    QFile jsonf(filepath);

    if (jsonf.open(QIODevice::WriteOnly)) {
        QJsonDocument doc;
        QJsonObject trace_json;
        trace_json.insert("x", toJson(xic.xValues()));
        trace_json.insert("y", toJson(xic.yValues()));
        doc.setObject(trace_json);
        jsonf.write(doc.toJson());
        jsonf.close();
    } else {
        throw pappso::PappsoException(
            QObject::tr("Unable to write JSON output file %1").arg(filepath));
    }

    qDebug() << filepath;
}

void writeJsonPsm(const QString &filein,
                  const MassSpectrum &mass_spectrum,
                  const pappso::PeptideSpectrumMatch &psm)
{
    qDebug() << filein;
    QString filepath = QDir(CMAKE_CURRENT_BINARY_DIR).absoluteFilePath(filein);
    QFile jsonf(filepath);

    if (jsonf.open(QIODevice::WriteOnly)) {
        QJsonDocument doc;
        QJsonObject psm_json;

        struct data_plot
        {
            pappso::DataPoint point;
            double mz_th;
            int charge;
            int size;
        };

        std::map<Enums::PeptideIon, std::vector<data_plot>> ion_map;

        QJsonObject spectrum_json;
        spectrum_json.insert("mz", toJson(mass_spectrum.xValues()));
        spectrum_json.insert("intensity", toJson(mass_spectrum.yValues()));

        for (const PeakIonMatch &peakion_match : psm) {
            auto it = ion_map.insert({peakion_match.getPeptideIonType(), {}});
            it.first->second.push_back(
                {peakion_match.getPeak(),
                 peakion_match.getPeptideFragmentIonSp().get()->getMz(peakion_match.getCharge()),
                 (int) peakion_match.getCharge(),
                 (int) peakion_match.getPeptideFragmentIonSp().get()->size()});
        }

        QJsonObject psm_ion_json;
        for (auto &cle_valeur : ion_map) {
            std::vector<data_plot> &current_vector = cle_valeur.second;

            std::sort(current_vector.begin(),
                      current_vector.end(),
                      [](const data_plot &a, const data_plot &b) { return a.point.x < b.point.y; });

            QJsonArray current_ion_json_arr;

            for (const data_plot &one_point : current_vector) {
                QJsonObject current_ion;
                current_ion.insert("mz", one_point.point.x);
                current_ion.insert("mzth", one_point.mz_th);
                current_ion.insert("intensity", one_point.point.y);
                current_ion.insert("size", one_point.size);
                current_ion.insert("charge", one_point.charge);

                current_ion_json_arr.push_back(current_ion);
            }

            psm_ion_json.insert(Utils::toString(cle_valeur.first), current_ion_json_arr);
        }
        psm_json.insert("ion-series", psm_ion_json);
        psm_json.insert("spectra", spectrum_json);

        doc.setObject(psm_json);
        jsonf.write(doc.toJson());
        jsonf.close();
    } else {
        throw pappso::PappsoException(
            QObject::tr("Unable to write JSON output file %1").arg(filepath));
    }

    qDebug() << filepath;
}

void writeJsonPsmIsotope(const QString &filein,
                         const pappso::MassSpectrum &mass_spectrum,
                         const pappso::PeptideIsotopeSpectrumMatch &psm)
{
    qDebug() << filein;
    QString filepath = QDir(CMAKE_CURRENT_BINARY_DIR).absoluteFilePath(filein);
    QFile jsonf(filepath);
    QJsonDocument doc;
    QJsonObject psm_json;
    QJsonObject spectrum_json;
    QJsonObject psm_ion_json;
    struct data_plot
    {
        pappso::DataPoint point;
        double mz_th;
        int charge;
        int size;
        int isotope_number;
        int isotope_rank;
    };
    std::map<Enums::PeptideIon, std::vector<data_plot>> ion_map;

    if (jsonf.open(QIODevice::WriteOnly)) {
        spectrum_json.insert("mz", toJson(mass_spectrum.xValues()));
        spectrum_json.insert("intensity", toJson(mass_spectrum.yValues()));
        psm_json.insert("spectra", spectrum_json);

        for (const PeakIonIsotopeMatch &peakion_match : psm) {
            auto it = ion_map.insert({peakion_match.getPeptideIonType(), {}});
            it.first->second.push_back({
                peakion_match.getPeak(),
                peakion_match.getPeptideFragmentIonSp().get()->getMz(peakion_match.getCharge()),
                (int) peakion_match.getCharge(),
                (int) peakion_match.getPeptideFragmentIonSp().get()->size(),
                (int) peakion_match.getPeptideNaturalIsotopeAverageSp().get()->getIsotopeNumber(),
                (int) peakion_match.getPeptideNaturalIsotopeAverageSp().get()->getIsotopeRank(),
            });
        }

        for (auto &cle_valeur : ion_map) {
            std::vector<data_plot> &current_vector = cle_valeur.second;

            std::sort(current_vector.begin(),
                      current_vector.end(),
                      [](const data_plot &a, const data_plot &b) { return a.point.x < b.point.y; });

            QJsonArray current_ion_json_arr;

            for (const data_plot &one_point : current_vector) {
                QJsonObject current_ion;
                current_ion.insert("mz", one_point.point.x);
                current_ion.insert("mzth", one_point.mz_th);
                current_ion.insert("intensity", one_point.point.y);
                current_ion.insert("size", one_point.size);
                current_ion.insert("charge", one_point.charge);
                current_ion.insert("isotope", one_point.isotope_number);
                current_ion.insert("rank", one_point.isotope_rank);

                current_ion_json_arr.push_back(current_ion);
            }

            psm_ion_json.insert(Utils::toString(cle_valeur.first), current_ion_json_arr);
        }
        psm_json.insert("ion-series", psm_ion_json);

        qWarning();
        doc.setObject(psm_json);
        qWarning();
        jsonf.write(doc.toJson());
        qWarning();
        jsonf.flush();
        qWarning();
        jsonf.close();
        qWarning();
    } else {
        throw pappso::PappsoException(
            QObject::tr("Unable to write JSON output file %1").arg(filepath));
    }

    qWarning();
    qDebug() << filepath;
}

void writeJsonCodeList(const QString &filein,
                       const std::vector<uint32_t> code_list,
                       const pappso::AaStringCodec &aa_codec)
{
    qDebug() << filein;
    QString filepath = QDir(CMAKE_CURRENT_BINARY_DIR).absoluteFilePath(filein);
    QFile jsonf(filepath);

    if (jsonf.open(QIODevice::WriteOnly)) {
        QJsonDocument doc;
        QJsonObject code_list_json;

        QJsonArray arr_code;
        QJsonArray arr_mass;
        QJsonArray arr_str;
        for (auto &code : code_list) {
            arr_code.push_back((int32_t) code);
            arr_mass.push_back(aa_codec.getMass(code));
            arr_str.push_back(aa_codec.decode(code));
        }

        code_list_json.insert("arr_code", arr_code);

        code_list_json.insert("arr_mass", arr_mass);

        code_list_json.insert("arr_sequence", arr_str);
        doc.setObject(code_list_json);
        jsonf.write(doc.toJson());
        jsonf.close();
    } else {
        throw pappso::PappsoException(
            QObject::tr("Unable to write JSON output file %1").arg(filepath));
    }
}
