#ifndef __stef_h__
#define __stef_h__

#include <vector>
#include <map>
#include <list>
#include <string>
#include <iostream>

#include "segd.h"
#include "smali.h"

#include <stack>

namespace ff {

using namespace std;

typedef enum {
	AFRIT = 1,
	GILDI = 2
} vidfangsTegund;

typedef list<pair<string, int> > symtab;
typedef list<string> stringlist;

struct symloc {
	unsigned int foldun;
	int offset;
	symloc() : foldun(0), offset(0) {}
};

class Stef {
	string _nafn;
	symtab _afritsVidfong;
	symtab _gildisVidfong;
	symtab _localBreytur;
	list<string> _innfluttarBreytur;

	list<Segd*> _frumstillingar;
	list<Segd*> _segdaruna;

	int _stackSize;
	int _fjoldiAfritsVidfanga;
	int _fjoldiGildisVidfanga;
	int _fjoldiLocalBreyta;
	int _steflokLabel;

	Stef* _parent;
	int _nestingLevel;
	map<string, Stef*> _undirStef;

	stack<int> _stackMarks; /* fyrir lykkjur */
	stack<int> _utLabels;   /* ditto */

public:
	Stef(string& nafn)
		: _nafn(nafn), _parent(NULL), _nestingLevel(0),
		  _stackSize(0), _fjoldiAfritsVidfanga(0), _fjoldiGildisVidfanga(0),
		  _fjoldiLocalBreyta(0), _steflokLabel(newlabel())
	{}
	virtual ~Stef();

	/** Nafn stefsins.
	 \return nafn stefsins.
	 */
	const string& getNafn() const { return _nafn; }

	/** Lokamerki stefsins.
	 \return merki sem er skrifaš ķ žulu strax į undan eftirmįla
	 */
	int getEndLabel() const { return _steflokLabel; }

	/** Athugar hvort nafn er skilgreint inni ķ stefinu.
	 \return true ž.ž.a.a. nafn er skilgreint višfangs eša breytunafn
	 */
	bool isDefined(const string& nafn);

	/** Athugar hvort undirstef er skilgreint innan ķ stefinu
	 \return 0 ef ekki er til undirstef sem er hęgt aš kalla ķ frį nśverandi
	   stašsetningu ķ žulu, annars földunarhęš viškomandi undirstefs.
	 */
	int isDefinedUndirstef(const string& nafn, int n, int m);

	/** Skilar nafni į merki undirfalls 
	 \pre isDefinedUndirstef(...) == true
	 */
	string getUndirstefLabel(const string& nafn, int n, int m);

	/** Bętir viš višfangi af tegund t meš nafni nafn.
	 \pre isLocallyDefined(nafn) == false
	 \post Stefiš žekkir stašsetningu višfangsins į stafla
	 */
	void addVidfang(vidfangsTegund t, string& nafn);

	/** Sękir fjölda žegar skilgreindra višfanga.
	 \return fjölda žegar skilgreindra višfanga af tegund t
	 */
	int getFjoldiVidfanga(vidfangsTegund t);

	/** Bętir viš nafni innfluttrar breytu
	 \pre isLocallyDefined(nafn) == false
	 \post Stefiš žekkir nafn sem nafn innfluttrar breytu
	 */
	void addInnflutt(string& nafn);

	/** Bętir viš stašvęrri breytu, hugsanlega meš frumstillingu
	 \pre isLocallyDefined(nafn) == false og ef
	      frumstilling er annaš hvort null eša bendir į löglega Segš
	 \post Stefiš žekkir nafn sem breytunafn įsamt stašsetningu į stafla,
	       og mun skrifa śt žulu til aš frumstilla breytuna. Žetta stef
		   mun sjį um aš losa minni fyrir frumstillinguna
     */
	void addStadvaer(string& nafn, Segd* frumstilling = NULL);

	/** Bętir viš undirstefi undir žetta stef.
	 \pre isLocallyDefinedUndirstef(stef->_nafn) == false og stef er
	      bendir į löglegt Stef.
	 \post Žetta stef žekkir nafn sem nafn undirstefs og mun skrifa śt
	       žulu žess. Kallaš hefur veriš ķ stef->setParent meš réttu višf.
		   Žetta Stef mun sjį um aš losa minni sem stef bendir ķ.
	 */
	void addUndirstef(Stef* stef);

