#include "lemfst.h"

LemFST::LemFST(const char* plik_fst)
{
    fst = StdFst::Read(plik_fst);
}


LemFST::~LemFST(void)
{
	delete fst;
	path.clear();
}

/** Zwraca true jezeli s jest koncowym */
bool LemFST::accept(State s) {
	if (s == fst::kNoStateId) return false;
	return fst->Final(s)!=StdFst::Weight::Zero();
}


LemFST::State LemFST::start() { 
	return fst->Start();
}


/** Przechodzi do nastepnego stanu po jednym znaku */
LemFST::State LemFST::next(State s, Char c) {
	if (s==fst::kNoStateId) return fst::kNoStateId;
	ArcsIt aiter(*fst, s);
	for (; !aiter.Done(); aiter.Next()) {
		StdArc arc = aiter.Value();	
		if (arc.ilabel==c) return arc.nextstate;
		
	}
	return fst::kNoStateId;
}

/** Konsumuje slowo W zaczynajac w stanie s.
  * Zwraca:
  * State (int64) -  w jakim sie znajdzie
  * kNoStateId (-1) - gdy nie moze dokonac takiego przejscia.
  */
LemFST::State LemFST::next(State s, Word w) {
	if (s==fst::kNoStateId) return fst::kNoStateId;
	LemFST::WordIt it;
	State ns = s;
	for(it=w.begin(); it!=w.end(); ++it) {
		ns = next(ns, *it);
		if (ns==fst::kNoStateId) {
			return fst::kNoStateId;
		}
	}
	return ns;

}

/** Funkcja zwraca kolejne sciezki z automatu w formie generatora.
  * Ustawienie s na kNoStateId (lub -1) restartuje generator.
  * Dopoki s!=-1 metoda zwraca przy kolejnych wywolaniach kolejne sciezki od stanu s 
  * do stanow konczacych.
  * Sciezke mozna odzyskac poprzez metode getPath();
  */
long LemFST::cont(State s, Word *result, int maxPath) {
	// Restart generatora po podaniu -1
	// Lub przekroczeniu dlugosci sciezki
	if (s==fst::kNoStateId || path.size() > maxPath) {
		path.clear();
		return 0;
	}


	// Stos jest pusty. Mozemy dodac do niego wierzcholek poczatkowy.
	if (path.empty()) {
		path.push_back(getStateInfo(s, fst::kNoStateId, 0));
	}
	while(!path.empty()) {

		// Zdejmujemy ze stosu stan i jego iterator:
		StateInfo *state = &path.back();
		ArcsIt *ait = state->it;

		// Jezeli stan jest koncowy to zwracamy sciezke do stanu "s"
		// Dodatkowo sprawdzamy czy juz nie startowalismy z tego stanu, aby nie wyswietlac wielokrotnie tego samego
		if (!state->checked && accept(state->id)) {
			state->checked = true;
			StateInfo tState;
			PathRevIt pit=path.rbegin();
			State prevId = pit->id;
            		result->clear();
			for(; pit!=path.rend(); ++pit) {
				tState = (*pit);
				if (tState.id == prevId && tState.prev!=fst::kNoStateId && tState.id !=s) {
					ArcsIt *tArcIt = tState.it;
					result->push_front(tState.symbol);
				}
				if (tState.prev==fst::kNoStateId) break;
				prevId = tState.prev;		
			}
			ait->Next();
			return path.size();
		}


		// Skonczylismy sprawdzac dany iterator (stan).
		// Dlatego mozemy go usunac ze stosu.
		if (ait->Done()) {
			path.pop_back();
			// Jezeli jakies wierzcholki sa jeszcze na stosie to kontynuuj:
			if (path.size() > 0) continue;
			// w.p.p zakoncz.
			else return 0;
		}


		// Odwiedzamy stan.
		// Dodajemy jego nastepnikow:
		for(; !ait->Done(); ait->Next()) {
			State next = ait->Value().nextstate;
			Char isymbol = ait->Value().ilabel;
			path.push_back(getStateInfo(next, state->id, isymbol));
		}
	} // Koniec (while(!path.empty())



	return 0;
}


/** Zwraca strukture StateInfo uzupelniajac ja o dane
  * Parametry:
  * s - ktory stan opisujemy
  * prev - poprzednik stanu w automacie
  * isymbol - symbol po jakim dostalismy sie z 'prev' do 's'
  */
inline const LemFST::StateInfo LemFST::getStateInfo(State s, State prev, Char isymbol) {
	StateInfo sInfo;
	sInfo.id = s; 
	sInfo.it = new ArcsIt(*fst, s); //Wyciagamy iterator z automatu
	sInfo.symbol = isymbol; 
	sInfo.prev = prev;
	sInfo.checked = false; // Czy stan byl juz sprawdzany jako koncowy
	return sInfo;
}


