#include <relapp.h>
#include <tabutil.h>
#include <recarray.h>
#include <assoc.h>
#include <checks.h>
#include <defmask.h>

#include "mglib01.h"


// **************************
// classe livelli giacenze
TMag_livelli:: TMag_livelli(const char *tabname)
{	
	TTable _tabformato(tabname);	
	int e;
	
	e=_tabformato.first();
	for (int i=0; i<	MANY_MAG_LEV; i++) {
		_enabled[i]=(e==NOERR);
		if (_enabled[i]) {
			_name[i]=_tabformato.get("S0");
			_code_lenght[i]=atoi(_tabformato.get("I0"));
			_picture[i]=_tabformato.get("S1");
			_last_level=i+1;
		} else {
			_name[i]="";
			_code_lenght[i]=0;
			_picture[i]="";
		}
		e=_tabformato.next();
	}
}

const bool TMag_livelli::enabled(int levnum)
{
	if (levnum<=MANY_MAG_LEV &&  levnum>0)
		return(_enabled[levnum-1]);
	else
		return(FALSE)	;
}

const int TMag_livelli::code_lenght(int levnum)
{
	if (levnum<=MANY_MAG_LEV &&  levnum>0)
		return(_code_lenght[levnum-1]);
	else
		return(0)	;
}

const TString & TMag_livelli::name(int levnum)
{
	if (levnum<=MANY_MAG_LEV &&  levnum>0)
		return(_name[levnum-1]);
	else
		return("");	
}

const TString & TMag_livelli::picture(int levnum)
{
	if (levnum<=MANY_MAG_LEV &&  levnum>0)
		return(_picture[levnum-1]);
	else
		return("");	
}

void TMag_livelli::pack_grpcode(TString & pc, const TString &codlev, const int levnum)
{
      
  int start=0;
	for (int i=1; i<levnum; i++) 
		start+=	_code_lenght[i-1];
	pc.overwrite(codlev.left(_code_lenght[levnum-1]),start);	    	
}

TString TMag_livelli::unpack_grpcode(const TString & pc, const int levnum)
{
	int start=0;
	if (!_enabled[levnum-1])
		return("");
	for (int i=1; i<levnum; i++) 
		start+=	_code_lenght[i-1];
	if (start>=pc.len())
		return("");
	else	
		return(pc.mid(start,levnum == _last_level ? -1 : _code_lenght[levnum-1]));
}


// *******************************
// *******************************

// *******************************
TStateset::TStateset()
{ 
	empty();
}

TStateset & TStateset::empty()
{ _current=0;
	for (int i= 0; i< MAXSTATES; _container[i++]=0);
	return *this;
}

TStateset & TStateset::enclose(int el)
{ if (el< MAXSTATES && el>=0)
		_container[el]=1;
	return *this;
}

TStateset & TStateset::singleton(int el)
{ empty();
	_container[el]=1;
	return *this;
}

TStateset & TStateset::cap(TStateset & s)
{
	for (int i= 0; i< MAXSTATES; i++)
		_container[i]|=s._container[i];
	return *this;
}
	
bool TStateset::is_empty() const
{ 
	for (int i= 0; i< MAXSTATES; i++) {
		if (_container[i]) return FALSE;
	}
	return TRUE;
}

bool TStateset::is_member(int e)    const
{ 
	return ((e< MAXSTATES && e>=0)?  _container[e] : FALSE );
}

int TStateset::get_first()
{
	_current=-1;
	return(get_next());
}

int TStateset::get_next()
{
	while (_current+1 < MAXSTATES)  {
		if (_container[++_current])
			return _current;
	} 
	return (-1);	
}

// *******************************
// *******************************
// automa per il riconoscimento di metacaratteri
#define EPSILON 0
#define FIRST_STATE 1
#define FIRST_NEMPTY_SYMBOL 1

// labels and states
bool TR_automa::is_state(int statenum)
{
	return(statenum<=_maxstate && statenum>0);
}

// restituisce il numero dello stato con quella etichetta
int TR_automa::label2state(const char *label)
{
	for (int s=0; s<_maxstate;s++) {
		if (*(st[s]._label)==label)
			return(s+1);
	}
	return(0);	
}


