// definizione dei record_text per trasferimento da/a file di testo

#include <config.h>
#include <fstream.h>
#include <object.h>
#include <strings.h>

//	nomi delle variabili in .ini per HEADER
#define HEADER		"HEADER"
#define NOMEFILE	"NOMEFILE"
#define LUNGHEZZA	"LUNGHEZZA"
#define RECORDSEP "RECORDSEP"
#define FIELDSEP	"FIELDSEP"


class TFile_text;
class TRecord_text : public TToken_string
{
	
	// lunghezza del record (se a lunghezza fissa)
	int 			_length;
	
	// separatore di record (se necessario)
	TString16	_recordsep;

	// il separatore di campi esiste già (_separator)
	
public:

	// restituisce la lunghezza del record
	int length() { return _length; }
	// restituisce il separatore di record (se esiste)
	const char* recordsep() { return _recordsep; }

	// ritorna il campo alla posizione <pos> 
	// aggiungere la possibilità di estrarlo <da> <a>
	const char* get(int pos) const;	
	                              
	// ritorna il campo che va dal carattere <da> al carattere <a>
	const char* get(int da, int a) const; 
	
	// scrive il campo <c> nel record alla posizione <pos>
	
	void put(const char* c, int pos = -1);	
	
	// scrive il campo <c> nel record partendo dal carattere <da> fino al carattere <a>
	// se il campo <c> è più lungo viene troncato
	
	void put(const char* c, int da, int a);
	             
	// costruttore	             
	TRecord_text(const char* recordsep = "\n", char fieldsep = '|')
	: _length(-1), _recordsep(recordsep)
	{
		TToken_string::TToken_string("",fieldsep);
	}
	
	// costruttore
	TRecord_text(int length, const char* recordsep = "\n")
	: _length(length)
	{
		TToken_string::TToken_string(length);
		spaces();
	}
	       
	// distruttore	       
	~TRecord_text() {};
	
};


const char* TRecord_text::get(int pos) const
{
	return ((TToken_string*)this)->get(pos);		
}

const char* TRecord_text::get(int da, int a) const
{
	CHECK(_length > 0, "Get non consentita su record non a lunghezza fissa");
	
	return sub(da, a);
}

void TRecord_text::put(const char* c, int pos)
{
	add(c, pos);
}

void TRecord_text::put(const char* c, int da, int a)
{ 
	CHECK(_length > 0, "Put non consentita su record non a lunghezza fissa");
	
	TString s = c;
	int len = da-a+1;
	if ( s.len() > len)	s = s.cut(len);
	// sistemare se è più corta (la fill non va bene)
	TToken_string::overwrite(s, da);
}

// definizione dei field_text per trasferimento da/a file di testo

class TField_text : public TObject
{               
	// nome del campo
	TString16 _name;
	// posizione assoluta nel record (se con separatore)
	int			_pos;
	// posizione iniziale nel record (se a lunghezza fissa)
	int			_from;
	// posizione finale /se a lunghezza fissa)
	int			_to;
		
protected:
	
public:
	// costruttore
	TField_text() : _name(0), _pos(-1), _from(-1), _to(-1) {}
	// costruttore
	TField_text(const char* name, int pos = -1, int from = -1, int to = -1)
	:	_name(name), _pos(pos), _from(from), _to(to) {}

	// ritorna il nome del campo	
	const char* name() const { return _name; }
	// setta il nome del campo
	void set_name(const char* name) { _name = name; }

	// ritorna la posizione iniziale del campo	
	int from() const { return _from; } 
	// setta la posizione iniziale del campo
	void set_from(int from) { _from = from; }
	
	// ritorna la posizione finale del campo
	int to() const { return _to; }
	// setta la posizione finale del campo
	void set_to(int to) { _to = to; }
	                                      
	// ritorna la posizione del campo	                                      
	int pos() const { return _pos; }
	// setta la posizione del campo
	void set_pos(int pos) { _pos = pos; }

	// cerca nel record il campo e ne ritorna il contenuto	
	const char* read(const TRecord_text& rec) const;
	
	// cerca nel record il campo e vi scrive <val>
	void write(const char* val, TRecord_text& rec);

	virtual ~TField_text() {};		
	
};

const char* TField_text::read(const TRecord_text& rec) const
{
	int pos = TField_text::pos();
	if (pos = -1)
	{
		int from = TField_text::from();
		int to = TField_text::to();
		if (from != -1 && to != -1)
			return rec.get(from, to);
		else
			return "";
	}
	else
		return rec.get(pos);						
}

