/* ***************************************************************************
 *
 *                              KisSplice
 *      de-novo calling alternative splicing events from RNA-seq data.
 *
 * ***************************************************************************
 *
 * Copyright INRIA
 *  contributors :  Vincent Lacroix
 *                  Pierre Peterlongo
 *                  Gustavo Sacomoto
 *                  Vincent Miele
 *                  Alice Julien-Laferriere
 *                  David Parsons
 *
 * pierre.peterlongo@inria.fr
 * vincent.lacroix@univ-lyon1.fr
 *
 * This software is a computer program whose purpose is to detect alternative
 * splicing events from RNA-seq data.
 *
 * This software is governed by the CeCILL license under French law and
 * abiding by the rules of distribution of free software. You can  use,
 * modify and/ or redistribute the software under the terms of the CeCILL
 * license as circulated by CEA, CNRS and INRIA at the following URL
 * "http://www.cecill.info".

 * As a counterpart to the access to the source code and  rights to copy,
 * modify and redistribute granted by the license, users are provided only
 * with a limited warranty  and the software's author,  the holder of the
 * economic rights,  and the successive licensors  have only  limited
 * liability.

 * In this respect, the user's attention is drawn to the risks associated
 * with loading,  using,  modifying and/or developing or reproducing the
 * software by the user in light of its specific status of free software,
 * that may mean  that it is complicated to manipulate,  and  that  also
 * therefore means  that it is reserved for developers  and  experienced
 * professionals having in-depth computer knowledge. Users are therefore
 * encouraged to load and test the software's suitability as regards their
 * requirements in conditions enabling the security of their systems and/or
 * data to be ensured and,  more generally, to use and operate it in the
 * same conditions as regards security.
 *
 * The fact that you are presently reading this means that you have had
 * knowledge of the CeCILL license and that you accept its terms.
 */




#ifndef COMPRESSED_GRAPH_H
#define COMPRESSED_GRAPH_H

// ===========================================================================
//                               Include Libraries
// ===========================================================================
#include <stdio.h>
#include <string.h>
#include <vector>
#include <stack>
#include <algorithm>
#include <utility>




// ===========================================================================
//                             Include Project Files
// ===========================================================================
#include "CEdge.h"
#include "LabelledCEdge.h"




// ===========================================================================
//                              Class declarations
// ===========================================================================



// ===========================================================================
//                             Declare Used Namespaces
// ===========================================================================
using namespace std;





//! A class representing the compacted De-Bruijn graphs
/*!\
 * \brief Its light structure is used for the detection of bi-connected
 *  component in the graph.
 */

#define MAX 1024


class CGraph
{
  public:
    // =======================================================================
    //                               Constructors
    // =======================================================================

  /*!
   * \brief CGraph constructor.
   * Initializes the attributes according to the node numbers
   * Creates directly the CGraph object with its edges from allEdges
  */
    CGraph( int nbNodes, vector<LabelledCEdge> & allEdges, int kValue );

    // =======================================================================
    //                              Public Attributes
    // =======================================================================
    int k_value;

    // =======================================================================
    //                              Public Methods
    // =======================================================================
    inline int *get_adj_list(int node) const;
    inline int get_adj_list_sz(int node) const;
    inline int get_n_nodes(void) const;

    void insert_unique_edge_dir(int u, int v);
    void insert_unique_edge(int u, int v);
    void destroy_adj_list(void);
    void destroy(void);

  protected:
    // =======================================================================
    //                            Forbidden Constructors
    // =======================================================================

   CGraph(const CGraph &) = delete;
   CGraph() = delete;

    // =======================================================================
    //                              Protected Methods
    // =======================================================================

    // =======================================================================
    //                             Protected Attributes
    // =======================================================================
    // NOTA: The neighborhood size is always <= 8

    //! Adjacency list
    /*!
     * adj_list is a vector of int tables containing the IDs of "outgoing neighbours"
     * i.e. neighbours v having a u -> v edge (u being the present node).
     * adj_list_sz contains the corresponding sizes
     */
    vector<int*>  adj_list;
    vector<int>   adj_list_sz;
};

// ===========================================================================
//                          Inline functions' definition
// ===========================================================================
int *CGraph::get_adj_list(int node) const
{
	return adj_list[node];
}

int CGraph::get_adj_list_sz(int node) const
{
	return adj_list_sz[node];
}

int CGraph::get_n_nodes(void) const
{
	return (int) adj_list.size();
}


// ===========================================================================
//                          Other functions
// ===========================================================================

void read_node_file(FILE *node_file, vector<char *>& seqs, int k_val);
void read_edge_file(FILE *edge_file, vector<LabelledCEdge>& edges);

