//
// DeBruijnGraph.h
//
//
//  Created by Pierre Peterlongo on 28/11/2012 (bon anniversaire Matt)
//

#include "Fragment.h"
#include "commons.h"
//#include "BooleanVector.h"
#include <vector>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <map>
#ifndef _DBG_H
#define _DBG_H


using namespace std;

class Edge_dbg;


typedef std::vector<Edge_dbg * >      Path;
/**
 * A node contains a starter (considered as direct strand) and several target nodes
 */
class Node_dbg{
public:
    //    long starting_id_in_the_boolean_vector; // used to know if the current tested read (during mapping) was already tested on this fragment at a given position
    Mapped_Fragment * fragment; // the fragment contains a hash_table of mapped reads that is used for
    vector<Edge_dbg*> edges;
    bool visited;
    /**
     * The node id
     */
    int node_id;
    
    //    std::map< int,  distance_Edge_Path> pwi_to_distance_and_path_mapping; // given a pwi value, indicates the distance that was computed and the associated stored path in the graph in case of success(avoids redondant computations)
    
    std::map< int, pair<int, Edge_dbg *> >* left_pwi_to_distance_and_path_mapping; // given a pwi value, indicates the distance that was computed and the associated stored path in the graph in case of success(avoids redondant computations)
    
    std::map< int, pair<int, Edge_dbg *> >* right_pwi_to_distance_and_path_mapping; // given a pwi value, indicates the distance that was computed and the associated stored path in the graph in case of success(avoids redondant computations)
    
    void alloc_coverage_quality(const int nb);
    Node_dbg(char * fragment_sequence, const int number_of_read_sets, const int node_id);
    Node_dbg(Node_dbg * from, const int node_id);
    ~Node_dbg();
    /**
     * Adds a target node to this node.
     * This function is assymetrical: e.g. if it adds A --FF--> B, it does not creates the edge B --RR--> A.
     */
    void add_child(Node_dbg * child, char label);
    
    /**
     * test if a position has already been tested (for instance an anchoring position of a mapped read)
     */
    bool position_already_tested(const int pos, const bool right);
    
    std::map< int, pair<int, Edge_dbg *> >* get_map(const bool right);
    
    void create_map(const bool right);
};

/**
 * An edge of the dbg: a target node and the label
 */
class Edge_dbg {
public:
    /**
     * 0=FF, 1=RR, 2=FR, 3=RF
     */
    char my_label;
    
    /**
     * the target node
     */
    Node_dbg* to;
    
    
    /**
     * = the edge used for leaving using this incomming charcter node
     */
    Edge_dbg * out_edge;
    
    /**
     * Can we set an out edge given an input edge: by default: true
     * becomes false if several distinct out edges are reachable from a single input character
     */
    bool doable;
    
    /**
     * number of reads covering this edge (from each read file) - an edge and its reverse complement have the same coverage.
     */
#ifdef CHARQUAL
    unsigned char * coverage;
#else
    int * coverage;
#endif
    Edge_dbg * find_reverse_edge();
    void alloc_coverage(const int nb);
    Edge_dbg(Node_dbg * to, char my_label): to(to), my_label(my_label){coverage=0; doable=true; out_edge=NULL;};
    ~Edge_dbg(){if(coverage) free(coverage);}
    
    void increase_coverage(const int read_file_id);
};



/**
 * A bidirected de bruijn graph
 */
class DBG {
public:
    vector<Node_dbg*> all_nodes;
    
    vector<Node_dbg*> current_mapped_nodes; // while a fragment is mapped on a node, we add this node in this vector, in order to then clean the pwi_to_distance_and_mapping_success map of this node.
    
    /**
     * Stores all seeds info.
     * an entry = a couple (fragment_id, position on the fragment)
     * The seeds_count table enables for each seed to know were it starts and how many occurrences one has
     */
    couple *seed_table;
    
    /**
     * total number of stored seeds (size of seed_table)
     */
    uint64_t total_seeds;
    
    /**
     * seeds from the fragments. key = seed, value=number of occurrences.
     */
    hash_t seeds_count;
    
    //    /**
    //     * used to mark a position in nodes, for instance to know if it has been already tested with seed
    //     */
    //    BooleanVector * bv;
    
    /**
     * How much substitutions are allowed during the mapping
     */
    int threshold_substitutions;
    
    //    int estimated_size_reads;
    
    void populate_in_and_out(Edge_dbg * incomming_edge, Edge_dbg *  outgoing_edge);
    
