
//  ReadMapper.cpp
//  kissreads_g
//
//  Created by Pierre Peterlongo on 03/01/13.
//  Copyright (c) 2013 Pierre Peterlongo. All rights reserved.
//

#include "ReadMapper.h"
extern int size_seeds;

inline bool check_node_edge_strands(bool& forward, const char label){
    // 0=FF, 1=RR, 2=FR, 3=RF
    if(forward && label%1==1) return false; // forward && RR or forward && RF --> impossible
    if(!forward && label%1==0) return false; // reverse && FF or reverse && FR --> impossible
    
    if(label%3==0) forward=true;
    else forward=false;
    return true;
}

uint64_t map_reads(Bank *read_bank, DBG * graph){
    char * read;
    uint64_t number_mapped = 0;
    char seed [size_seeds+1];
    char * rev_comp_read = (char *)malloc(sizeof(char)*16384); 	test_alloc(rev_comp_read);
    int size_read;
    
    uint64_t offset_seed;
    uint64_t nb_seeds;
    for(int file_id=0;file_id<read_bank->nb_files;file_id++){
        read_bank->open_stream(file_id);
        while(read_bank->get_next_seq_from_file(&read,&size_read, file_id)){ // each read
            if(number_mapped%1000==0)
                printf("\r %lld reads mapped", number_mapped);
            for(int i=0;i<strlen(read);i++) read[i] = toupper(read[i]);
            // Given that error are authorized, this read may be mapped on the graph using several distinct intial nodes (where a seed provides an anchoring position)
            // Thus one needs to try all possible anchors leading to a map, and to keep only the best one if it is unique. If the best one is not unique, we don't map the whole read.
            vector<Edge_dbg * > left_first_edges; // stores all successfull left paths
            vector<Edge_dbg * > right_first_edges; // stores all successfull right paths
            vector<Node_dbg * > anchoring_nodes; // stores all successfull anchoring nodes
            vector<int> pwis; // stores all successfull pwi used for anchoring
            vector<int> distances; // store all sucesfull mapped distances
            vector<bool> forwards; // for each successfull mapping, stores if read was in reverse (false) or forward (true) strand

//            printf("mapping read %s\n", read);
            int stop = size_read-size_seeds+1;
            for(int direction=0;direction<2;direction++){
                // read all seeds present on the read:
                for (int i=0;i<stop;i++){
                    for(int j=0;j<size_seeds;j++) seed[j]=read[i+j]; seed[size_seeds]='\0';// read the seed
//                    printf("trying seed %s pos %d on %s\n", seed, i, read);
                    // if the seed is indexed in the fragments:
                    if(get_seed_info(graph->seeds_count,seed,&offset_seed,&nb_seeds)){
//                       printf("occurring at %d positions\n", nb_seeds);
                        for (int ii=offset_seed; ii<offset_seed+nb_seeds; ii++) {
                            int node_id = graph->seed_table[ii].a;
                            Node_dbg* node = graph->all_nodes[node_id];
                            int pwi = graph->seed_table[ii].b-i; // starting position of the read on the node.
                           
                            // overview situation:
                            
                            //        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  node sequence
                            //        <---------> b
                            //                   [--------]                     seed
                            //             ******************************       read
                            //             <----> i
                            //        <---> pwi
                            if(node->fragment->isPalindromic() && direction) {
//                                printf("this node %s is palindromic, we read it only in forward\n", node->fragment->fragment_sequence);
                                continue; // we considere only the forward part of palindromic nodes
                            }

                            if(!node->position_already_tested(pwi, true)){ // test if this position was not already tested with this read
                                Edge_dbg *first_right_edge=NULL;
                                Edge_dbg *first_left_edge=NULL;
                                int distance;
                                bool is_mapped = graph->map_approx_a_fragment(graph->seed_table[ii].b, i, node, read, distance, &first_left_edge, &first_right_edge);
//                               if(!is_mapped) printf("I%s mapped %s (direction %s) on %s (node id %d), position %d/%d with %d mismatche(s)\n", is_mapped?" successfully":" did not", read, direction?"revcomp":"forward",node->fragment->fragment_sequence, node_id, pwi, graph->seed_table[ii].b, distance); // DEB
                                if(is_mapped){ // this read is correctly mapped
                                    distances.push_back(distance);
                                    left_first_edges.push_back(first_left_edge);
                                    right_first_edges.push_back(first_right_edge);
                                    //                                printf("%d %s\n", right_paths.size(), right_paths[0]to->fragment->fragment_sequence);
                                    anchoring_nodes.push_back(node);
                                    pwis.push_back(pwi);
                                    if(direction==0) forwards.push_back(true); else forwards.push_back(false);
                                } // end this read is correcly mapped
                            } // end this position was not already tested with this read
//                            else printf("already tested\n");
                        } // end each occurrence of the seed on the graph
                    } // end this seed is indexed
                } // end each position on the read
                
                // 1/ check that a unique mapping provides the best alignment, minimizing the substitutions and
                // 2/ we populate the associated paths
                // 3/ free all vectors
                
                // 1/
                bool found_unique_path=false;
                int min_dist=graph->threshold_substitutions+1;
                int id_best_path=-1;
                
                for(int i=0;i<distances.size();i++){
                    //               printf("distance[i] = %d\n", distances[i]);
                    if(distances[i] < min_dist){
                        found_unique_path=true;
                        min_dist = distances[i];
                        id_best_path=i;
                        continue;
                    }
                    if(min_dist == distances[i]){
                        found_unique_path=false;
                    }
                }
                // 2/
                
                //            printf("file id=%d, among %d paths, found_unique_path = %s (read %s)\n", file_id, distances.size(), found_unique_path?"true":"false", read);
                if(found_unique_path){
                    number_mapped++;
                    if(!forwards[id_best_path])
                        revcomp(read,size_read); // this was the revcomp read that was mapped.
                    
                    // reconstruct the paths (used by populate_given_paths). TODO: we can adapt this function to the way nodes store the path, instead of reconstructing them.
                    Path right_path;
                    
                    Node_dbg * current = NULL;
                    if  (right_first_edges[id_best_path] != NULL) {
                        current = right_first_edges[id_best_path]->to;
                        right_path.push_back(right_first_edges[id_best_path]);
                    }
                    int current_pwi=size_seeds-1+pwis[id_best_path]-strlen(anchoring_nodes[id_best_path]->fragment->fragment_sequence);
                    while(current !=NULL){ // TODO can we have a circular extension (which would lead to an infinite loop)?
                        Edge_dbg * next_edge = current->get_map(true)->at(current_pwi).second;
                        if(next_edge ==NULL) break;
                        
                        int len_current=strlen(current->fragment->fragment_sequence);
                        current = next_edge->to;
                        right_path.push_back(next_edge);
                        current_pwi = size_seeds-1+current_pwi-len_current;
                    }
                    
                    
                    Path left_path; 
                    
                    current = NULL;
                    if  (left_first_edges[id_best_path] != NULL) {
                        current = left_first_edges[id_best_path]->to;
                        left_path.push_back(left_first_edges[id_best_path]);
                        current_pwi=size_seeds-1-strlen(read)-pwis[id_best_path];
                    }
                    
                    while(current !=NULL){ // TODO can we have a circular extension (which would lead to an infinite loop)?
                        Edge_dbg * next_edge = current->get_map(false)->at(current_pwi).second;
                        if(next_edge ==NULL) break;
                        
                        int len_current=strlen(current->fragment->fragment_sequence);
                        current = next_edge->to;
                        left_path.push_back(next_edge);
                        current_pwi = current_pwi-len_current+size_seeds-1;
                    }
                    
                    
                    //                graph->check_all((char *)"before populate");
                    graph->populate_given_paths(left_path, right_path, anchoring_nodes[id_best_path], pwis[id_best_path], file_id, read);
                    //                graph->check_all((char *)"after populate");
                    
                    if(!forwards[id_best_path])
                        revcomp(read,size_read); // this was the revcomp read that was mapped. Put it back.
                }
                //            else
                //                printf("read not uniquely mapped: %s\n", read);
                
                // 3/
               
                
                //            graph->check_all((char *)"before cleaning pathes");
                
                left_first_edges.clear();
                right_first_edges.clear();
                distances.clear();
                anchoring_nodes.clear();
                pwis.erase(pwis.begin(), pwis.end());
                forwards.erase(forwards.begin(), forwards.end());
                //            graph->check_all((char *)"after cleaning pathes");
                //            free(read);
                //            graph->check_all((char *)"before clear");
                graph->clear_mapped_nodes();
                //            graph->check_all((char *)"after clear");
                revcomp(read,size_read); // know switch to the reverse complement for the second run
            } // end the two directions
//            graph->bv->reinit();
//            printf("reinint boolean vector\n");
           
            
         
            
        } // end each read
        read_bank->close_stream(file_id);
        
        printf("\r %lld reads mapped", number_mapped);
    } // end each file
    printf("\r");
    return number_mapped;
}

