#pragma once

#include "component.hpp"
#include "sparse_dbg.hpp"
namespace dbg {
    inline void printFasta(std::ostream &out, const Component &component, bool mask = false) {
        size_t cnt = 0;
        size_t masked_cnt = 1;
        for (Edge &edge : component.edges()) {
            Sequence edge_seq = edge.start()->seq + edge.seq;
            Vertex &end = *edge.end();
            out << ">" << cnt << "_";
            if(mask && !component.contains(*edge.start())) {
                out << masked_cnt << "0000";
                masked_cnt++;
            }
            out << edge.start()->hash() << int(edge.start()->isCanonical());
            out << "_";
            if(mask && !component.contains(*edge.end())) {
                out << masked_cnt << "0000";
                masked_cnt++;
            }
            out << end.hash() << int(end.isCanonical());
            out << "_" << edge.size() << "_" << edge.getCoverage() << "\n";
            out << edge_seq << "\n";
            cnt++;
        }
    }

    inline void printAssembly(std::ostream &out, const Component &component) {
        size_t cnt = 0;
        for (Edge &edge : component.edgesUnique()) {
            Sequence edge_seq = edge.start()->seq + edge.seq;
            Vertex &end = *edge.end();
            out << ">" << cnt << "_" << edge.start()->hash() << int(edge.start()->isCanonical()) <<
                        "_" << end.hash() << int(end.isCanonical()) << "_" << edge.size()
                        << "_" << edge.getCoverage() << "\n";
            out << edge_seq << "\n";
            cnt++;
        }
    }

    inline Sequence cheatingCutStart(Sequence seq, unsigned char c, size_t min_size, size_t k) {
        size_t pos = seq.size() - min_size;
        while(pos > 0 && seq[pos + k] != c)
            pos--;
        return seq.Subseq(pos, seq.size());
    }

    inline void cheatingFasta(const std::experimental::filesystem::path &outf, const Component &component, size_t cut) {
        std::ofstream out;
        out.open(outf);
        size_t cnt = 0;
        size_t k = component.graph().hasher().getK();
        for (Edge &edge : component.edges()) {
            Sequence edge_seq = edge.start()->seq + edge.seq;
            if(edge.size() > cut) {
                if(!component.contains(*edge.start())) {
                    edge_seq = cheatingCutStart(edge_seq, edge.seq[0], cut, k);
                } else if(!component.contains(*edge.end())) {
                    edge_seq = !cheatingCutStart(!edge_seq, edge.rc().seq[0], cut, k);
                }
            }
            Vertex &end = *edge.end();
            out << ">" << cnt << "_" << edge.start()->hash() << int(edge.start()->isCanonical()) <<
                "_" << end.hash() << int(end.isCanonical()) << "_" << edge_seq.size() - edge.start()->seq.size()
                << "_" << edge.getCoverage() << "\n";
            out << edge_seq << "\n";
            cnt++;
        }
        out.close();
    }

    inline void printFasta(const std::experimental::filesystem::path &outf, const Component &component, bool mask = false) {
        std::ofstream out;
        out.open(outf);
        printFasta(out, component, mask);
        out.close();
    }

    inline void printAssembly(const std::experimental::filesystem::path &outf, const Component &component) {
        std::ofstream out;
        out.open(outf);
        printAssembly(out, component);
        out.close();
    }

    inline void printGFA(std::ostream &out, const Component &component, bool calculate_coverage) {
        out << "H\tVN:Z:1.0" << std::endl;
        size_t cnt = 0;
        std::unordered_map<const Edge *, std::string> eids;
        for (Edge &edge : component.edges()) {
            if (edge.start()->isCanonical(edge)) {
                eids[&edge] = edge.oldId();
                eids[&edge.rc()] = edge.oldId();
                if (calculate_coverage)
                    out << "S\t" << edge.oldId() << "\t" << edge.start()->seq << edge.seq
                        << "\tKC:i:" << edge.intCov() << "\n";
                else
                    out << "S\t" << edge.oldId() << "\t" << edge.start()->seq << edge.seq << "\n";
            }
        }
        for (Vertex &vertex : component.verticesUnique()) {
            for (const Edge &out_edge : vertex) {
                std::string outid = eids[&out_edge];
                bool outsign = vertex.isCanonical(out_edge);
                for (const Edge &inc_edge : vertex.rc()) {
                    std::string incid = eids[&inc_edge];
                    bool incsign = !vertex.rc().isCanonical(inc_edge);
                    out << "L\t" << incid << "\t" << (incsign ? "+" : "-") << "\t" << outid << "\t"
                        << (outsign ? "+" : "-") << "\t" << component.graph().hasher().getK() << "M" << "\n";
                }
            }
        }
    }

    inline void printGFA(const std::experimental::filesystem::path &outf, const Component &component, bool calculate_coverage) {
        std::ofstream out;
        out.open(outf);
        printGFA(out, component, calculate_coverage);
        out.close();
    }
}