// TODO support @Override etc properly.

public class JavaParser extends Tokenizer {
	protected Symbol S_openbracketclosebracket = Symbol.intern("[]");
	protected Symbol S_int = Symbol.intern("int");
	protected Symbol S_float = Symbol.intern("float");
	protected Symbol S_double = Symbol.intern("double");
	protected Symbol S_void = Symbol.intern("void");
	private ConsNode scope = null;
	protected TypeRefNode void_ref = new TypeRefNode(new ConsNode(S_void, null));
	
	public ModuleNode parse() {
		ModuleNode result = new ModuleNode();
		result.setContent(parseModuleBody());
		return result;
	}

	private ConsNode parseModuleBody() {
		if (getToken() != null && getToken() != S_starEOF) {
			if (hasProtection())
				parseProtection(); // FIXME save.
			if (hasComment())
				return new ConsNode(parseComment(), parseModuleBody(), true);
			else if (hasClass())
				return new ConsNode(parseClass(), parseModuleBody(), true);
			else if (hasImport())
				return new ConsNode(parseImport(), parseModuleBody(), true);
			else
				return (ConsNode) raiseError("<class>");
		} else
			return null;
	}

	public boolean hasComment() {
		return getToken() == S_slashstarstarslash || getToken() == S_slashslash;
	}

	public CommentNode parseComment() {
		// TODO Auto-generated method stub
		return null;
	}

	public boolean hasImport() {
		return getToken() == S_import;
	}

	public ImportNode parseImport() {
		StringBuffer buffer = new StringBuffer();
		assert (hasImport());
		consumeQ(S_import);
		while (getToken() != null && getToken() != S_semicolon) {
			String matchText = getInputData().getMatchText();
			buffer.append(matchText);
			consumeQ(S_ID);
			buffer.append(".");
			if (getToken() != S_dot)
				break;
			consumeQ(S_dot);
		}
		consumeQ(S_semicolon);
		String result = buffer.toString();
		return new ImportNode(result.substring(0, result.length() - 1));
	}

	public ClassNode parseClass() {
		consumeQ(S_class);
		Symbol ID = parse_ID();
		consumeQ(S_opencurlybrace);
		ConsNode body = parseClassBody();
		consumeQ(S_closecurlybrace);
		// TODO Auto-generated method stub
		return new ClassNode(ID, new BlockNode(body));
	}

	private ConsNode parseClassBody() {
		if (getToken() == S_closecurlybrace)
			return null;
		else
			return new ConsNode(parseClassMember(), parseClassBody(), true);
	}

	public boolean hasProtection() {
		return getToken() == S_public || getToken() == S_protected || getToken() == S_private || getToken() == S_final || getToken() == S_static;
	}

	protected ProtectionNode parseProtection() {
		assert (hasProtection());
		return new ProtectionNode(parseProtectionBody());
	}

	private ConsNode parseProtectionBody() {
		if (hasProtection()) {
			Symbol token = consumeQ();
			return new ConsNode(token, parseProtectionBody());
		} else
			return null;
	}

	protected TypeRefNode parseTypeSpec(Symbol ID) {
		// TODO Auto-generated method stub
		// TODO template args
		if (ID == null)
			ID = parse_ID();
		return new TypeRefNode(new ConsNode(ID, parseOptionalTemplateArgs()));
	}

	protected ConsNode parseTemplateArgsBody() {
		TypeRefNode head = parseTypeSpec(null);
		ConsNode rest = null;
		if (getToken() == S_comma) {
			consumeQ();
			rest = parseTemplateArgsBody();
		}
		return new ConsNode(head, rest);
	}

	protected ConsNode parseOptionalTemplateArgs() {
		if (getToken() != S_less)
			return null;
		consumeQ();
		ConsNode result = parseTemplateArgsBody();
		consumeQ(S_greater);
		return result;
	}

