
//  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, vector<DBG*> dbgs){
    char * read;
    uint64_t number_mapped = 0;
    uint64_t number_tested = 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 coded_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_tested%100000==0)
                printf("\r %lld read%s mapped among %lld read%s tested", number_mapped, number_mapped>1?"s":"", number_tested, number_tested>1?"s":"");
            number_tested++;
//            printf("mapping read1 of len %d\n", strlen(read));
            for(int i=0;i<strlen(read);i++) read[i] = toupper(read[i]);
            
            for(int i=0;i<dbgs.size();i++){
                
             DBG *   graph=dbgs[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:
                bool toinit=true;  // we have to init a new seed
                for (int i=0;i<stop;i++){
//                    bool validSeed=true;
                    
                    if(toinit) { // if we have to init a new seed
//                        for(int j=0;j<size_seeds && validSeed;j++)// read all characters to check if its a valid seed
//                            if(!valid_character(read[j+i])){ // if i+j character is not valid
//                                toinit=true; // next new seed will have to be recomputed from scratch
//                                validSeed=false;
//                                i+=j; // don't test the next j+1 next positions (+1 will come from the 'for' loop)
//                            }
//                        if(validSeed)
                            coded_seed=codeSeed_uint64_t(read+i); // init the seed
                        toinit=false;
                    }
                    else { // previous seed was correct, we try to extend it.
//                        if(!valid_character(read[i+size_seeds-1])) { // if the new character is not valid:
//                            toinit=true; // indicate that the next seed is not valid
//                            validSeed=false;
//                            i+=size_seeds-1; // don't check the next k starting positions (+1 will come from the 'for' loop).
//                        }
//                        if(validSeed)
                            coded_seed=updateCodeSeed_uint64_t(read+i,&coded_seed); // utpdate the previous seed with a
                    }
//                    if(!validSeed) continue;
//                    toinit=false; // we passed over a valid seed, we won't have to reinit next ones.
                    
                    
                    
                    
//                    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,&coded_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);
//                               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 graph
            
        } // end each read
        read_bank->close_stream(file_id);
        
        printf("\r %lld read%s mapped among %lld read%s tested", number_mapped, number_mapped>1?"s":"", number_tested, number_tested>1?"s":"");
    } // end each file
    printf("\r");
    return number_mapped;
}
