import java.io.IOException;
import java.io.Writer;


public class GenJava implements Visitor {
	Writer out;
	String indent="";
	void addIndent(){
		indent=indent+"  ";
	}
	void remIndent(){
		indent=indent.substring(2);
	}
	
	void newLine(){
		try {
			out.write("\n");
			out.write(indent);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public GenJava(Writer out) {
		super();
		this.out = out;
	}

	@Override
	public void visit(Type type) {
		try {
			out.write(type.name);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void visit(Param param) {
		param.type.welcome(this);
		try {
			out.write(" "+param.name);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void visit(IfExp ifExp) {
		try {
			out.write("(");
			ifExp.cond.welcome(this);
			out.write("?");
			ifExp.alt1.welcome(this);
			out.write(":");
			ifExp.alt2.welcome(this);
			out.write(")");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}

	@Override
	public void visit(App app) {
		try {
			out.write(app.functionName+"(");
			boolean first = true;
			for (Exp e:app.args){
				if (first) first=false;
				else out.write(",");
				e.welcome(this);
			}
			out.write(")");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void visit(OpExp opExp) {
		try {
			out.write("(");
			opExp.left.welcome(this);
			out.write(opExp.name);
			opExp.right.welcome(this);
			out.write(")");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void visit(Var var) {
		try {
			out.write(var.name);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void visit(IntLiteral intLiteral) {
		try {
			out.write(""+intLiteral.n);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void visit(FunctionDef functionDef) {
		newLine();
		try {
			out.write("static ");
			functionDef.resultType.welcome(this);
			out.write(" "+functionDef.name+"(");
			boolean first=true;
			for (Param p :functionDef.params){
				if (first){
					first=false;
				}else out.write(",");
				p.welcome(this);
			}
			out.write("){");
			addIndent();
			newLine();
			out.write("return ");
			functionDef.body.welcome(this);
			out.write(";");
			remIndent();
			newLine();
			out.write("}");		
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}

	@Override
	public void visit(Program program) {
		try {
			out.write("class Prog{");
			addIndent();
			
			for(FunctionDef f:program.functions){
				f.welcome(this);
			}
			newLine();
			out.write("public static void main(String []_){");
			addIndent();
			newLine();
			out.write("System.out.println(");
			program.e.welcome(this);
			out.write(");");
			remIndent();
			newLine();
			out.write("}");
			remIndent();
			newLine();
			out.write("}");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}

}
