#include <stdlib.h>

#include "NdbEndfIO.h"

#define NUMBER_SIZE	11

char	NdbEndfIO::_str[100];		// define static _str var

ClassImp(NdbEndfIO)

/* ============ NdbEndfIO ============== */
NdbEndfIO::NdbEndfIO( char *filename, Int_t mode )
{
	f = fopen(filename,mode==TENDF_READ?"r":"w");
	matStart = 0;
	mfStart = 0;
	mtStart = 0;
	iMAT = 0;
	iMF = 0;
	iMT = 0;
	lineNum = 0;
	lineTxt[0] = 0;
} // NdbEndfIO


/* -------- FindMAT --------- */
// Will search for the specific material
Bool_t
NdbEndfIO::FindMAT( Int_t mat, Bool_t rewind )
{
	if (rewind)
		fseek(f, 0L, SEEK_SET);

	while (ReadLine())
		if (iMAT == mat)
			return TRUE;
	
	return FALSE;
} // FindMAT

/* -------- FindMATMF --------- */
// Will search for the specific material - mf
Bool_t
NdbEndfIO::FindMATMF( Int_t mat, Int_t mf, Bool_t rewind )
{
	FindMAT(mat,rewind);

	do {
		if (iMF == mf)
			return TRUE;
	} while (ReadLine());
	
	return FALSE;
} // FindMATMF

/* -------- FindMATMFMT --------- */
// Will search for the specific material - mf - mt
Bool_t
NdbEndfIO::FindMATMFMT( Int_t mat, Int_t mf, Int_t mt, Bool_t rewind )
{
	FindMATMF(mat,mf,rewind);

	do {
		if (iMT == mt)
			return TRUE;
	} while (ReadLine());
	
	return FALSE;
} // FindMATMFMT

/* ------------ FindMFMT ------------- */
Bool_t
NdbEndfIO::FindMFMT( Int_t mf, Int_t mt )
{
	// If current MFMT is higher than what we search go to the beggining
	if (mf*1000+mt >= iMF*1000+iMT) {
		RewindMAT();
		if (!ReadLine())
			return FALSE;
	}

	while (mf*1000+mt > iMF*1000+iMT)
		if (!ReadLine())
			return FALSE;
	return TRUE;
} // FindMFMT

/* -------- Substr ---------- */
char *
NdbEndfIO::Substr(Int_t start, Int_t length)
{
	if (start + length > lineLen) {
//		error(ERR_INVALID_RECORD);
		return NULL;
	}
	memcpy(_str, lineTxt+start, length);
	_str[length] = 0;

	return _str;
} // Substr

/* -------- NextNumber ---------- */
/* Advance lastNumber Point to the correct number
 * so to read 6 numbers of width 11 in each line
 */
Bool_t
NdbEndfIO::NextNumber(Int_t pos)
{
	if (pos<0 || pos>5*NUMBER_SIZE) {
		lastNumPos += NUMBER_SIZE;
		if (lineTxt[0]=='\0' || lastNumPos>5*NUMBER_SIZE) {
			Int_t	mf = iMF;	// Remember current MF, MT
			Int_t	mt = iMT;
			if (!ReadLine() || (mf*1000+mt != iMF*1000+iMT))
				return TRUE;
			lastNumPos = 0;
		}
	} else {
		lastNumPos = pos * NUMBER_SIZE;
	}
	return FALSE;
} // NextNumber

/* -------- SubReadInt ---------- */
Int_t
NdbEndfIO::SubReadInt(Int_t start, Int_t length)
{
	NdbEndfIO::Substr(start,length);
	return atoi(_str);
} // SubReadInt

/* ---------- ReadInt ------------- */
/* Read one by one the int numbers with length 11 from the current line.
 * If the this is the last one then it prompts for an new line
 * only if it stays inside the same MF:MT type
 */
Int_t
NdbEndfIO::ReadInt(Bool_t *error, Int_t pos)
{
	if (NextNumber(pos)) {
		*error = TRUE;
		return 0;
	}
	*error = FALSE;
	return SubReadInt(lastNumPos,NUMBER_SIZE);
} // ReadInt

/* -------- SubReadReal ---------- */
Float_t
NdbEndfIO::SubReadReal(Int_t start, Int_t length)
{
	char	numstr[20];
	char	*dst, *src;
	
	NdbEndfIO::Substr(start,length);

	// Real numbers are in the following formats
	//	+/- N.NNNNNN+/-N
	//	+/- N.NNNNN+/-bN	(b=Blank)
	//	+/- N.NNNNN+/-NN

	dst = numstr;
	src = _str;

	/* skip blanks */
	while (*src==' ') src++;

	/* copy sign */
	if (*src=='+' || *src=='-') *dst++ = *src++;

	/* copy realpart */
	while (1) {
		if (memchr(".0123456789",*src,11))
			*dst++ = *src++;
		else
			break;
	}

	/* append the exponent */
	*dst++ = 'E';

	/* copy sign */
	if (*src=='+' || *src=='-') *dst++ = *src++;

	/* skip blanks */
	while (*src==' ') src++;

	/* copy exponent */
	while (1) {
		if (memchr("0123456789",*src,10))
			*dst++ = *src++;
		else
			break;
	}

	/* close the destination */
	*dst = 0;

	/* if something is still in source then we have an error number */
	if (*src) {
//		error(ERR_INVALID_REAL_NUMBER);
		return 0.0;
	}

	return atof(numstr);
} // SubReadReal

/* ---------- ReadReal ------------- */
/* Read one by one the real numbers with length 11 from the current line.
 * If the this is the last one then it prompts for an new line
 * only if it stays inside the same MF:MT type
 */
Float_t
NdbEndfIO::ReadReal(Bool_t *error, Int_t pos)
{
	if (NextNumber(pos)) {
		*error = TRUE;
		return 0.0;
	}
	*error = FALSE;
	return SubReadReal(lastNumPos,NUMBER_SIZE);
} // ReadReal

/* -------- ReadLine -------- */
Bool_t
NdbEndfIO::ReadLine()
{
	Long_t	current_pos;

	// --- Force the next number reading to be from the beggining of line
	lastNumPos = -NUMBER_SIZE;

	// --- Have we reached the end of file
	if (feof(f)) {
		lineTxt[0] = 0;
		return FALSE;
	}

	// --- Remember the position of start of line ---
	current_pos = ftell(f);

	// --- Read one line ---
	fgets(lineTxt,sizeof(lineTxt),f);

	lineLen = strlen(lineTxt);

	// Chop the trailing newline char
	if (lineTxt[lineLen-1] == '\n')
		lineTxt[--lineLen] = 0;

	// --- Remember previous values ---
	Int_t	oldMAT	= iMAT;
	Int_t	oldMF	= iMF;
	Int_t	oldMT	= iMT;

	// --- Read the material, mf, mt and line number ---
	iMAT    = SubReadInt(66, 4);
	iMF     = SubReadInt(70, 2);
	iMT     = SubReadInt(72, 3);
	lineNum = SubReadInt(75, 5);

	// --- Update pointers if necessary ---
	if (iMAT && iMAT != oldMAT)
		matStart = current_pos;

	if (iMF && iMF != oldMF)
		mfStart = current_pos;

	if (iMT && iMT != oldMT)
		mtStart = current_pos;

	return TRUE;
} // ReadLine