	public Node parseClassMember() {
		parseOptionalAnnotations(); // FIXME do something with it.
		if (hasProtection()) {
			parseProtection(); // FIXME do something with it.
		}
		if (hasClass()) {
			return parseClass();
		} else {
			TypeRefNode typeRef = parseTypeSpec(null);
			Symbol name;
			if (hasOpenParenthesis()) { // if name == class name, then this is the constructor and we DON'T need a return type.
				// What's more is that it looks like a return type, so workaround, workaround...
				name = (Symbol) typeRef.getTypeTemplate().getHead(); 
				typeRef = void_ref;
			} else {
				name = parse_ID();
			}
			if (hasOpenParenthesis()) {
				FunctionDeclNode fn = parseFunctionPrototype(typeRef, name);
				fn.setBody(parseFunctionBody());
				return fn;
			} else {
				Node result = parseVariableDeclaration(typeRef, name);
				consumeQ(S_semicolon);
				return result;
			}
		}
	}

	private VarDeclNode parseVariableDeclaration(TypeRefNode typeRef, Symbol name) {
		// TypeRefNode typeSpec = parseTypeSpec();
		// Symbol name = parse_ID();
		return new VarDeclNode(typeRef, name, parseOptionalInitializer());
	}

	private Node parseOptionalInitializer() {
		Node result = null;
		if (getToken() != S_semicolon) {
			consumeQ(S_equal);
			result = parse_expression();
		}
		// consumeQ(S_semicolon);
		return result;
	}

	protected ConsNode parseFunctionParameters() {
		consumeQ(S_openparenthesis);
		ConsNode result = parseFunctionParameterBody(false);
		consumeQ(S_closeparenthesis);
		return result;
	}

	protected ConsNode parseFunctionParameterBody(boolean expectComma) {
		if (getToken() == S_closeparenthesis)
			return null;
		else {
			if (expectComma)
				consumeQ(S_comma);
			TypeRefNode typeRef = parseTypeSpec(null);
			Symbol name = parse_ID(); // TODO optional
			return new ConsNode(new VarDeclNode(typeRef, name, null), parseFunctionParameterBody(true));
		}
	}

	private FunctionDeclNode parseFunctionPrototype(TypeRefNode typeRef, Symbol name) {
		ConsNode parameters = parseFunctionParameters();
		return new FunctionDeclNode(name, parameters, typeRef);
	}

	protected Node parseBlockOrStatement() {
		if (getToken() == S_opencurlybrace) {
			return parseBlock();
		} else
			return parseStatement();
	}

	protected BlockNode parseBlock() {
		// TODO Auto-generated method stub
		consumeQ(S_opencurlybrace);
		BlockNode result = new BlockNode(parseBlockBody());
		consumeQ(S_closecurlybrace);
		return result;
	}

	private ConsNode parseBlockBody() {
		if (getToken() == S_closecurlybrace)
			return null;
		else {
			return new ConsNode(parseStatement(), parseBlockBody(), true);
		}
	}

	protected BlockNode parseFunctionBody() {
		return parseBlock();
	}

	private boolean hasClass() {
		return getToken() == S_class;
	}

	private Symbol parse_ID() {
		if (getToken() != S_ID)
			raiseError("<ID>");
		String matchText = getInputData().getMatchText();
		consumeQ();
		return Symbol.intern(matchText);
	}

	private boolean hasOpenParenthesis() {
		return getToken() == S_openparenthesis;
	}

	public Node parseStatement() {
		if (hasReturn())
			return parseReturnStatement();
		else if (hasIf())
			return parseIfStatement();
		/*
		 * else if(getToken() == S_ID) { Symbol ID = parse_ID(); // workaround for detecting "Symbol result = foo;", i.e. variable declaration. return parse_ID_starting(ID, true); // FIXME this. ....
		 */
		else {
			Node result = parse_expression();
			consumeQ(S_semicolon);
			return result;
			// raiseError("<statement>");
		}
	}

	private ReturnNode parseReturnStatement() {
		assert (hasReturn());
		consumeQ();
		Node expression = parse_expression();
		consumeQ(S_semicolon);
		return new ReturnNode(expression);
	}

