package org.codeworker;

@

function getJavaType(myType : node) {
	switch(myType) {
		case "base":
			switch(myType.name) {
				case "void":
					if myType.pointers {
						if myType.pointers == 1 return "java.lang.Object";
						return "java.lang.Object[]";
					}
					return "void";
				case "int":
				case "double":
				case "long":
					if myType.pointers error(myType.name + "* not handled yet!");
					if myType.is_unsigned return "unsigned " + myType.name;
					return myType.name;
				case "bool":
					if myType.pointers error("bool* not handled yet!");
					return "boolean";
				case "char":
					if myType.pointers {
						if myType.pointers == 1 return "byte[]";
						error("char** or more not handled yet!");
					}
					if myType.is_unsigned return "byte";
					return "char";
			}
			break;
		case "stl":
			switch(myType.name) {
				case "string":
					return "String";
				case "map":
					return "java.util.Map<" + getJavaType(myType.templates#front) + "," + getJavaType(myType.templates#back) + ">";
				case "map::const_iterator":
				case "map::iterator":
					return "java.util.MapIterator<" + getJavaType(myType.templates#front) + "," + getJavaType(myType.templates#back) + ">";
				case "list":
					return "java.util.List<" + getJavaType(myType.templates#front) + ">";
				case "list::const_iterator":
				case "list::iterator":
					return "java.util.ListIterator<" + getJavaType(myType.templates#front) + ">";
				case "set":
					return "java.util.Set<" + getJavaType(myType.templates#front) + ">";
				case "ifstream":
					return "java.io.FileInputStream";
				case "string::size_type":
					return "int";
			}
			break;
		case "indirect":
			if myType.indirect == "struct" {
				if myType.indirect.system {
					if myType.indirect.name == "FILE" return "java.io.InputStream";
					traceObject(myType.indirect);
					error("indirect struct type not handled yet!");
				}
				return myType.indirect.name;
			} else if myType.indirect == "class" {
				return "org.codeworker." + myType.indirect.name;
			} else if myType.indirect == "typedef" {
				if myType.indirect.name == "END_STREAM_CALLBACK" return "org.codeworker.END_STREAM_CALLBACK";
				traceObject(myType.indirect);
				error("indirect typedef not handled yet!");
			} else {
				traceObject(myType.indirect);
				error("indirect type '" + myType.indirect + "' not handled yet!");
			}
	}
}

function getJavaExpression(theExpr : node) {
	switch(theExpr) {
		case "null": return "null";
		case "bool":
		case "double":
			return theExpr.value;
		case "char":
			if !theExpr.value return "'\\0'";
			return "'" + theExpr.value.composeCLikeString() + "'";
		case "string":
			return '"' + theExpr.value.composeCLikeString() + '"';
		case "&ref":
			return getJavaExpression(theExpr.reference);
		case "var":
			local sExpr = theExpr.name;
			if theExpr.next {
				sExpr += '.' + getJavaExpression(theExpr.next);
			}
			return sExpr;
		case "star":
			return getJavaExpression(theExpr.star);
		case "const_cast":
			return getJavaExpression(theExpr.expr);
		case "npos":
			return "-1/*npos*/";
		case "this":
			return "this";
		case "cast":
			return "((" + getJavaType(theExpr.type) + ") " + getJavaExpression(theExpr.cast) + ')';
		case "new":
			local sExpr = "new " + getJavaType(theExpr.type);
			if theExpr.array.existVariable() {
				sExpr += '[' + getJavaExpression(theExpr.array) + ']';
			}
			return sExpr;
		case "()":
			local sExpr = theExpr.function.name + '(';
			foreach i in theExpr.params {
				if !i.first() sExpr += ", ";
				sExpr += getJavaExpression(i);
			}
			return sExpr + ')';
		case "(*":
			local sExpr = theExpr.function + ".callback(";
			foreach i in theExpr.params {
				if !i.first() sExpr += ", ";
				sExpr += getJavaExpression(i);
			}
			return sExpr + ')';
		case "++":
		case "--":
			return theExpr + getJavaExpression(theExpr.expr);
		case "post++":
		case "post--":
			return getJavaExpression(theExpr.expr) + theExpr;
		case '!':
			return '!' + getJavaExpression(theExpr.expr);
		case "?":
			return "((" + getJavaExpression(theExpr.condition) + ") ? " + getJavaExpression(theExpr.then) + " : " + getJavaExpression(theExpr.else) + ')';
		case '+':
		case '-':
		case '*':
		case '/':
		case '&':
		case '|':
		case "!=":
		case "==":
		case "<=":
		case ">=":
		case ">":
		case "<":
		case "&&":
		case "||":
			return "(" + getJavaExpression(theExpr.left) + " " + theExpr + " " + getJavaExpression(theExpr.right) + ')';
		case "substring":
			return "org.codeworker.Utility.substring(" + getJavaExpression(theExpr.char_array) + ", " + getJavaExpression(theExpr.length) + ')';
		default:
			traceObject(theExpr);
			error("expression '" + theExpr + "' not handled yet!");
	}
}

function generateJavaInstruction<T>(theInstr : node) {
	traceObject(theInstr);
	error("instruction '" + T + "' not handled yet!");
}

function generateJavaInstructions(block : node) {
	foreach i in block {
		generateJavaInstruction<i>(i);
	}
}

function generateJavaInstruction<"{}">(theInstr : node) {
	@		{
@
	incrementIndentLevel();
	generateJavaInstructions(theInstr);
	decrementIndentLevel();
	@		}
@
}

function generateJavaInstruction<"return">(theInstr : node) {
	@		return@
	if theInstr.expr.existVariable() {
		@ @getJavaExpression(theInstr.expr)@@
	}
	@;
@
}

function generateJavaInstruction<"break">(theInstr : node) {
	@		break;
@
}

function generateJavaInstruction<"continue">(theInstr : node) {
	@		continue;
@
}

function generateJavaInstruction<"=">(theInstr : node) {
	@		@getJavaExpression(theInstr.variable)@ = @getJavaExpression(theInstr.expr)@;
@
}

function generateJavaInstruction<"*=">(theInstr : node) {
	@		@getJavaExpression(theInstr.variable)@ *= @getJavaExpression(theInstr.expr)@;
@
}

function generateJavaInstruction<"+=">(theInstr : node) {
	@		@getJavaExpression(theInstr.variable)@ += @getJavaExpression(theInstr.expr)@;
@
}

function generateJavaInstruction<"-=">(theInstr : node) {
	@		@getJavaExpression(theInstr.variable)@ -= @getJavaExpression(theInstr.expr)@;
@
}

function generateJavaInstruction<"++">(theInstr : node) {
	@		++@getJavaExpression(theInstr.variable)@;
@
}

function generateJavaInstruction<"--">(theInstr : node) {
	@		--@getJavaExpression(theInstr.variable)@;
@
}

function generateJavaInstruction<"declaration">(theInstr : node) {
	@		@getJavaType(theInstr.variable.type)@ @theInstr.variable.name@@
	if theInstr.init.existVariable() {
		@ = @getJavaExpression(theInstr.init)@@
	} else if theInstr.constructor.existVariable() {
		@(@
		foreach i in theInstr.constructor {
			if !i.first() {
				@, @
				@@getJavaExpression(i)@@
			}
		}
		@)@
	}
	@;
@
}

function generateJavaInstruction<"if">(theInstr : node) {
	@		if (@getJavaExpression(theInstr.condition)@)
@
	incrementIndentLevel();
	generateJavaInstructions(theInstr.then);
	decrementIndentLevel();
	if theInstr.else.existVariable() {
		@		else
@
		incrementIndentLevel();
		generateJavaInstructions(theInstr.else);
		decrementIndentLevel();
	}
}

function generateJavaInstruction<"for">(theInstr : node) {
	@		for (@
	if theInstr.init.existVariable() {
		if theInstr.init == "declaration" {
			@@getJavaType(theInstr.init.type)@@
		}
		@ @theInstr.init.index@ = @getJavaExpression(theInstr.init.expr)@;@getJavaExpression(theInstr.condition)@;@getJavaExpression(theInstr.next)@@
	} else {
		@;;@
	}
	@)
@
	incrementIndentLevel();
	generateJavaInstructions(theInstr.body);
	decrementIndentLevel();
}

function generateJavaInstruction<"while">(theInstr : node) {
	@		while (@getJavaExpression(theInstr.condition)@)
@
	incrementIndentLevel();
	generateJavaInstructions(theInstr.body);
	decrementIndentLevel();
}

function generateJavaInstruction<"do">(theInstr : node) {
	@		do
@
	incrementIndentLevel();
	generateJavaInstructions(theInstr.body);
	decrementIndentLevel();
	@		while (@getJavaExpression(theInstr.condition)@);
@
}

function generateJavaInstruction<"switch">(theInstr : node) {
	@		switch(@getJavaExpression(theInstr.expr)@) {
@
	incrementIndentLevel();
	foreach i in theInstr.cases {
		@		case @getJavaExpression(i.constant)@:
@
		incrementIndentLevel();
		generateJavaInstructions(i.body);
		decrementIndentLevel();
	}
	if theInstr.default.existVariable() {
		@		default:
@
		incrementIndentLevel();
		generateJavaInstructions(theInstr.default);
		decrementIndentLevel();
	}
	decrementIndentLevel();
	@		}
@
}

function generateJavaInstruction<"()">(theInstr : node) {
	@		@theInstr.function.name@(@
	foreach i in theInstr.params {
		if !i.first() {
			@, @
		}
		@@getJavaExpression(i)@@
	}
	@);
@
}

function generateJavaInstruction<"*()">(theInstr : node) {
	@		@theInstr.function@.callback(@
	foreach i in theInstr.params {
		if !i.first() {
			@, @
		}
		@@getJavaExpression(i)@@
	}
	@);
@
}

function generateJavaInstruction<"operator<<()">(theInstr : node) {
	@		@getJavaExpression(theInstr.instance)@.writeOperator(@
	foreach i in theInstr.params {
		if !i.first() {
			@, @
		}
		@@getJavaExpression(i)@@
	}
	@);
@
}

function generateJavaInstruction<"delete">(theInstr : node) {
	@		@getJavaExpression(theInstr.variable)@ = null; // delete the object!
@
}

function generateJavaInstruction<"throw">(theInstr : node) {
	@		throw new org.codeworker.@theInstr.type@(@getJavaExpression(theInstr.message)@);
@
}

function generateClass(theClass : node) {
	foreach i in theClass.structs {
		generateClass(i);
	}
	@
@
	if theClass.name == this.name {
		@public @
	}
	@class @theClass.name@ {
@

	foreach i in theClass.attributes {
		@	@i.access@ @
		if i.modifiers.findElement("static") {@static @}
		@@getJavaType(i.type)@ @i.name@@
		if i.default {
			@ = @getJavaExpression(i.default)@@
		}
		@;
@
	}
	@
@
	foreach i in theClass.methods {
		foreach method in i {
			if method.name.startString('~') {
				// destructor
				if method.body.existVariable() {
					// not empty
					@	protected void finalize() {
	}
@
				}
			} else {
				local sMethodName = method.name;
				if startString(i.key(), "operator") {
					switch(subString(i.key(), 8)) {
						case "<<":
							sMethodName = "writeOperator";
							break;
					}
				}
				@	@method.access@ @
				if method.modifiers.findElement("static") {@static @}
				if method.name != theClass.name {
					// not a constructor: return type
					@@getJavaType(method.return_type)@ @
				}
				@@sMethodName@(@
				foreach parameter in method.params {
					if !parameter.first() {
						@, @
					}
					@@getJavaType(parameter.type)@ @parameter.name@@
				}
			@) {
@
				if method.name == theClass.name {
					// a constructor!
					if method.initializers.super.existVariable() {
						@		super(@
						foreach j in method.initializers.super {
							if !j.first() {
								@, @
								@@getJavaExpression(j)@@
							}
						}
						@);
@
					}
					foreach j in method.initializers {
						if j.size() != 1 {
							error("cannot initialize a class yet!");
						} else if j#front != "null" {
							@		@j.key()@ = @getJavaExpression(j#front)@;
@
						}
					}
				}
				if theClass.parse_body {
					generateJavaInstructions(method.body);
				}
				@	}

@
			}
		}
	}

	@}
@
}

generateClass(this);
