/*! read_tra_file.cpp
 * Provides functions for reading a *.tra file describing the transition
 * system of a Markov chain (DTMC) and saving it into a corresponding
 * matrix.
 *
 * Reuses code from the file "read_tra_file.c" from the old MRMC project.
 *
 *  Created on: 15.08.2012
 *      Author: Thomas Heinemann
 */

#include "parser.h"

#include "src/parser/read_tra_file.h"
#include "src/exceptions/file_IO_exception.h"
#include "src/exceptions/wrong_file_format.h"
#include "boost/integer/integer_mask.hpp"
#include <cstdlib>
#include <cstdio>

namespace mrmc {

namespace parser{

// Only disable the warning if we are not using GCC, because
// GCC does not know this pragma and raises a warning 
#ifndef __GNUG__
// Disable C4996 - This function or variable may be unsafe.
#pragma warning(disable:4996)
#endif

/*!
* This method does the first pass through the .tra file and computes
* the number of non-zero elements that are not diagonal elements,
* which correspondents to the number of transitions that are not
* self-loops.
* (Diagonal elements are treated in a special way).
*
* @return The number of non-zero elements that are not on the diagonal
* @param p File stream to scan. Is expected to be opened, a NULL pointer will
*          be rejected!
*/
static uint_fast32_t make_first_pass(FILE* p) {
   if(p==NULL) {
      throw exceptions::file_IO_exception ("make_first_pass: File not readable (this should be checked before calling this function!)");
   }
   char s[BUFFER_SIZE];                 //String buffer
   int rows=0, non_zero=0;

   //Reading No. of states
   if (fgets(s, BUFFER_SIZE, p) != NULL) {
      if (sscanf( s, "STATES %d", &rows) == 0) {
         (void)fclose(p);
         throw mrmc::exceptions::wrong_file_format();
      }
   }

   //Reading No. of transitions
   if (fgets(s, BUFFER_SIZE, p) != NULL) {
      if (sscanf( s, "TRANSITIONS %d", &non_zero) == 0) {
         (void)fclose(p);
         throw mrmc::exceptions::wrong_file_format();
      }
   }

   //Reading transitions (one per line)
   //And increase number of transitions
   while (NULL != fgets( s, BUFFER_SIZE, p ))
   {
      int row=0, col=0;
      double val=0.0;
      if (sscanf( s, "%d%d%lf", &row, &col, &val ) != 3) {
         (void)fclose(p);
         throw mrmc::exceptions::wrong_file_format();
      }
      //Diagonal elements are not counted into the result!
      if(row == col) {
         --non_zero;
      }
   }
   return static_cast<uint_fast64_t>(non_zero);
}



/*!Reads a .tra file and produces a sparse matrix representing the described Markov Chain.
 *
 * Matrices created with this method have to be freed with the delete operator.
 * @param filename input .tra file's name.
 * @return a pointer to the created sparse matrix.
 */

storage::SquareSparseMatrix<double> * read_tra_file(const char * filename) {
	FILE *p = NULL;
	char s[BUFFER_SIZE];
	uint_fast64_t non_zero = 0;
	int rows = 0;
	storage::SquareSparseMatrix<double> *sp = NULL;

	p = fopen(filename, "r");
	if(p == NULL) {
		throw exceptions::file_IO_exception("mrmc::read_tra_file: Error opening file! (Does it exist?)");
		return NULL;
	}
	non_zero = make_first_pass(p);

	//Set file reader back to the beginning
	rewind(p);

	//Reading No. of states
	if ((fgets(s, BUFFER_SIZE, p) == NULL) || (sscanf(s, "STATES %d", &rows) == 0)) {
		(void)fclose(p);
		throw mrmc::exceptions::wrong_file_format();
		return NULL;
	}

	/* Reading No. of transitions
	 * Note that the result is not used in this function as make_first_pass()
	 * computes the relevant number (non_zero)
	 */
	int nnz = 0;
	if ((fgets(s, BUFFER_SIZE, p) == NULL) || (sscanf(s, "TRANSITIONS %d", &nnz) == 0)) {
		(void)fclose(p);
		throw mrmc::exceptions::wrong_file_format();
		return NULL;
	}

	/* Creating matrix
	 * Memory for diagonal elements is automatically allocated, hence only the number of non-diagonal
	 * non-zero elements has to be specified (which is non_zero, computed by make_first_pass)
	 */
	sp = new storage::SquareSparseMatrix<double>(static_cast<uint_fast64_t>(rows) + 1);
	if ( NULL == sp ) {
		throw std::bad_alloc();
		return NULL;
	}
	sp->initialize(non_zero);

	//Reading transitions (one per line) and saving the results in the matrix
	while (NULL != fgets(s, BUFFER_SIZE, p )) {
		int row=0, col=0;
		double val = 0.0;
		if (sscanf(s, "%d%d%lf", &row, &col, &val) != 3) {
			(void)fclose(p);
			throw mrmc::exceptions::wrong_file_format();
			// Delete Matrix to free allocated memory
			delete sp;
			return NULL;
		}
		sp->addNextValue(static_cast<uint_fast64_t>(row),static_cast<uint_fast64_t>(col),static_cast<uint_fast64_t>(val));
	}

	(void)fclose(p);

	sp->finalize();
	return sp;
}

} //namespace parser

} //namespace mrmc