void TField_text::write(const char* val, TRecord_text& rec)
{
	int pos = TField_text::pos();
	if (pos != -1)
	{
		int from = TField_text::from();
		int to = TField_text::to();
		if (from != -1 && to != -1)
			rec.put(val, from, to);
	}
	else
		rec.put(val, pos);
}

// definizione dei file_text per trasferimento da/a file di testo

class TFile_text: public fstream
{
	TFilename			_name;
	TRecord_text	_current;
	TString 			_buffer;
	TArray				_arr_exprs;
	TArray				_arr_fields;
	
public:

	// apre il file, se create = TRUE e il file non esiste lo crea
	int open(TFilename name, bool create = FALSE);
	// chiude il file 
	int close();
	// crea il file
	int create(TFilename name) { return open(name, TRUE); };
	// cancella il file
	int remove();

	// ritorna il record corrente
	TRecord_text& curr() { return _current; }

	// setta _current	a rec
	void set_curr(TRecord_text& rec);

	// restituisce il nome del file
	const char* name() { return _name; }	

	// setta il nome del file	
	void set_name(TFilename name) { _name = name;}
	
	// si posiziona sul primo record del file
	int first();
	// si posiziona sul prossimo record del file
	int next();

	// aggiunge il record rec in fondo
	int write(TRecord_text& rec);
	// aggiunge il record corrente in fondo
	int write();

	// costruttore
	TFile_text(const char* file_name, const char* config_name)
	: _name(file_name), _buffer(1024*16)
	{         
  	setbuf((char*)(const char*)_buffer, _buffer.size());
		TConfig config(config_name);
		const TString_array fields;
		const char* recordsep = config.get(RECORDSEP,HEADER);
		const char* fieldsep = config.get(FIELDSEP,HEADER);
		int lunghezza = config.get_int(LUNGHEZZA,HEADER);
		if (lunghezza = 0)
			_current(recordsep, fieldsep);			
		else
			_current(lunghezza,recordsep);
		const int i = config.list_paragraphs(fields);
		//int posheader = fields.find(HEADER);
		fields.remove(fields.find(HEADER),TRUE);
		//for ( int k = 0; k < fields.items(); k++ )
	};
	
	// distruttore
	~TFile_text() 
	{
		
	};
};


void TFile_text::set_curr(TRecord_text& rec)
{
	//CHECK(rec,"NULL record");
	_current = rec;
}

int TFile_text::write(TRecord_text& rec)
{
	//CHECK(rec = NULL, "NULL record");
	const char* record = (char*)&rec;
	if (rec.length() > 0)
		fstream::write(record, rec.length());
	else
		fstream::write(record, rec.len());
	const TString16 separator = rec.recordsep();
	if ( separator.ok())
	{
		int lensep = separator.len();
		fstream::write(separator, lensep);
	}
	return 0;
}

int TFile_text::write()
{
	//CHECK(_current, "NULL current record");
	const char* record = (char*)(&_current);
	if (_current.length() > 0)
		fstream::write(record, _current.length());
	else
		fstream::write(record, _current.len());
	const TString16 separator = _current.recordsep();
	if ( separator.ok())
	{
		int lensep = separator.len();
		fstream::write(separator, lensep);
	}
	return 0;
}

int TFile_text::open(TFilename name, bool create)
{
	set_name(name);
	if (create)
		open(name,ios::app|ios::binary|ios::out);
	else		
		open(name,ios::app|ios::binary|ios::in|ios::nocreate);
	if (bad())
		fatal_box("Impossibile aprire %s",name);
	return 0;
}

int TFile_text::next()
{
	//CHECK(_current, "NULL current record");
	if (_current.length() > 0)
		fstream::read((char*)(&_current), _current.length());
	else
	{
		TString16 separator = curr().recordsep();
		if (separator.ok())		
		{
			TString record;
			int countsep = 0;
			do	
			{
				char c = get();
				record << c;
				if (c = separator[countsep])
					countsep++;
				else
					countsep = 0;				
			}	while (countsep < separator.len() && good() && !eof());
			if (countsep = separator.len())
				record.cut(record.len()-separator.len());
			(TToken_string&)_current = record;							
		}							
	}		
	return 0;		
}

int TFile_text::close()
{
	_name = "";
	_current = NULL;
	_buffer = "";
	fstream::close();
	return 0;
}