// resetta uno stato dell'automa (senza eliminarlo)
TR_automa & TR_automa::reset_state(int statenum)
{
	int _from,_to;
	if (statenum>0 && statenum<=_maxstate) {
		_from=statenum;
		_to=statenum;
	} else {
		_maxstate=0;
		_isdeterministic=TRUE;
		_from=1;
		_to=MAXSTATES;
	}	
	{
		for (int i=_from; i<=_to;i++) {
			set_label(i,"");
			set_final(i,FALSE);
			for (int j=0; j<MAXSIMBOLS;j++) {
				del_trans(i,j);	
			}
		}			
	}
	return *this;
}

// aggiunge una label
int TR_automa::add_state(const char * label)
{
	_maxstate++;
	reset_state(_maxstate);
	set_label(_maxstate,label);
	return _maxstate;
}

TR_automa & TR_automa::set_label(int statenum, const char * name)
{
	(*(st[statenum-1]._label))=name;
	return *this;
}

const char * TR_automa::label(int statenum)
{
	return (*(st[statenum-1]._label));
}

bool TR_automa::is_final(int statenum) const
{	
	return (st[statenum-1]._final);
}

bool TR_automa::is_final(TStateset ss ) const
{	
	bool retv=FALSE;
	int statenum=ss.get_first();
	do {
		retv=retv || is_final(statenum);
	} while ((statenum=ss.get_next())>0);
		
	return retv;
}

TR_automa & TR_automa::set_final(int statenum,bool v)
{
	st[statenum-1]._final=v;
	return *this;
}

//*******************
// bows
void TR_automa::add_tran(int statenum, unsigned char symbol,int next)
{
	st[statenum-1]._transaction[symbol]=next;		
	if (symbol==EPSILON)
		_isdeterministic=FALSE;
}

void TR_automa::del_trans(int statenum, unsigned char symbol)
{
	st[statenum-1]._transaction[symbol]=0;
}

int TR_automa::trans_to(int statenum, unsigned char symbol)
{
	return (st[statenum-1]._transaction[symbol]);		
}

// costruttore che crea un automa non deterministico 
// (transazioni Epsilon ma nessuna transazione multipla)
// eventualmente duplica un automa (e lo rende deterministico)
TR_automa::TR_automa(TR_automa * aa,bool makedet) 
{
	TStateset arrival;
	TStateset newstateset;
	TString16  tmplabel;
	int curr_new_state;

	for (int i=0; i<MAXSTATES;i++) {
		st[i]._label = new TString16;
	}			
	if (aa==NULL) {
	  reset_state();
		return;
	}
	if (makedet) {
	  reset_state();
		// crea il primo stato nell'automa deterministico
		set2label(aa->union_of_closures(newstateset.singleton(FIRST_STATE)),tmplabel);
		add_state(tmplabel);
		curr_new_state=FIRST_STATE;
		while (is_state(curr_new_state)) {
			// determina l'insieme degli stati dell'automa non deterministico
			// che corrispondono a questo stato dell'automa deterministico
			label2set(label(curr_new_state),newstateset);
			// lo stato è finale se include stati finali dell'automa non det.
			set_final(curr_new_state,aa->is_final(newstateset));
			// determina tutte le transazioni 
			for (short symbol=FIRST_NEMPTY_SYMBOL; symbol<MAXSIMBOLS; symbol++) {
				// determina lo stato di arrivo nell'automa det.:
        // esso è pari all'insieme degli stati raggiunti col questo simbolo 
        // dal sottoinsieme degli stati dell'automa non det. che etichetta il nuovo stato nell'automa det.
        arrival.empty();
       	int new_next,old_next;
        int old_state=newstateset.get_first() ;
        do {
        	if (old_next=aa->trans_to(old_state,symbol))
        		arrival.enclose(old_next);
        } while ((old_state=newstateset.get_next())>0);
        if (!arrival.is_empty()) {
        		// crea il nuovo arco nell'automa deterministico
        	set2label(aa->union_of_closures(arrival),tmplabel);
    			if (!(new_next=label2state(tmplabel))) {
					new_next=add_state(tmplabel);
					}	
					add_tran(curr_new_state,symbol,new_next);
				}
			}
			curr_new_state++;
		}
	  _isdeterministic=TRUE;
	} // fine  conversione
	else 
	{
		*this=*aa;
		for (int i=0; i<MAXSTATES;i++) {
			st[i]._label= aa->st[i]._label;
		}			
	}
}


