//***************************************************************************
//* Copyright (c) 2015 Saint Petersburg State University
//* Copyright (c) 2011-2014 Saint Petersburg Academic University
//* All Rights Reserved
//* See file LICENSE for details.
//***************************************************************************

/*
 * paired_info_checker.cpp
 *
 *  Created on: Sep 26, 2011
 *      Author: andrey
 */

#include "../lc_common.hpp"
#include "../lc_io.hpp"

using namespace debruijn_graph;

class PairedInfoChecker {
private:
    Graph& g_;

public:
    PairedInfoChecker(Graph& g) : g_(g) {

    }

    bool IsSymmetric(PairedInfoIndex<Graph>& index) {
        bool result = true;
        for (auto iter = index.begin(); iter != index.end(); ++iter) {
            auto pi = *iter;
            if (pi.size() == 0) {
                continue;
            }
            EdgeId e1 = pi.back().first;
            EdgeId e2 = pi.back().second;

            auto sym_pi = index.GetEdgePairInfo(e2, e1);

            for (auto i1 = pi.begin(); i1 != pi.end(); ++i1) {
                for (auto i2 = sym_pi.begin(); i2 != sym_pi.end(); ++i2) {
                    if (math::eq(i1->d, - i2->d) && !math::eq(i1->weight, i2->weight)) {
                        INFO("No symmetric found ");
                        result = false;
                    }
                }
            }

        }
        return result;
    }

    bool IsConjugateSymmetric(PairedInfoIndex<Graph>& index) {
        bool result = true;
        for (auto iter = index.begin(); iter != index.end(); ++iter) {
            auto pi = *iter;
            if (pi.size() == 0) {
                continue;
            }
            EdgeId e1 = pi.back().first;
            EdgeId e2 = pi.back().second;

            auto conj_pi = index.GetEdgePairInfo(g_.conjugate(e1), g_.conjugate(e2));

            for (auto i1 = pi.begin(); i1 != pi.end(); ++i1) {
                for (auto i2 = conj_pi.begin(); i2 != conj_pi.end(); ++i2) {
                    double new_d = i1->d - g_.length(e1) + g_.length(e2);
                    if (math::eq(i1->d, - new_d) && !math::eq(i1->weight, i2->weight)) {
                        INFO("No conjugate found ");
                        result = false;
                    }
                }
            }

        }
        return result;
    }

    bool AreEqual(PairedInfoIndex<Graph>& index1, PairedInfoIndex<Graph>& index2) {
        bool result = true;
        for (auto iter = index1.begin(); iter != index1.end(); ++iter) {
            auto pi = *iter;
            if (pi.size() == 0) {
                continue;
            }
            EdgeId e1 = pi.back().first;
            EdgeId e2 = pi.back().second;

            auto pi2 = index2.GetEdgePairInfo(e1, e2);

            for (auto i1 = pi.begin(); i1 != pi.end(); ++i1) {
                for (auto i2 = pi2.begin(); i2 != pi2.end(); ++i2) {
                    if (math::eq(i1->d, i2->d) && !math::eq(i1->weight, i2->weight)) {
                        INFO("Unequal weights");
                        result = false;
                    }
                }
            }

        }
        return result;
    }

    void AggregatePairedInfo(PairedInfoIndex<Graph>& clustered, PairedInfoIndex<Graph>& advanced,
            size_t insert_size, size_t read_length,
            PairedInfoIndex<Graph>* result) {

        PairedInfoWeightNormalizer<Graph> normalizer(g_, insert_size, read_length, K);

        for (auto iter = clustered.begin(); iter != clustered.end(); ++iter) {
            auto pi = *iter;
            if (pi.size() == 0) {
                continue;
            }

            EdgeId e1 = pi.back().first;
            EdgeId e2 = pi.back().second;

            auto pi2 = advanced.GetEdgePairInfo(e1, e2);

            for (auto i1 = pi.begin(); i1 != pi.end(); ++i1) {

                auto norm_pi = normalizer.NormalizeWeight(*i1);

                for (auto i2 = pi2.begin(); i2 != pi2.end(); ++i2) {
                    if (math::ge(i1->d, i2->d - lc_cfg::get().u.dev) && math::le(i1->d, i2->d + lc_cfg::get().u.dev) && math::gr(i2->weight, 0.0)) {
                        norm_pi.weight *= lc_cfg::get().es.advanced_coeff;
                    }
                }

                result->AddPairInfo(norm_pi, false);
            }

        }

    }

};


int main() {
    cfg::create_instance(cfg_filename);
    lc_cfg::create_instance(long_contigs::lc_cfg_filename);

    Graph g(K);
    EdgeIndex<K + 1, Graph> index(g);
    PairedInfoIndex<Graph> pairedIndex(g, 0);
    KmerMapper<K+1, Graph> mapper(g);
    Sequence sequence("");

    long_contigs::LoadFromFile(lc_cfg::get().ds.graph_file, &g, sequence, &mapper);
    PairedInfoChecker checker(g);

    DataScanner<Graph> dataScanner(g);

    switch (lc_cfg::get().u.mode) {
    case 1: {
        INFO("Checking " << lc_cfg::get().u.file1);
        dataScanner.loadPaired(lc_cfg::get().u.file1, pairedIndex);
        INFO("Symmetric: " << checker.IsSymmetric(pairedIndex));
        INFO("Conjugate symmetric: " << checker.IsConjugateSymmetric(pairedIndex));
        break;
    }
    case 2: {
        PairedInfoIndex<Graph> pairedIndex2(g, 0);
        dataScanner.loadPaired(lc_cfg::get().u.file1, pairedIndex);
        dataScanner.loadPaired(lc_cfg::get().u.file2, pairedIndex2);

        INFO("Checking " << lc_cfg::get().u.file1 << " and " << lc_cfg::get().u.file2);
        INFO("1 is subset of 2 " << checker.AreEqual(pairedIndex, pairedIndex2));
        INFO("2 is subset of 1 " << checker.AreEqual(pairedIndex2, pairedIndex));
        break;
    }
    case 3: {
        INFO("Aggregating paired info");

        PairedInfoIndex<Graph> cl(g, 0);
        PairedInfoIndex<Graph> ad(g, 0);
        PairedInfoIndex<Graph> res(g, 0);

        dataScanner.loadPaired(lc_cfg::get().u.clustered, cl);
        dataScanner.loadPaired(lc_cfg::get().u.advanced, ad);

        checker.AggregatePairedInfo(cl, ad,
                lc_cfg::get().u.insert_size, lc_cfg::get().u.read_size,
                &res);

        DataPrinter<Graph> dataPrinter(g);
        dataPrinter.savePaired( "./" + lc_cfg::get().paired_info_file_prefix + "IS" + ToString(lc_cfg::get().u.insert_size) + "_RS" + ToString(lc_cfg::get().u.read_size)
                + "_agregate_" + ToString(lc_cfg::get().es.advanced_coeff), res);

        INFO("Done");
        break;

    }
    default: {
        INFO("Unknown mode");
    }
    }

    return 0;
}