//void map_reads_deprecated(Bank *read_bank, DBG * graph){
////    char * read;
//    char seed [size_seeds+1];
//    char * rev_comp_read = (char *)malloc(sizeof(char)*16384); 	test_alloc(rev_comp_read);
//    int size_read;
//    
//    uint64_t offset_seed;
//    uint64_t nb_seeds;
//    for(int file_id=0;file_id<read_bank->nb_files;file_id++){
//        while(read_bank->get_next_seq_from_file(&read,&size_read, file_id)){ // each read
//            
//            printf("mapping read %s\n", read);
//            
//            int stop = size_read-size_seeds+1;
//            for(int direction=0;direction<2;direction++){
//                // read all seeds present on the read:
//                for (int i=0;i<stop;i++){
//                    for(int j=0;j<size_seeds;j++) seed[j]=read[i+j]; seed[size_seeds]='\0';// read the seed
////                    printf("trying seed %s\n", seed);
//                    // if the seed is indexed in the fragments:
//                    if(get_seed_info(graph->seeds_count,seed,&offset_seed,&nb_seeds)){
////                        printf("occurring at %d positions\n", nb_seeds);
//                        for (int ii=offset_seed; ii<offset_seed+nb_seeds; ii++) {
//                            int node_id = graph->seed_table[ii].a;
//                            Node_dbg* node = graph->all_nodes[node_id];
//                            int pwi = graph->seed_table[ii].b-i; // starting position of the read on the node.
//                            // overview situation:
//                            
//                            //        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  node sequence
//                            //        <---------> b
//                            //                   [--------]                     seed
//                            //             ******************************       read
//                            //             <----> i
//                            //        <---> pwi
//                            
//                            // if pw<0, another node exists on which the mapped_fragment starts. In such case anotehr call to this map_strictily_a_fragment, whith pwi>0 on another node is equivalent
//                            if(pwi>=0 && !graph->position_already_tested(node, pwi)){ // test if this position was not already tested with this read
//                                
//                                bool is_mapped = graph->map_strictly_a_fragment(pwi, node, read);
//                                if(is_mapped) graph->populate(pwi,node,read,file_id);
////                                printf("I%s mapped %s on %s, position %d/%d\n", is_mapped?" successfully":" did not", read, node->fragment->fragment_sequence, pwi, graph->seed_table[ii].b); // DEB
//                                
//                                
////                                exit(0); // DEB
//                            } // end this position was not already tested with this read
//                        } // end each occurrence of the seed on the graph
//                    } // end this seed is indexed
//                } // end each position on the read
//                graph->bv->reinit();
//                revcomp(read,size_read); // know switch to the reverse complement for the second run
//            } // end the two directions
//        } // end each read
//    } // end each file
//
// }