TR_automa::~TR_automa()
{ 
	for (int i=0; i<MAXSTATES;i++) {
		delete (st[i]._label);
	}			
}


void TR_automa::set2label(const TStateset ss,TString16 & label)
{
	char coded[MAXSTATES];
	char c=0;
	for (int i= 0; i< MAXSTATES; i++) {
		if (ss.is_member(i)) 
			coded[c++]=(char)(i+'0');
	}
	coded[c]='\0';
	label=coded;		
}

void TR_automa::label2set(const TString16 & label,TStateset & ss)
{
	ss.empty();
	for (int i= 0; i<= label.len();i++)
		ss.enclose(int(label[i]-'0'));
}


// costruisce l'unione delle epslilon closures
TStateset TR_automa::union_of_closures(TStateset &start_set)
{
	TStateset u_of_clo,clo;
	int _state,_next;
	bool toadd;
	
	
	u_of_clo.empty();
	_state=start_set.get_first() ;
  do {
		// la chiusura è composta dallo stato e tutte le sue transazioni epsilon
		clo.singleton(_state);
		do {
			int _state2=clo.get_first();
			do {
				toadd=((_next=trans_to(_state2,EPSILON)) && (!clo.is_member(_next)));
				if (toadd)
					clo.enclose(_next);
			} while	((_state2=clo.get_next())>0);
		} while (toadd) ;
		u_of_clo.cap(clo);
	} while ((_state=start_set.get_next())>0);
	return u_of_clo;
}

// tenta di riconoscere la stringa passata
bool TR_automa::recognized(const char * t_str) 
{
	if (_isdeterministic) {
		// ricoNosce la stringa di token
		int curr_state=FIRST_STATE;
		for (int i=0; t_str[i]; i++) {
			if (!(curr_state=trans_to(curr_state,t_str[i])))
				// fine per mancanza di trasizioni
				return FALSE; 
		}
		// fine per mancanza di caratteri di input della stringa
		return (is_final(curr_state));
	} else {
		//    
		return FALSE;
	}
}


// *******************************
// gestione di stringhe di metacaratteri
// *******************************
#define C_ESCAPEMETA '\\'
#define S_BLANK 1
#define S_DIGIT 2
#define S_LETTER 3
#define S_ANY 4

bool TMetachar::recognized(const char * s)
{
	return(_au->recognized(s));
}
        
void TMetachar::add_tran(int s,unsigned char metasymbol, int nextstate)
{
		unsigned char c;
		switch (metasymbol) {
			case EPSILON:// blank
				_au->add_tran(s,EPSILON,nextstate);
			break;
			case S_BLANK:// blank
				_au->add_tran(s,' ',nextstate);
			break;
			case S_DIGIT:// cifra 
				for (c='0';c<='9';c++)
					_au->add_tran(s,c,nextstate);
			break;
			case S_LETTER: // lettera 
				for (c='a';c<='z';c++)
					_au->add_tran(s,c,nextstate);
				for (c='A';c<='Z';c++)
					_au->add_tran(s,c,nextstate);
			break;
			case S_ANY: // qualsiasi carattere 
				for (c=MAXSIMBOLS-1;c>=FIRST_NEMPTY_SYMBOL;c--)
					_au->add_tran(s,c,nextstate);
			break;
		}
}

// restituisce la stringa di metacaratteri del linguaggio riconosciuto
const char * TMetachar::language() const
{
	return(_language);
}