// Functor that load the bcc nodes in memory
class NodeLoader 
{
 private:
  vector<char *>& _seqs;
  vector<int>& _label;
 public:
  NodeLoader(vector<char *>& seqs, vector<int>& label)
    : _seqs(seqs), _label(label) {}
  void operator()(FILE* node_file, int written_lines){
    char* buffer = new char[100 * MAX];      
    for (int i = 0; i < written_lines ; i++) {
      char* p;
      fgets(buffer, 100 * MAX, node_file);      
      if (strlen(buffer) == 100 * MAX)
	{  
	  p = strtok(buffer, "\t\n");
	  fprintf(stdout, "ERROR: node %s with sequence larger than %d!", p, 100 * MAX);
	  exit(0);
	}      
      // Node label
      p = strtok( buffer, "\t\n" );
      _label.push_back(atoi(p));      
      // Node seq
      p = strtok( NULL, "\t\n"  );
      char* seq = new char[strlen(p) + 1];
      strcpy( seq, p );      
      // Node seq_r
      p = strtok( NULL, "\t\n" );      
      _seqs.push_back( seq );
    }
    delete [] buffer;
  }
};

// return false if nbnodes<4
// required a functor to acts on the file lines
template<class TFunctor>
bool read_node_noncontigous_file_withoptimIO( FILE *contents_file_node, FILE* node_file, 
					      int *required_sequence, int *file_index,
					      TFunctor& functor )
{  
  char* buffer = new char[100 * MAX];
  ////////////////////////////
  ////////////////////////////
  ////////////////////////////
  // IO optimization (start)
  ////////////////////////////
  int written_lines_before, written_lines_for_record, written_lines;
  //normally would skip directly to the record, but for the time being use the formatted file
  for (int i = 0; i < *required_sequence+*file_index-2 ; i++) fgets(buffer, 100 * MAX, contents_file_node);
  fscanf(contents_file_node, "%d \n",&written_lines_before );
  fscanf(contents_file_node, "%d \n",&written_lines_for_record );
  // if a graph node was not written to disk   
  if ( written_lines_before == written_lines_for_record) 
    { fprintf(stderr, "The required record %d does not exist in the bcc graph !\n", *required_sequence); exit(0); }  
  // where to search the record
  written_lines =  written_lines_for_record - written_lines_before;

  bool atleast4nodes = false;
  if (written_lines>=4){ //Less than 4 nodes, cannot contain a bubble!
    atleast4nodes = true;
    //normally would skip directly to the record, but for the time being use the formatted file
    for (int i = 0; i < written_lines_before ; i++) fgets(buffer, 100 * MAX, node_file);  
    ////////////////////////////
    // IO optimization (end)
    ////////////////////////////
    ////////////////////////////
    ////////////////////////////  
    functor(node_file, written_lines);
  }
  delete [] buffer;
  return atleast4nodes;
}

// Functor that load the bcc edges in memory
class EdgeLoader
{
 private:
  vector<LabelledCEdge>& _edges;
 public:
 EdgeLoader(vector<LabelledCEdge>& edges) 
   : _edges(edges) {}
  void operator()(FILE* edge_file, int written_lines){
    if (written_lines>0){
      char* buffer = new char[100 * MAX];
      char* u = new char[MAX];
      char* v = new char[MAX];
      char* label = new char[MAX];
      _edges.reserve(written_lines);//count_nb_lines(edge_file));
      for (int i = 0; i < written_lines ; i++) {
	char* p;
	fgets(buffer, 100 * MAX, edge_file);	
	// outgoing node
	p = strtok( buffer, "\t\n" );
	strcpy( u, p );	
	// incoming node
	p = strtok( NULL, "\t\n" );
	strcpy( v, p );	
	// edge label
	p = strtok( NULL, "\t\n" );
	strcpy(label, p);	
	_edges.push_back( LabelledCEdge( atoi(u), atoi(v), label ) );
      }      
      sort( _edges.begin(), _edges.end() );
      delete [] u;
      delete [] v;
      delete [] label; 
      delete [] buffer;      
    }
  }
};

// required a functor to acts on the file lines
template<class TFunctor>
void read_edge_file_withoptimIO( FILE *contents_file_edge, FILE *edge_file,
				 int *required_sequence, int *file_index,
				 TFunctor& functor )
{
  char* buffer = new char[100 * MAX];
  ////////////////////////////
  ////////////////////////////
  ////////////////////////////
  // IO optimization (start) 
  // [replicate as above]
  ////////////////////////////
  int written_lines_before, written_lines_for_record, written_lines;
  //normally would skip directly to the record, but for the time being use the formatted file
  for (int i = 0; i < *required_sequence+*file_index-2 ; i++) fgets(buffer, 100 * MAX, contents_file_edge);
  fscanf(contents_file_edge, "%d \n",&written_lines_before );
  fscanf(contents_file_edge, "%d \n",&written_lines_for_record );
  // if a graph edge  was not written to disk   
  if ( written_lines_before == written_lines_for_record) 
    { fprintf(stderr, "The required record %d does not exist in the bcc graph !\n", *required_sequence); exit(0); }  
  // where to search the record
  written_lines =  written_lines_for_record - written_lines_before;
  //normally would skip directly to the record, but for the time being use the formatted file
  for (int i = 0; i < written_lines_before ; i++) fgets(buffer, 100 * MAX, edge_file);  
  ////////////////////////////
  // IO optimization (end)
  ////////////////////////////
  ////////////////////////////
  ////////////////////////////
  functor(edge_file, written_lines);       
  delete [] buffer;
}

#endif // COMPRESSED_GRAPH_H