	private boolean hasReturn() {
		return getToken() == S_return;
	}

	private boolean hasIf() {
		return getToken() == S_if;
	}

	private Node parseIfStatement() {
		assert (hasIf());
		consumeQ(S_if);
		consumeQ(S_openparenthesis);
		Node condition = parse_expression();
		consumeQ(S_closeparenthesis);
		Node true_branch;
		Node false_branch;
		true_branch = parseBlockOrStatement();
		false_branch = parseBlockOrStatement();
		return simplify((Node) create_CallNode_with_operands(S_questionmark, condition, true_branch, false_branch, null));
	}

	ConsNode parse_fn_call_argument_list(boolean B_supports_type_as_argument) {
		if (getToken() != S_closeparenthesis) {
			if (B_supports_type_as_argument) {
				// return(ConsNode_new(null,
				// simplify((Node)(Parser_parse_typeref(null))), null));
				return (ConsNode) raiseError("<typeref-not-implemented>");
			} else
				return (parse_call_comma()); /* TODO do not throw stuff away. */
		} else /* no args */{
			return (null); /*
							 * return(ConsNode_new(null, Parser_resolve_symbol(S_void), null));
							 *//* FIXME */
		}
	}

	/* l -> r */

	// Node parse_negation() /* ! ~ */;
	Node parse_atom() {
		if (getToken() == S_openparenthesis) {
			Node result;
			consumeQ();
			result = parse_expression();
			consumeQ(S_closeparenthesis);
			result = simplify(result);
			if ((result instanceof TypeNode)) {
				/* FIXME handle cast. FIXME proper precedence order. */
				Node source_expression;
				source_expression = parse_negation();
				source_expression = simplify(source_expression);
				return (handle_cast((TypeNode) result, source_expression));
			}
			return (result);
		} else {
			if (getToken() == S_stringliteral)
				return (parseStringLiteral());
			return parse_ID_atom(parse_ID(), true);
		}
	}

	private Node parse_ID_atom(Symbol ID, boolean allowVariableDeclarations) {
		Node resolved_node = resolve_symbol(ID); // TODO more complicated var.
		// decls
		if (getToken() == S_ID) { // "String s"
			TypeRefNode typeRef = parseTypeSpec(ID);
			Symbol name = parse_ID();
			return parseVariableDeclaration(typeRef, name);
		} else if (resolved_node != null && resolved_node instanceof TypeRefNode) {
			TypeRefNode typeRef = (TypeRefNode) resolved_node; // parseTypeSpec()
			// ;
			Symbol name = parse_ID();
			return parseVariableDeclaration(typeRef, name);
		} else if (allowVariableDeclarations && built_in_type_name_part_P(ID) || ID == S_const || (resolved_node != null && resolved_node instanceof TypeRefNode))
			return (handle_caster(ID)); /* handle complicated cast expression */
		else
			return (resolve_symbol(ID));
	}

	private Node handle_caster(Symbol id) {
		// TODO Auto-generated method stub
		return null;
	}

	private Node handle_cast(TypeNode result, Node source_expression) {
		// TODO Auto-generated method stub
		return null;
	}

	private Node resolve_symbol(Symbol id) {
		// TODO Auto-generated method stub
		return new SymbolResolveNode(id, scope); // TODO scope.
	}

	private boolean built_in_type_name_part_P(Symbol id) {
		return id == S_int || id == S_float || id == S_double || id == S_void; /* FIXME others */
	}

	private Node parseStringLiteral() {
		// TODO Auto-generated method stub
		return null;
	}

	private Node CallNode_new_with_operandsv2(Node result, ConsNode argument_list) {
		return new CallNode(result, argument_list);
	}

	/* r -> l */

	Node parse_prefix_plusplusminusminus() {
		Node result;
		if (getToken() == S_plusplus || getToken() == S_dashdash) {
			Symbol token = consumeQ();
			result = simplify((Node) create_CallNode_with_operands(token, parse_prefix_plusplusminusminus(), /*
																											 * TODO just parse_postfix_plusplusminusminus would be OK , too
																											 */
			null));
		} else
			result = parse_level1();
		return (result);
	}