void TMetachar::set_language(const char * language)
{
	int s;
	bool escaped_char=FALSE;
  TString16 label("-"),nextlabel("-");
  
	// crea gli insiemi di metacaratteri standard
	strcpy(_metach_mand,"0LA&");
	strcpy(_metach_opz,"#9?ac");

	
	_language=language;
	// crea l'automa 
	_au->reset_state();
	for (int i=0; language[i]; i++) {
		label[0]='a'+i;
		nextlabel[0]='a'+i+1;
		if (language[i]!=C_ESCAPEMETA) {
			if (!escaped_char)
			{
			// meta-caratteri e literal fuori set
				switch (language[i]) {
					case '0':// cifra obbligatoria
						s=_au->add_state(label);
						add_tran(s,S_DIGIT,s+1);
					break;
					case '9':// cifra opzionale
						s=_au->add_state(label);
						add_tran(s,EPSILON,s+1);
						add_tran(s,S_DIGIT,s+1);
					break;
					case '#':// cifra o blank opzionale
						s=_au->add_state(label);
						add_tran(s,EPSILON,s+1);
						add_tran(s,S_BLANK,s+1);
						add_tran(s,S_DIGIT,s+1);
						break;
					case 'L': // lettera obbligatoria
						s=_au->add_state(label);
						add_tran(s,S_LETTER,s+1);
					break;
					case '?': // lettera opzionale
						s=_au->add_state(label);
						add_tran(s,EPSILON,s+1);
						add_tran(s,S_LETTER,s+1);
					break;
					case 'A': // lettera o numero obbligatorio
						s=_au->add_state(label);
						add_tran(s,S_LETTER,s+1);
						add_tran(s,S_DIGIT,s+1);
					break;
					case 'a': // lettera o numero opzionale
						s=_au->add_state(label);
						add_tran(s,EPSILON,s+1);
						add_tran(s,S_LETTER,s+1);
						add_tran(s,S_DIGIT,s+1);
					break;
					case '&': // qualsiasi carattere obbligatorio
						s=_au->add_state(label);
						add_tran(s,S_ANY,s+1);
					break;
					case 'c': // qualsiasi carattere opzionale
						s=_au->add_state(label);
						add_tran(s,EPSILON,s+1);
						add_tran(s,S_ANY,s+1);
					break;
					default:
						s=_au->add_state(label);
						_au->add_tran(s,language[i],s+1);
					break;
				}
			} else {
			// escaped char
					s=_au->add_state(label);
				_au->add_tran(s,language[i],s+1);
			}
		}
		escaped_char=(language[i]==C_ESCAPEMETA);
	} // end of loop
	// aggiunge lo stato finale
	s=_au->add_state(nextlabel);
	_au->set_final(s);

}

// ricerca caratteri del set opzionale
bool TMetachar::has_opzchars(const char * pattern)
{
	int i=0; 
	bool next_literal=FALSE;
	
	while (pattern[i]) {           
		if (!next_literal && strchr(_metach_opz,pattern[i]))
			return(TRUE);
		next_literal=(!next_literal && pattern[i]==C_ESCAPEMETA);
		i++;
	}
	return(FALSE);
}

// ricerca caratteri del set opzionale
bool TMetachar::has_mandchars(const char * pattern)
{
	int i=0; 
	bool next_literal=FALSE;
	
	while (pattern[i]) {
		if (next_literal || strchr(_metach_mand,pattern[i]))
			return(TRUE);
		next_literal=(!next_literal && pattern[i]==C_ESCAPEMETA);
		i++;
	}
	return(FALSE);
}

// stabilisce la lunghezza della stringa di metacaratteri
int TMetachar::maxstrlen(const char * pattern) const
{
	int i=0,l=0; 
	bool next_literal=FALSE;
	
	while (pattern[i]) {
		if (!next_literal)
			l++;
		next_literal=(!next_literal && pattern[i]==C_ESCAPEMETA);
		i++;
	}
	return(l);
}

// costruttore e distruttore
TMetachar::TMetachar ()
{
	_au = new TR_automa;       
	set_language("");
}

TMetachar::TMetachar (const char * metastr)
{
	// crea l'automa e lo trasforma in deterministico
//	TR_automa auxau;
//	set_language(&auxau,metastr);
	_au=new TR_automa;
	set_language(metastr);
	if (!_au->is_deterministic()) {
		TR_automa * auxau = new TR_automa(_au,TRUE);       
		delete _au;
		_au=auxau;
	}	
}

TMetachar::~TMetachar ()
{
	delete _au;
}

