/************************************************
  Team: Jon Gregory, Gor Nishanov
  Project #2 Part 2
    for RPI course 66-648 Compiler Design Theory

  stmts.h:
    clases representing java statements

    */

struct Label : public QuickNew {
	Identifier name;
	Label* nextInOwner;

	Label(Identifier nam):name(nam),nextInOwner(0){}
};

class Statement : public Node {
	List<Label> labels;
public:
	Statement(int op):Node(op),nextInOwner(0){}

	virtual bool firstConstructor() const {return false;}
	void    addLabel(Label* lab){labels.add(lab); }
	void    addLabel(Identifier id){addLabel(new Label(id) ); }

	void printIndent(ostream& o, int indent) const {
		for (int i = 0 ; i < indent ; i++) o << "  ";	}

	virtual void print2(ostream& o, int) const {
		Label* l = labels.first;
		while(l != 0) {
			o << l->name << ": ";
			l = l -> nextInOwner;
		}
	}

	virtual void print(ostream& o) const {print2(o, (int)0);}

	static void stmtPrint(ostream& o, Statement* st, int indent) {
		if(st == null) o << "<empty>";
		else st->print2(o, indent);
	}

	Statement * glue(Statement* b) {
		nextInOwner = b;
		return this;
	}
	Statement * nextInOwner;
};
typedef List<Statement> StmtList;

class BreakStatement : public Statement {
	Identifier lbl;
public:
	BreakStatement(Identifier label):Statement(BREAK), lbl(label) {}

	virtual void print2(ostream& o, int indent) const {
		Statement::print2(o, indent);
		o << "break";
		if (!(lbl == Identifier::nullstr)) {
			o << ' ' << lbl; }
		o << ';';
	}
};

class CaseStatement : public Statement {
	Expression* expr;
public:
	CaseStatement(Expression* e):Statement(CASE),expr(e) {}

	virtual void print2(ostream& o, int indent) const {
		Statement::print2(o, indent);
		if (expr == null) o << "default";
		else { o << "case "; safePrint(o, expr); }
		o << ':';
	}
};

class CatchStatement : public Statement {
	Expression * throwExpr;
	Identifier id;
	Statement * body;
	Field * field;
public:
	CatchStatement(Expression* Expr, Identifier Id, Statement* Body):
		Statement(CATCH), throwExpr(Expr), id(Id), body(Body), field(0) {}

	virtual void print2(ostream& o, int indent) const {
		Statement::print2(o, indent);
		o << "catch (" << throwExpr;
		o << ' ' << id  << ") ";
		if (body != null) {	body->print2(o, indent);	}
		else o << "<empty>";
	}
};

class ContinueStatement : public Statement {
	Identifier lbl;
public: ContinueStatement(Identifier label):Statement(CONTINUE),lbl(label){}

	virtual void print2(ostream& o, int indent) const {
		Statement::print2(o, indent);
		o << "continue" << ' ' << lbl << ';';
	}
};

class CompoundStatement : public Statement {
	Statement* stmts;
public:
	CompoundStatement(Statement* Stmts):Statement(STAT),stmts(Stmts){}

	virtual bool firstConstructor() const {
		return (stmts != 0) && stmts->firstConstructor();	}

	virtual void print2(ostream& o, int indent) const {
		Statement::print2(o, indent);
		cout << '{' << endl;
		Statement* s = stmts;
		while(s != 0) {
			printIndent(o, indent+1);
			s -> print2(o, indent + 1);
			o << endl; s = s->nextInOwner;
		}
		printIndent(o, indent);	o << '}';
	}
};

class DoStatement : public Statement {
	Statement *body;
	Expression *cond;

public:
	DoStatement(Statement* Body, Expression* Cond):
		Statement(DO), body(Body), cond(Cond) {}

	virtual void print2(ostream& o, int indent) const {
		Statement::print2(o, indent); o << "do ";
		body -> print2(o, indent);
		o << " while " << *cond << ';';
	}
};

class EmptyStatement : public Statement {
public:
  EmptyStatement():Statement(VOID) {}
	virtual void print2(ostream& o, int) const { o << ';' ; }
};

class DeclarationStatement : public Statement {
  int mod;
  Expression * type;
  Statement * args;

public:
  DeclarationStatement(int m, Expression *typ, Statement* arg):
    Statement(DECLARATION),mod(m),type(typ),args(arg){}

virtual void print2(ostream& o, int indent) const {
  o << "declare "; printMod(o, mod);
  Statement::print2(o, indent);
  o << type << ' ';
  Statement* s = args;
  while(s != 0) {
    if(s != args) o << ", ";
    s -> print(o);
    s = s -> nextInOwner;
  }
  o << ';';
 }
};

class FinallyStatement : public Statement {
	Statement* body;
	Statement* finalBody;
	bool finallyFinishes; // does finalBody never return?

public:
	FinallyStatement(Statement* Body, Statement* finalbody):
		Statement(FINALLY),body(Body), finalBody(finalbody) {}
	virtual void print2(ostream& o, int indent) const {
		Statement::print2(o, indent);
		o << "try ";
		stmtPrint(o, body, indent);
		o << "finally ";
		stmtPrint(o, finalBody, indent);
	}
};