    /**
     * Given a size of seeds (in common.h), all clean seeds (with only A,C,G,T) of size size_seeds are indexed in the hash table
     */
    void index_fragments();
    
    DBG(int size_reads=200);
    ~DBG();
    int node_id_to_node_number(int node_id);
    void alloc_coverage_quality(const int nb);
    void check_all(char * text);
    void depth_first_print();
    void depth_first_check();
    int depth_first_clean();
    void clear_visited();
    void index_nodes();
    void set_firstNodeId(char * line); //Set firstNodeId for each substarter
    //    // DEPRECATED
    //    bool map_strictly_a_fragment(const int pwi, const Node_dbg * node, char * mapped_fragment);
    // DEPRECATED
    //    void init_boolean_vector();
    
    /**
     * set a position has already been tested (for instance an anchoring position of a mapped read)
     */
    //    void position_tested(const Node_dbg* node, int pos);
    static void output_graphs(char * original_json, FILE * out, const int nb_read_files, bool consider_coverage, vector<DBG*> dbgs);
    int output_graph(FILE * out, const int nb_read_files, int& edge_id, bool considere_coverage, bool surrounded_by_braces=true);
    bool populate(const int pwi, const Node_dbg * node, char * mapped_fragment, int read_file_id);
    //    int unpalindromize();
    int duplicate_simple_nodes();
    int duplicate_deadend_nodes();
    //    int simplify_simple_paths();
    int simplify_simple_paths_couples();
    int remove_uncovered_edges(const int threshold, const int nb_read_sets);
    int remove_uncovered_nodes(const int threshold, const int nb_read_sets);
    
    void populate_given_paths(vector<Edge_dbg *> left_path, vector<Edge_dbg *>  right_path, const Node_dbg * node, const int pwi, int read_file_id, char * mapped_fragment);
    
    bool map_approx_a_fragment(const int b, const int i, Node_dbg * node, char * fragment, int& distance, Edge_dbg ** first_left_edge, Edge_dbg ** first_right_edge);
    
    void clear_mapped_nodes();
private:
    void    remove_node(const int node_index);
    void depth_first_from(Node_dbg * current_node, bool forward, int indent);
    void depth_first_clean_rec(Node_dbg * current_node, bool forward, int indent);
    void depth_first_check_rec(Node_dbg * current_node, bool forward);
    void last_seeds_rec(const Node_dbg * current_node,
                        const bool forward,
                        char * last_kmers,
                        int size_last_kmers,
                        const bool index,
                        const int fragment_id,
                        const int starting_position);
    void last_seeds(const Node_dbg * current_node, const bool index, const int fragment_id);
    
    void count_seeds(char * fragment, const int first_non_indexed);
    void index_a_fragment(const char * fragment, const int fragment_id, const int starting_position, const int first_non_indexed);
    
    //    // DEPRECATED
    //    bool map_strictly_right_a_fragment(const int pwi, const Node_dbg * node, const char * mapped_fragment, bool forward);
    int  number_of_substitutions(int pos_on_node, int pos_on_fragment, const char * node_sequence, const char * mapped_fragment, const int max=0);
    //    // DEPRECATED
    //    void populated_coverage(const int read_file_id, const int pwi, const Node_dbg * node, const bool forward, const int read_size, bool overlapped_node);
    //    // DEPRECATED
    //    bool populate_right(const int pwi, const Node_dbg * node, const char * mapped_fragment, bool forward, int read_file_id);
    
    bool map_fragment(Node_dbg * current_node, // the node on which the fragment is mapped
                      char * fragment, // the fragment we are trying to map on the graph
                      const int starting_on_fragment,
                      const int current_pwi, // starting position of the fragment on the current node (may be negative)
                      //int starting_pwi, // starting position of the whole fragment on the graph w.r.t. this current_node
                      const bool forward, // is the forward direction of the node is mapped (true) or the reverse (false)
                      int& distance, // stores the (minimal) number of substitutitons used while mapping the fragment on the graph
                      const bool right  // are we extending a right fragment?
    );
    
    // modify the graph
    Node_dbg *  duplicate_one_simple_node(Node_dbg * current, Edge_dbg *in1, Edge_dbg * out1, Edge_dbg *in2, Edge_dbg *out2);
    //    bool merge_node_trio(Node_dbg * central);
    bool merge_node_couples(Node_dbg * a, Edge_dbg * ab);
    
};
#endif // _DBG_H

