#ifndef TRANSPILER_H
#define TRANSPILER_H
/*
**	Yab2Cpp
**
**	Transpiler by Samuel D. Crow
**
**	Based on Yab
**
*/
#include <string>
#include <list>
#include <unordered_map>
#include <memory>
#include <iostream>

using namespace std;

#define VER_MAJOR 0
#define VER_MINOR 0
#define VER_RELEASE 1

/*
** list of all compiler errors 
**
** Note:  There must be a corresponding error message
** to each entry in the COMPILE_ERROR_NAMES constant array.
*/
enum COMPILE_ERRORS {
	E_OK=0,
	E_BAD_SYNTAX,
	E_TYPE_MISMATCH,
	E_BAD_ALLOC,
	E_STACK_UNDERFLOW,
	E_INTERNAL,
	E_DUPLICATE_SYMBOL
};

/* These correspond to the enum COMPILE_ERRORS. */
const char *COMPILE_ERROR_NAMES[]={
	"no error",
	"incorrect syntax",
	"wrong type",
	"failed allocation",
	"stack underflow",
	"internal compiler error",
	"duplicated label"
};

extern enum COMPILEERRORS errorLevel;
	
/* flags used internally by the compiler
	(must be powers of 2) */
#define COMPILE 1
#define DUMP 2
#define DEBUG 4

/* list of all variable and constant types */
enum TYPES
{
	T_NONE=0,
	T_STRING,
	T_INT,
	T_FLOAT,
	T_STRINGVAR,
	T_INTVAR,
	T_FLOATVAR,
	T_INTCALL_ARRAY,
	T_FLOATCALL_ARRAY,
	T_STRINGCALL_ARRAY,
}
/* list of all kinds of other code structures */
enum CODES
{
	T_PRINT=0,
	T_PRINTSEGMENT,
	T_WHILELOOP,
	T_FORLOOP,
	T_REPEATLOOP,
	T_DOLOOP,
	T_IF,
	T_PROCEDURE,
	T_FUNCTION,
	T_GOSUB,
	T_ASSIGNMENT,
	T_LABEL,
	T_PARAMLIST,
	T_DATAITEM
};

/* These correspond to the types of enum TYPES. */
const string TYPENAMES[]={
	"none",
	"string constant",
	"integer constant",
	"floating point constant",
	"string variable",
	"integer variable",
	"floating point variable",
	"string array or function",
	"integer array or function",
	"floating point array or function"
};

const string CODETYPES[]={
	"print sequence",
	"print segment",
	"while loop",
	"for loop",
	"repeat loop",
	"do loop",
	"if statement",
	"procedure statement",
	"function statement",
	"assignment",
	"label",
	"parameter list or array index",
	"data item"
};

typedef union
{
	double d;
	int i;
	string *s;
}boxTypes;

/* subtypes of the T_PRINTSEPARATOR type */
enum SEPARATORS
{
	S_COMMA,
	S_SEMICOLON,
	S_LINEFEED
};

enum OPERATORS
{
	O_PLUS,
	O_MINUS,
	O_MULTIPLY,
	O_DIVIDE,
	O_REMAINDER,
	O_NEGATE,
	O_EXPONENT,
	O_GREATER,
	O_LESS,
	O_EQUAL,
	O_GREATER_EQUAL,
	O_LESS_EQUAL,
	O_UNEQUAL,
	O_NOT,
	O_INVERT,
	O_OR,
	O_AND,
	O_TERM
};

/* internal states used by the parser */
class operands
{
	enum TYPES type;
	unsigned int id;
	static unsigned int nextID;
	static unordered_map<string &, operands *> globals;
	static unordered_map<string &, unsigned int> strConst;
public:
	enum TYPES getType() const {return type;}
	unsigned int getID() const {return id;}

	static void dumpVars(ostream &out);
	static unsigned int getOrCreateStr(string &s);

	enum TYPES getSimpleVarType();
	void generateBox(ostream &out);
	virtual void boxName(ostream &out);
	enum TYPES coerceTypes();

	explicit operands(enum TYPES t);
	virtual ~operands()
	{}
};

/* expression can be terminal or non-terminal */
class expression
{
	operands *op;
	expression *left;
	expression *right;
	enum OPERATORS oper;
public:
	enum OPERATORS getOp() const {return oper;}
	expression *getLeft() const {return left;}
	expression *getRight() const {return right;}

	bool isBinOp();
	operands *evaluate();

	/* r is NULL for unary operators */
	expression(expression *l, enum OPERATORS o, expression *r=NULL)
	{
		this->left=l;
		this->right=r;
		this->oper=o;
	}
	virtual ~expression();
};

/* parent class of all code types */
class codeType
{
	unsigned int id;
	enum CODES type;
	static unsigned int nextID;
	static list<codeType *> nesting;
public:
	enum CODES getType() const {return this->type;}
	unsigned int getID() const {return this->id;}

