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

#pragma once

#include "sequence_mapper.hpp"
#include "omni/edges_position_handler.hpp"
#include "io/wrapper_collection.hpp"

namespace debruijn_graph {

template<class Graph>
class PosFiller {
    typedef typename Graph::EdgeId EdgeId;
    typedef std::shared_ptr<SequenceMapper<Graph>> MapperPtr;
    const Graph& g_;
    MapperPtr mapper_;
    EdgesPositionHandler<Graph>& edge_pos_;

  public:
    PosFiller(const Graph& g, MapperPtr mapper,
              EdgesPositionHandler<Graph>& edge_pos) :
            g_(g), mapper_(mapper), edge_pos_(edge_pos) {

    }

    void Process(const Sequence& s, string name) const {
        //todo stupid conversion!
        return Process(io::SingleRead(name, s.str()));
    }

    void Process(const io::SingleRead& read) const {
        MappingPath<EdgeId> path = mapper_->MapRead(read);
        const string name = read.name();
        int cur_pos = 0;
        TRACE("Contig " << name << " mapped on " << path.size()
            << " fragments.");
        for (size_t i = 0; i < path.size(); i++) {
            EdgeId ei = path[i].first;
            MappingRange mr = path[i].second;
            int len = (int) (mr.mapped_range.end_pos - mr.mapped_range.start_pos);
            if (i > 0)
                if (path[i - 1].first != ei)
                    if (g_.EdgeStart(ei) != g_.EdgeEnd(path[i - 1].first)) {
                        TRACE(
                            "Contig " << name
                            << " mapped on not adjacent edge. Position in contig is "
                            << path[i - 1].second.initial_range.start_pos
                            + 1
                            << "--"
                            << path[i - 1].second.initial_range.end_pos
                            << " and "
                            << mr.initial_range.start_pos + 1
                            << "--" << mr.initial_range.end_pos);
                    }
            edge_pos_.AddEdgePosition(ei, name, mr.initial_range.start_pos,
                                      mr.initial_range.end_pos,
                                      mr.mapped_range.start_pos,
                                      mr.mapped_range.end_pos);
            cur_pos += len;
        }
    }

    void Process(io::SingleStream& stream) const {
        io::SingleRead read;
        while (!stream.eof()) {
            stream >> read;
            Process(read);
        }
    }

  private:
    DECL_LOGGER("PosFiller");
};

template<class gp_t>
void FillPos(gp_t& gp, const string& contig_file, string prefix, bool with_rc = false) {
    PosFiller<typename gp_t::graph_t> pos_filler(gp.g, MapperInstance(gp), gp.edge_pos);
    auto irs = std::make_shared<io::PrefixAddingReaderWrapper>(io::EasyStream(contig_file, with_rc), prefix);
    pos_filler.Process(*irs);
}

template<class gp_t>
void FillPos(gp_t& gp, const Sequence& s, string name) {
    PosFiller<typename gp_t::graph_t> pos_filler(gp.g, MapperInstance(gp), gp.edge_pos);
    pos_filler.Process(s, name);
}



//inline
//void CollectPositions(conj_graph_pack &gp) {
//    gp.edge_pos.clear();
//    if (gp.genome.size() > 0) {
//        FillPos(gp, gp.genome, "ref0");
//        FillPos(gp, !gp.genome, "ref1");
//    }
//
//    if (!cfg::get().pos.contigs_for_threading.empty() &&
//        path::FileExists(cfg::get().pos.contigs_for_threading))
//      FillPos(gp, cfg::get().pos.contigs_for_threading, "thr_", true);
//
//    if (!cfg::get().pos.contigs_to_analyze.empty() &&
//        path::FileExists(cfg::get().pos.contigs_to_analyze))
//      FillPos(gp, cfg::get().pos.contigs_to_analyze, "anlz_", true);
//}

}