	Node parse_call(Node result) {
		Node decl = null;
		boolean B_supports_type_as_argument;
		if ((result instanceof VarDeclNode)) {
			TypeRefNode type_ref;
			VarDeclNode var_decl = (VarDeclNode) result;
			Node default_value;
			type_ref = var_decl.get_type();
			if (type_ref != null) {
				TypeNode decl2;
				decl2 = type_ref.try_resolve();
				if (decl2 != null)
					decl = (Node) decl2;
				else
					decl = (Node) type_ref;
			}
			if (((var_decl.get_storage_flags()) & VarDeclNode.STORAGE_CONST) != 0) {
				default_value = var_decl.get_default_value();
				if (default_value != null)
					result = default_value;
				else {
					/* FIXME extern decl */
				}
			} else
				; /* keep variable */
		} else {
			if (!(result instanceof FunctionDeclNode)) {
				// raiseError("<callable>", result.toString());
				// return(null);
				result = simplify((Node) CallNode_new_with_operandsv2(result, parse_fn_call_argument_list(false)));
				consumeQ(S_closeparenthesis);
				return (result);
			}
			decl = result;
		}
		B_supports_type_as_argument = ((FunctionDeclNode) decl).get_B_supports_type_as_argument();
		// consumeQ(S_openparenthesis);
		result = simplify((Node) CallNode_new_with_operandsv2(result, parse_fn_call_argument_list(B_supports_type_as_argument)));
		consumeQ(S_closeparenthesis);
		return result;
	}

	private Node parse_level1() {
		Node result;
		result = parse_atom();
		while (getToken() == S_openbracket || getToken() == S_dot || getToken() == S_plusplus || getToken() == S_dashdash || getToken() == S_openparenthesis) {
			Symbol token = consumeQ();
			result = (token == S_dot) ? new MemberAccessorNode(result, parse_ID()) : (token == S_plusplus || token == S_dashdash) ? simplify((Node) create_CallNode_with_operands(token, result, null)) : (token == S_openparenthesis) ? parse_call(result) : simplify((Node) create_CallNode_with_operands(token, result, parse_atom(), null));
			if (getToken() == S_openbracket)
				consumeQ(S_closebracket);
		}
		return (result);
	}

	Node parse_unary_plusminus() {
		Node result;
		if (getToken() == S_plus || getToken() == S_dash) {
			Symbol token = consumeQ();
			result = simplify((Node) create_CallNode_with_operands(token, parse_unary_plusminus(), null));
		} else
			result = parse_prefix_plusplusminusminus();
		return (result);
	}

	Node parse_negation() /* ! ~ */{
		Node result;
		if (getToken() == S_exclamation || getToken() == S_tilde) {
			Symbol token = consumeQ();
			result = simplify((Node) create_CallNode_with_operands(token, parse_negation(), null));
		} else
			result = parse_unary_plusminus();
		return (result);
	}

	private Node simplify(Node arg) {
		// TODO?
		return arg;
	}

	Node parse_castnew() {
		if (getToken() == S_new) {
			consumeQ();
			TypeRefNode typeRef = parseTypeSpec(null);
			return new NewNode(typeRef, parse_fn_call_argument_list_top(false));
		} else
			return (parse_negation()); /* FIXME support casts */
		/*
		 * if(getToken() == S_openparenthesis) { consumeQ(); Symbol ID; ID = parse_ID(); consumeQ(S_closeparenthesis); result = simplify((Node) create_CallNode_with_operands(S_openparenthesis, resolve_symbol(ID), parse_negation(), null)); } else result = parse_negation(); return(result);
		 */
	}

	private ConsNode parse_fn_call_argument_list_top(boolean b) {
		ConsNode result;
		consumeQ(S_openparenthesis);
		result = parse_fn_call_argument_list(b);
		consumeQ(S_closeparenthesis);
		return result;
	}