class ExpressionStatement : public Statement {
	Expression* expr;
public:
	ExpressionStatement(Expression* e):Statement(EXPRESSION),expr(e) {}

	bool firstConstructor() const {return expr->firstConstructor(); }

	virtual void print2(ostream& o, int indent) const {
		Statement::print2(o, indent);
		o << expr << ';';
	}
};


class ForStatement : public Statement {
	Statement* init;
	Expression* cond;
	Expression* inc;
	Statement* body;

public:
  ForStatement(Statement* ini, Expression* c, Expression* incr,Statement* Body)
    :Statement(FOR),init(ini),cond(c), inc(incr), body(Body){}

  virtual void print2(ostream& o, int indent) const {
    Statement::print2(o, indent);
    o << "for (";
    if (init != null) {
       init -> print2(o, indent); o << ' ';
   } else {
     o << "; ";
   }
   if (cond != null) {
     cond->print(o);
     o << " ";
  }
  o << "; ";
  if (inc != null) {inc->print(o);}
     o << ") ";
     if (body != null) {
       body->print2(o, indent);
     } else {
       o << ';';
     }
  }
};


class IfStatement : public Statement {
	Expression* cond;
	Statement* ifTrue;
	Statement* ifFalse;

public:
	IfStatement(Expression* c, Statement* Then, Statement* Else):
		Statement(IF),cond(c),ifTrue(Then),ifFalse(Else) {}

	virtual void print2(ostream& o, int indent) const {
		Statement::print2(o, indent);
		o << "if " << cond << ' ';
		ifTrue -> print2(o, indent);
		if (ifFalse != null) {
			o << " else ";
			ifFalse -> print2(o, indent);
		}
	}
};

class ReturnStatement : public Statement {
	Expression* expr;
public:
	ReturnStatement(Expression* e):Statement(RETURN),expr(e){}

	virtual void print2(ostream& o, int indent) const {
		Statement::print2(o, indent);
		o << "return";
		if (expr != null) o << ' ' << expr;
		o << ';';
	}
};

class TryStatement : public Statement {
	Statement* body;
	Statement* args; // []

public:
	TryStatement(Statement* Body, Statement* arg):
		Statement(TRY), body(Body), args(arg){}

	virtual void print2(ostream& o, int indent) const {
		Statement::print2(o, indent);
		o << "try ";
		stmtPrint(o, body, indent);
		Statement *s = args;
		while(s != 0) {
			o << ' ' << s;
			s = s->nextInOwner;
		}
	}
};

class SwitchStatement : public Statement {
	Expression* expr;
	Statement* args; //[];

public:
	SwitchStatement(Expression* e, Statement* arg):
		Statement(SWITCH),expr(e),args(arg) {}

	virtual void print2(ostream& o, int indent) const {
		Statement::print2(o, indent);
		o << "switch (" << expr << ") {" << endl;
		Statement *s = args;
		while(s != 0) {
			printIndent(o, indent + 1);
			s -> print2(o, indent + 1);
			o << endl;
		}
		printIndent(o, indent);
		o << '}';
	}
};

class WhileStatement : public Statement {
	Expression* cond;
	Statement* body;
public:
	WhileStatement(Expression* c, Statement* b):
		Statement(WHILE),cond(c), body(b) {}

	virtual void print2(ostream& o, int indent) const {
		Statement::print2(o, indent);
		o << "while " << cond;
		if (body != null) {
			o << ' ';
			body -> print2(o, indent);
		} else {
			o << ';';
		}
	}
};

class SynchronizedStatement : public Statement {
	Expression* expr;
	Statement* body;

public:
	SynchronizedStatement(Expression* e, Statement* b):
		Statement(SYNCHRONIZED), expr(e), body(b) {}

	virtual void print2(ostream& o, int indent) const {
		Statement::print2(o, indent);
		o << "synchronized " << expr << ' ';
		stmtPrint(o, body, indent);
	}
};


class VarDeclarationStatement : public Statement {
	Field* field;
	Expression* expr;
public:
	VarDeclarationStatement(Expression* e):
		Statement(VARDECLARATION), expr(e), field(0) {}
	VarDeclarationStatement(Field* f, Expression* e):
		Statement(VARDECLARATION), expr(e), field(f) {}

	virtual void print2(ostream& o, int indent) const {
		o << "local ";
		if (field != null) {
//			o << *field;
			if (expr != null) {
				o << " = " << *expr;
			}
		} else {
			o << expr << ';';
		}
	}
};



class ThrowStatement : public Statement {
	Expression* expr;

public:
	ThrowStatement(Expression* e):Statement(THROW),expr(e){}
	virtual void print2(ostream& o, int indent) const {
		Statement::print2(o, indent);
		o << "throw " << expr << ';';
	}
};

