/* ***************************************************************************
 *
 *                              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.
 */



// ===========================================================================
//                               Include Libraries
// ===========================================================================
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <vector>


// ===========================================================================
//                             Include Project Files
// ===========================================================================
#include <debug.h>
#include <NGraph.h>
#include <CGraph.h>
#include <CycleCompression.h>
#include <SplitBcc.h>
#include <Utils.h>

#define MAX 1024


// ===========================================================================
//                         Define Miscellaneous Functions
// ===========================================================================

void read_edges_and_nodes( char* edges_fname, char* nodes_fname, const int k,
        vector<LabelledCEdge>& allEdges, vector<char*>& seqs )
{
  FILE* edge_file = open_file(edges_fname);
  FILE* node_file = open_file(nodes_fname);
  
  read_edge_file( edge_file, allEdges );
  read_node_file( node_file, seqs, k );
  
  fclose( edge_file );
  fclose( node_file );
}

int main( int argc, char** argv )
{
  vector<char*> seqs;
  
  vector<LabelledCEdge> allEdges;

  string base_name = "./bcc/graph";

  bool output_context = false;

  if ( argc < 4 )
  { 
    fprintf( stderr, "Wrong number of arguments!\n" );
    fprintf( stderr, "Usage: ./run_modules edge_file node_file k_value path_to_output [--output-context]\n" );
    return 0;
  }
  
  if ( argc >= 5 )
  {
    base_name = argv[4];
  }
  
  if ( argc == 6  && strcmp(argv[5], "--output-context") == 0 )
  {
    output_context = true;
  }
 // CGraph graph;
  int k_value = atoi( argv[3] );

  // Read input files
  read_edges_and_nodes( argv[1], argv[2], k_value, allEdges, seqs );
  
 // Creating & Initializing the graph with the edges reads
  CGraph graph( (int)seqs.size(), allEdges, k_value );

  // Decompose graph into BCCs
  fprintf(stdout, "Searching biconnected components...\n");
  vector< vector<CEdge> > bcc = find_bcc(graph);
  fprintf( stdout, "Number of biconnected components found: %d\n\n", (int)bcc.size() );

  
  graph.destroy_adj_list();
  NGraph* component;

  ////////////////////////////
  ////////////////////////////
  ////////////////////////////
  // IO optimization (start)
  ////////////////////////////

  // P1 - descriptor files
  char contents_edge_fname[1024];
  char contents_node_fname[1024];
  sprintf( contents_edge_fname,  "%s_contents_edges_bcc",  base_name.c_str());
  sprintf( contents_node_fname,  "%s_contents_nodes_bcc",  base_name.c_str());
  FILE *contents_edge_file;
  FILE *contents_node_file;
  contents_edge_file = fopen(contents_edge_fname,"w");
  contents_node_file = fopen(contents_node_fname,"w");
  int lines_written_edges = 0;
  int lines_written_nodes = 0;
  fprintf( contents_edge_file, "%d\n",lines_written_edges );
  fprintf( contents_node_file, "%d\n",lines_written_nodes );

  // P2 - data files
  int number_of_files_max;
  int records_per_file;
  if ((int)bcc.size()==1){
	  number_of_files_max = 1;
	  records_per_file = 1;
  }
  else{
	  if ((int)bcc.size()<NUMBEROFFILES)
		  number_of_files_max = (int)bcc.size();
	  else
		  number_of_files_max = NUMBEROFFILES;
	  records_per_file = (int)bcc.size()/(number_of_files_max-1); // this division may have a remainder, then extra file (+1) required
  }
  char total_edge_fname[1024];
  char total_node_fname[1024];
  char total_log_fname[1024];
  char info_snp_fname[1024];
  FILE *total_edge_file[number_of_files_max];
  FILE *total_node_file[number_of_files_max];
  FILE *total_log_file;
  FILE *info_snp_file;
  int ii = 0;
  sprintf( total_edge_fname,  "%s_all_edges_bcc_%d",  base_name.c_str(), ii+1 );
  total_edge_file[ii] = fopen(total_edge_fname,"w");
  sprintf( total_node_fname,  "%s_all_nodes_bcc_%d",  base_name.c_str(), ii+1 );
  total_node_file[ii] = fopen(total_node_fname, "w");
  sprintf( total_log_fname,   "%s_all_log_bcc",  base_name.c_str() );
  total_log_file = fopen(total_log_fname, "w");
  sprintf( info_snp_fname,   "%s_info_snp_bcc",  base_name.c_str() );
  info_snp_file = fopen(info_snp_fname, "w");

  // P3 - ascii info file to get all parameters to read the big files
  char info_fname[1024];
  FILE *info_file;
  sprintf( info_fname,  "%s_info_bcc",  base_name.c_str());
  info_file = fopen(info_fname,"w");
  fprintf(info_file, "%d \n",(int)bcc.size() );
  fprintf(info_file, "%d \n",records_per_file );

  ///////////////////////////
  // IO optimization (end)
  ///////////////////////////
  ///////////////////////////
  ///////////////////////////


  // For each BCC, ...
  for ( int i = 0 ; i < (int)bcc.size() ; i++ )
  {
    fprintf(stdout, "Processing component %d...\n", i+1);
    
    // Build uncompact graph corresponding to current BCC
    component = new NGraph( graph, seqs, allEdges, bcc[i] );

    fprintf( stdout, "Initial size: %d nodes.\n", (int)component->getNbNodes() );


    // Compress linear paths of size > 2
    fprintf(stdout, "Compressing linear paths...\n");
    int original_size = (int)component->getNbNodes();
    component->compress_all_paths();
    fprintf( stdout, "Number of compressed nodes: %d.\n", original_size - (int)component->getNbNodes() );
    
    
    // Compress bubbles
    fprintf( stdout, "Compressing simple bubbles...\n" );
    int n_compressed_bubbles;
    component->compress_all_bubbles( &n_compressed_bubbles, total_log_file, i+1, output_context  );
    fprintf( stdout, "Number of compressed bubbles: %d.\n", n_compressed_bubbles );
    int nbsnps = component->getNbOutput();
    if (nbsnps)
      fprintf(info_snp_file, "%d\t%d\n", i+1, nbsnps); // considering (i+1) and not i

    
    // Recompress linear paths of size > 2
    fprintf(stdout, "Recompressing linear paths...\n");
    original_size = (int)component->getNbNodes();
    component->compress_all_paths();
    fprintf( stdout, "Number of compressed nodes: %d.\n", original_size - (int)component->getNbNodes() );


    
    if ( (int)component->getNbNodes() >= 4 )
    { 
      ///////////////////////////
      // IO optimization
      component->print_graph_edges_new( &lines_written_edges, contents_edge_file,total_edge_file[ii], NULL, NULL, false );
      component->print_graph_nodes_new( &lines_written_nodes, contents_node_file,total_node_file[ii], NULL, NULL, false );
      ///////////////////////////;
    }    
    else {
      fprintf( contents_edge_file, "%d\n",lines_written_edges );
      fprintf( contents_node_file, "%d\n",lines_written_nodes );
    }
    fprintf( stdout, "Final size: %d nodes.\n", (int)component->getNbNodes() );
    fprintf(info_file, "%d %d\n", i+1, (int)component->getNbNodes()); // outuput size of bcc for further use
    fprintf( stdout, "Done!\n\n" );
        
    ////////////////////////////
    ////////////////////////////
    ////////////////////////////
    // IO optimization (start)
    ////////////////////////////
    //check if file has to be changed - considering (i+1) and not i
    if ( ((i+1) % records_per_file == 0) && ((ii+1) < number_of_files_max) ) {
      ii++;
      sprintf( total_edge_fname,  "%s_all_edges_bcc_%d",  base_name.c_str(), ii+1 );
      total_edge_file[ii] = fopen(total_edge_fname,"w");
      sprintf( total_node_fname,  "%s_all_nodes_bcc_%d",  base_name.c_str(), ii+1 );
      total_node_file[ii] = fopen(total_node_fname, "w");
      lines_written_edges = 0;
      lines_written_nodes = 0;
      //write an additional 0 in the first place
      fprintf( contents_edge_file, "%d\n",lines_written_edges );
      fprintf( contents_node_file, "%d\n",lines_written_nodes );

      //close the previous files, this avoids having too many open file at the smae time
      fclose(total_edge_file[ii-1]);
      fclose(total_node_file[ii-1]);
    }
    ////////////////////////////
    // IO optimization (end)
    ////////////////////////////
    ////////////////////////////
    ////////////////////////////
    
    delete component;
  }
  ///////////////////////////
  // IO optimization
  fclose(contents_edge_file);
  fclose(contents_node_file);
  fclose(total_log_file);
  fclose(info_snp_file);
  fclose(info_file);
  
  // Close just the last one
  fclose(total_edge_file[ii]);
  fclose(total_node_file[ii]);
  
  ///////////////////////////;
     
  // STL does not delete the objects in the vector when we call clear,
  // we have to do it manually !!!!!
  for ( vector<char*>::iterator it = seqs.begin() ; it != seqs.end() ; it++ )
  {
    delete [] *it;
  } 
  seqs.clear();
  
  return EXIT_SUCCESS;
}