	Node parse_dereference() {
		Node result;
		/*
		 * Symbol op; int next_op; if(getToken() == S_star) { consumeQ(); result = simplify((Node) create_CallNode_with_operands(S_star, parse_dereference(), null)); } else
		 */
		result = parse_castnew();
		return (result);
	}

	Node parse_address_operator() {
		Node result;
		/*
		 * Symbol op; int next_op; if(op = getToken(), (op == '&' && !operator2_P(next_op))) { consumeQ(); result = simplify((Node) create_CallNode_with_operands(S_operatorampersand, parse_address_operator(), null)); } else
		 */
		result = parse_dereference();
		return (result);
	}

	Node parse_sizeof() { /* TODO actually do that? */
		return (parse_address_operator());
	}

	/* l -> r */

	Node parse_mulmod() {
		Node result;
		result = parse_sizeof();
		while (getToken() == S_star || getToken() == S_percent || getToken() == S_slash) {
			Symbol token = getToken();
			consumeQ();
			result = simplify((Node) create_CallNode_with_operands(token, result, parse_sizeof(), null));
		}
		return (result);
	}

	Node parse_addsub() {
		Node result;
		result = parse_mulmod();
		while (getToken() == S_plus || getToken() == S_dash) {
			Symbol token = getToken();
			consumeQ();
			result = simplify((Node) create_CallNode_with_operands(token, result, parse_mulmod(), null));
		}
		return (result);
	}

	Node parse_shift() /* << >> */{
		Node result;
		result = parse_addsub();
		while (getToken() == S_lessless || getToken() == S_greatergreater) {
			Symbol token = consumeQ();
			result = simplify((Node) create_CallNode_with_operands(token, result, parse_addsub(), null));
		}
		return (result);
	}

	Node parse_lessgreater() /* < <= > >= */{
		Node result;
		result = parse_shift();
		while (getToken() == S_less || getToken() == S_lessequal || getToken() == S_greater || getToken() == S_greaterequal || getToken() == S_instanceof) {
			Symbol token = consumeQ();
			result = simplify((Node) create_CallNode_with_operands(token, result, parse_shift(), null));
		}
		return (result);
	}

	Node parse_equalsne() /* == != */{
		Node result;
		result = parse_lessgreater();
		while (getToken() == S_equalequal || getToken() == S_exclamationequal) {
			Symbol token = consumeQ();
			result = simplify((Node) create_CallNode_with_operands(token, result, parse_lessgreater(), null));
		}
		return (result);
	}

	Node parse_bitwise_and() {
		Node result;
		result = parse_equalsne();
		while (getToken() == S_ampersand) {
			consumeQ();
			result = simplify((Node) create_CallNode_with_operands(S_ampersand, result, parse_equalsne(), null));
		}
		return (result);
	}

	private Node create_CallNode_with_operands(Symbol operator, Node... operands) {
		return new CallNode(resolve_symbol(operator), operands);
	}

	Node parse_bitwise_xor() {
		Node result;
		result = parse_bitwise_and();
		while (getToken() == S_circumflex) {
			consumeQ();
			result = simplify((Node) create_CallNode_with_operands(S_circumflex, result, parse_bitwise_and(), null));
		}
		return (result);
	}

	Node parse_bitwise_or() {
		Node result;
		result = parse_bitwise_xor();
		while (getToken() == S_pipe) {
			consumeQ();
			result = simplify((Node) create_CallNode_with_operands(S_pipe, result, parse_bitwise_xor(), null));
		}
		return (result);
	}

	Node parse_logical_and() {
		Node result;
		result = parse_bitwise_or();
		while (getToken() == S_ampersandampersand) {
			consumeQ();
			result = simplify((Node) create_CallNode_with_operands(S_ampersandampersand, result, parse_bitwise_or(), null));
		}
		return (result);
	}

	Node parse_logical_or() {
		Node result;
		result = parse_logical_and();
		while (getToken() == S_pipepipe) {
			consumeQ();
			result = simplify((Node) create_CallNode_with_operands(S_pipepipe, result, parse_logical_and(), null));
		}
		return (result);
	}