	/** Bętir viš segš ķ stefiš.
	 \pre s er bendir ķ löglega Segš
	 \post stefiš mun skrifa śt žulu segšarinnar strax į eftir žulum žeirra
	       segša sem žegar hafa veriš settar inn meš žessu boši. Kallaš hefur
		   veriš ķ s->setUmlykjandiStef. Žetta stef mun sjį um aš losa minni
		   sem stef bendir ķ
	 */
	void addSegd(Segd* s);

	/** Setur bendi ķ stefiš ķ nęstu földunarhęš fyrir ofan.
	 \pre parent er bendir ķ löglegt Stef
	 \post Žetta stef inniheldur bendi ķ parent og heiltölu földunardżpt, sem
	       er einum hęrri en samsvarandi tala ķ parent, žetta stef um sjį
		   um aš losa minni sem s bendir ķ
	 */
	void setParent(Stef* parent);
	
	/** Sękir földunardżpt žessa falls.
	 \return földunardżpt žessa falls (0 ef žetta er grunnfall)
	 */
	int getNestingLevel();

	/** Sękir stašsetningu višfangs eša breytu į stafla.
	 \pre isDefined(name) == true
	 \return symloc struct sem inniheldur tölur földun og offset. Földun
	   segir til um hvaš žarf aš fara upp um margar vakningarfęrslur til aš finna
	   viškomandi breytu, og offset inniheldur stašsetningu breytunnar
	   m.v. grunnstak (BP) žeirrar vakningarfęrslu ķ bętum
	   Ef name er nafn į innfluttri breytu skilar falliš sérgildinu {0,0}
	 */
	symloc getSymbolLocation(const string& name);

	/** Skilar streng sem auškennir falliš.
	 \return streng sem auškennir falliš śt frį nafni žess, fjölda višfanga aš hvorri
	  gerš og nęsta falli fyrir ofan ķ földunarhęš.
	 */
	string getInternalNafn();

	/** Gefur fallinu tilkynningu um aš žula breyti stęrš staflans.
	 \post Stęrš staflans hefur breyst um d bęti.
	 */
	void stackDelta(int d);

	/** Sękir stęrš staflans m.v. žį žulu sem hefur veriš skrifuš śt.
	 \return Stęrš staflans frį sķšustu stašvęru breytu ķ bętum eftir aš
	   sś žula sem hefur veriš skrifuš śt hefur keyrt.
	 */
	int getStackSize();

	/** Setur nśverandi staflastęrš efst į stafla.
	  \post nęsta kall ķ lastStackMark mun skila nśverandi staflastęrš
	  */
	void markStack();

	/** Gleymir sķšasta gildi śr markStack.
	  \pre Kallaš hefur veriš oftar ķ markStack en unmarkStack
	  \post nęsta gildi śr lastStackMark mun verša stęrš staflans viš žarsķšasta markStack
	  */
	void unmarkStack();

	/** Sękir stęrš staflans viš sķšasta markStack
	 \return Stęrš staflans žegar kallaš var ķ markStack sķšast, eša 0 ef
	   kallaš hefur veriš jafn oft ķ markStack og unmarkStack
	 */
	int lastStackMark();

	/* TODO: docs, gera ekki inline? */
	void pushUtLabel(int l) { _utLabels.push(l); }
	void popUtLabel() { _utLabels.pop(); }
	int getUtLabel() { return _utLabels.top(); }

	/** Smķšar žulu fyrir stefiš.
	 \pre Ekki veršur kallaš aftur ķ föllin addInnflutt, addSegd, addStadvaer,
	   addUndirstef, addVidfang eša setParent. out er löglegur śttaksstraumur.
	 \post Bśiš er aš skrifa ķ out žulu stefsins.
	 */
	void generate(ostream& out);

	/** Athugar hvort nafn er locally skilgreint
	  \return true ž.ž.a.a. nafn sé skilgreint breytu- eša višfangsnafn ķ žessu stefi
	  */
	bool isLocallyDefined(const string& nafn);

	/** Athugar hvort nafn er nafn į beinu undirstefi
	  \return true ž.ž.a.a. nafn sé nafn į beinu undirstefi žessa falls
	 */
	bool isLocallyDefinedUndirstef(const string& nafn, int n, int m);

private:
	/** Leitar ķ symboltöflu.
	 \pre s er lögleg symtab, nafn er löglegur strengur
	 \return iterator sem bendir į pariš <str,i> meš str==nafn ef žaš er til ķ s,
	   s.end() annars
	 */
	symtab::iterator findSymbol(symtab& s, const string& nafn);

};

}

#endif /* __stef_h__ */