	codeType *getCurrent();
	virtual void close();

	explicit codeType(enum CODES t);
	virtual ~codeType()
	{
	}
};

class label
{
	unsigned int id;
	static unsigned int nextID;
	static unordered_map<string, label *> lookup;
public:
	static dumpLabels(ostream &v);

	unsigned int getID() const {return id;}

	void generateJumpTo(ostream &out);
	void generateOnNSkip(ostream &k, list<label *> &dest);
	void generateOnNTo(ostream &out, expression *e);
	void generateCondJump(ostream &out, expression *e);
	void generate(ostream &out);
	
	static label *find(string &s);

	label(){this->id = ++nextID;}
	label(string &s)
	{
		label();
		lookup[s]=this;
	}

	virtual ~label()
	{}
};

/* if statement */
class conditional:public codeType
{
	shared_ptr<label>redo; /* for continue command */
	shared_ptr<label>done; /* for break or after "then" condition */
	shared_ptr<label>chain; /* For elsif command */
public:
	void generateContinue(ostream &out);
	void generateBreak(ostream &out);
	void alternative(ostream &out, expression *e=NULL); /* enable else or elsif condition */
	virtual void close(ostream &out) override; /* end if */

	explicit conditional(expression *e):codeType(T_IF);
	virtual ~conditional()
	{}
};

/* looping constructs */
class repeatLoop:public codeType
{
	shared_ptr<label>loopStart;
	shared_ptr<label>loopEnd;
public:
	virtual void close(ostream &out, expression *) override;

	explicit repeatLoop():codeType(T_REPEATLOOP);
	virtual ~repeatLoop()
	{}
};

class doLoop:public codeType
{
	shared_ptr<label>loopStart;
	shared_ptr<label>loopEnd;
public:
	virtual void close();

	explicit doLoop():codeType(T_DOLOOP);
	virtual ~doLoop()
	{}
};

class whileLoop:public codeType
{
	shared_ptr<label> loopContinue;
	shared_ptr<label> loopExit;
	expression cond;
public:
	virtual void close();

	explicit whileLoop(expression e):codeType(T_WHILELOOP);
	virtual ~whileLoop()
	{}
};

class variable;

/* TODO: Define expression constant ONE */
class forLoop:public codeType
{
	shared_ptr<variable *>var;
	shared_ptr<label>loopContinue;
	shared_ptr<label>loopExit;
	expression *step;
	expression *cond;
public:
	virtual void close();

	explicit forLoop(string &name, enum TYPES t, expression *start, expression *stop, expression *stepVal):assignment(name, t, start)
	{
		this->cond=stop;
		this->step=stepVal;
	}

	explicit forLoop(string &name, enum TYPES t, expression *start, expression *stop)
	{
		extern operands *ONE;
		forLoop(name, t, start, stop, ONE);
	}

	virtual ~forLoop();
};

class fn:codeType
{
	static unordered_map<string, operands *>locals;
	static unordered_map<string, operands *>statics;
	static unordered_map<string, fn>functions;
	static list<fn *> callStack;
	enum TYPES kind;
	shared_ptr<label>ret;
	unsigned int parameters;
public:
	static bool isCallStackEmpty() {return callStack.size()>0;}
	static void dumpCallStack(ostream &out);
	void setParameters(unsigned int num) const {this->parameters=num;}

	void addLocal(operands *);
	void addStatic(operands *);
	void generateCall(ostream &out, string &name, unsigned int params);
	void generateReturn(ostream &out, expression *expr=NULL);
	void generateGosub(ostream &out, shared_ptr<label> sub);
	/* must be called after label::generateOnNSkip */
	void generateOnNSub(ostream &out, expression *e);
	virtual void close();

	fn(string &name);
	fn(label *gosub);
	virtual ~fn();
};

class variable:public operands
{
public:
	static assignment(expression *value);
	explicit variable(ostream &scope, string &name, enum TYPES t):operands(t);
	virtual variable()
	{}
}

class arrayType:public variable
{
	list<unsigned int> dimensions;
public:
	virtual void boxName(ostream &out, list<unsigned int>indexes) override;

	explicit arrayType(ostream &scope, string &name, enum TYPES t, list<unsigned int>dim):variable(scope, name, t);
	virtual ~arrayType()
	{}
};

/* The next two structures are used to implement the PRINT statement. */
class printSegments
{
	expression *cargo;
	enum SEPARATORS kind;
public:
	printSegments(expression *e, enum SEPARATORS k)
	{
		this->cargo=e;
		this->kind=k;
	}
	printSegments(expression *e) {printSegments(e, S_LINEFEED);}
	printSegments() {printSegments(NULL);}
	virtual ~printSegments()
	{}
};

struct printStatement
{
	list<printSegments *> segments;
};

#endif