	/* r -> l */

	Node parse_ternary_conditional() {
		Node result;
		/* FIXME */
		result = parse_logical_or();
		if (getToken() == S_questionmark) {
			consumeQ();
			Node true_branch;
			Node false_branch;
			true_branch = parse_ternary_conditional(); /* FIXME */
			consumeQ(S_colon);
			false_branch = parse_ternary_conditional();
			result = simplify((Node) create_CallNode_with_operands(S_questionmark, result, true_branch, false_branch, null));
		}
		return (result);
	}

	Node parse_assignments() /* = += -= *= slash %= &= ^= |= <<= >>= */{
		Node result;
		result = parse_ternary_conditional(); /*
											 * TODO make sure this is NOT simplified, as we need to be able to assign to it.
											 */
		Symbol token = getToken();
		if (token == S_equal || token == S_plusequal || token == S_dashequal || token == S_starequal || token == S_slashequal || token == S_percentequal || token == S_circumflexequal || token == S_pipeequal || token == S_lesslessequal || token == S_greatergreaterequal || token == S_ampersandequal) {
			consumeQ();
			result = simplify((Node) create_CallNode_with_operands(token, result, parse_assignments(), null));
		}
		return (result);
	}

	Node parse_comma() { /* RIGHT associative */
		Node result;
		result = parse_assignments();
		if (getToken() == S_comma) {
			consumeQ();
			result = simplify((Node) create_CallNode_with_operands(S_comma, result, parse_comma(), null));
		}
		return (result);
	}

	ConsNode parse_call_comma() { /* RIGHT associative */
		Node result;
		ConsNode tail = null;
		result = parse_assignments();
		assert (result != null);
		if (getToken() == S_comma) {
			consumeQ();
			tail = parse_call_comma();
		}
		return (new ConsNode(result, tail));
	}

	Node parse_expression() {
		return (parse_comma());
	}

	ConsNode parseOptionalAnnotations() {
		return hasAnnotation() ? parseAnnotations() : null;
	}

	ConsNode parseAnnotations() {
		AnnotationNode head = parseAnnotation();
		return new ConsNode(head, hasAnnotation() ? parseAnnotations() : null);
	}

	private boolean hasAnnotation() {
		return getToken() == S_at;
	}

	AnnotationNode parseAnnotation() {
		return parseNormalAnnotation();
		// MarkerAnnotation
		// SingleElementAnnotation
	}

	Symbol parseTypeName() {
		return parse_ID();
	}

	AnnotationNode parseNormalAnnotation() {
		consumeQ(S_at);
		Symbol name = parseTypeName(); // FIXME Store
		ConsNode arguments = null;
		if (getToken() == S_openparenthesis) {
			consumeQ(S_openparenthesis);
			arguments = parseElementValuePairs(); // FIXME optional, store.
			consumeQ(S_closeparenthesis);
		} else
			arguments = null;
		return new AnnotationNode(name, arguments);
	}

	ConsNode parseElementValuePairs() {
		Node head = parseElementValuePair();
		return new ConsNode(head, getToken() == S_comma ? parseElementValuePairs() : null);
	}

	ConsNode parseElementValuePair() {
		Symbol head = parse_ID();
		consumeQ(S_equal);
		return new ConsNode(head, new ConsNode(parseElementValue(), null));
	}

	Node parseElementValue() {
		return parseAnnotation();
		// FIXME ConditionalExpression |
		/*
		 * Annotation | ElementValueArrayInitializer
		 */
	}

	Node parseElementValueArrayInitializer() {
		Node result;
		consumeQ(S_opencurlybrace);
		result = parseElementValue(); /* FIXME , opt */
		consumeQ(S_closecurlybrace);
		return result;
	}

	ConsNode parseElementValues() {
		Node head = parseElementValue();
		return new ConsNode(head, getToken() == S_comma ? parseElementValues() : null);
	}
}
