package hu.swankey.ammo.common.script;

import hu.swankey.ammo.common.script.statements.Statement;
import hu.swankey.ammo.common.yggdrasil.definition.ComplexClassDefinition;
import hu.swankey.ammo.common.yggdrasil.definition.YMethodDefinition;
import hu.swankey.ammo.common.yggdrasil.definition.AttributeDefinition;

import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;

public class Synthetizer {

//	private static final int CLASS_ACCESSLEVEL_INDEX = 0;
//	private static final int CLASS_IDENTIFIER_INDEX = 1;
//	private static final int CLASS_SUPERIORS_INDEX = 2;
//	private static final int CLASS_DEFINITIONS_INDEX = 3;
//
//	private static final int VAR_ACCESSLEVEL_INDEX = 0;
//	private static final int VAR_TYPE_INDEX = 1;
//	private static final int VAR_IDENTIFIER_INDEX = 2;
//	private static final int VAR_DEFAULTVALUE_INDEX = 3;
//
//	
//	private static final int METHOD_ACCESSLEVEL_INDEX = 0;
//	private static final int METHOD_TYPE_INDEX = 1;
//	private static final int METHOD_IDENTIFIER_INDEX = 2;
//	private static final int METHOD_BLOCK_INDEX = 3;
//	
//	private static final int PARAMETER_ID_INDEX = 0;
//	private static final int PARAMETER_VALUE_INDEX = 1;
//	
//	private static final String VOID = "void";
//	
//	private static final String FUNCTION_NAME_BLOCK = "block";
//	private static final String CALL_NAME = "Generated Call";
//	private static final String SOFTLINK_NAME = "Generated SoftLink";
	
	
//	public static DynamicCall createCall(String code) throws AmmoScriptException{
//		
//		AmmoScriptLexer lex = new AmmoScriptLexer(new ANTLRStringStream(code));		
//    	CommonTokenStream tokens = new CommonTokenStream(lex);
//    	AmmoScriptParser parser = new AmmoScriptParser(tokens);
//    	AmmoScriptParser.call_return ret;
//    	
//		try {
//			ret = parser.call();
//			
//			if (parser.getErrors().size() > 0)
//				throw new AmmoScriptSyntaxException(code, parser.getErrors());
//							
//	        return ret.call;
//		} catch (RecognitionException e) {
//			throw new RuntimeException(e);
//		}	
//	}
	
	
	public static Statement createStatement(String code) throws AmmoScriptException{
		
		AmmoScriptLexer lex = new AmmoScriptLexer(new ANTLRStringStream(code));		
    	CommonTokenStream tokens = new CommonTokenStream(lex);
    	AmmoScriptParser parser = new AmmoScriptParser(tokens);
    	AmmoScriptParser.statement_return ret;
    	
		try {
			ret = parser.statement();
			
			if (parser.getErrors().size() > 0)
				throw new AmmoScriptSyntaxException(code, parser.getErrors());
							
	        return ret.statement;
		} catch (RecognitionException e) {
			throw new RuntimeException(e);
		}	
	}	
	
//	public static CustomClassDefinition createCustomClassDefinition(String code) throws AmmoScriptException{
//		
//		AmmoScriptLexer lex = new AmmoScriptLexer(new ANTLRStringStream(code));		
//    	CommonTokenStream tokens = new CommonTokenStream(lex);
//    	AmmoScriptParser parser = new AmmoScriptParser(tokens);
//    	AmmoScriptParser.classDeclaration_return ret;    	
//    	
//		try {
//			ret = parser.classDeclaration();
//			
//			if (parser.getErrors().size() > 0)
//				throw new AmmoScriptSyntaxException(parser.getErrors());
//							
//	        return createClassDefinition( (CommonTree) ret.getTree(), tokens );
//		} catch (RecognitionException e) {
//			throw new RuntimeException(e);
//		}	
//	}
	
	public static ComplexClassDefinition createCustomClassDefinition(String code) throws AmmoScriptException {
		AmmoScriptLexer lex = new AmmoScriptLexer(new ANTLRStringStream(code));	
    	CommonTokenStream tokens = new CommonTokenStream(lex);
    	AmmoScriptParser parser = new AmmoScriptParser(tokens);
    	AmmoScriptParser.classDeclaration_return ret;
    	
		try {
			ret = parser.classDeclaration();
			
			if (parser.getErrors().size() > 0)
				throw new AmmoScriptSyntaxException(code, parser.getErrors());
			
			return ret.def;
		} catch (RecognitionException e) {
			throw new RuntimeException(e);
		}	
	}
	
	
	public static YMethodDefinition createMethodDefinition(String code) throws AmmoScriptException{
		
		AmmoScriptLexer lex = new AmmoScriptLexer(new ANTLRStringStream(code));		
    	CommonTokenStream tokens = new CommonTokenStream(lex);
    	AmmoScriptParser parser = new AmmoScriptParser(tokens);
    	AmmoScriptParser.methodDeclaration_return ret;    	
    	
		try {
			ret = parser.methodDeclaration();
			
			if (parser.getErrors().size() > 0)
				throw new AmmoScriptSyntaxException(code, parser.getErrors());
							
			return ret.def;
		} catch (RecognitionException e) {
			throw new RuntimeException(e);
		}	
	}
	
	public static AttributeDefinition createVariableDefinition(String code) throws AmmoScriptException{
		
		AmmoScriptLexer lex = new AmmoScriptLexer(new ANTLRStringStream(code));		
    	CommonTokenStream tokens = new CommonTokenStream(lex);
    	AmmoScriptParser parser = new AmmoScriptParser(tokens);
    	AmmoScriptParser.variableDeclaration_return ret;    	
    	
		try {
			ret = parser.variableDeclaration();
			
			if (parser.getErrors().size() > 0)
				throw new AmmoScriptSyntaxException(code, parser.getErrors());
							
			return ret.def;
		} catch (RecognitionException e) {
			throw new RuntimeException(e);
		}	
	}	
	
//	public static Call createCall(String code) throws AmmoScriptException{
//		
//		AmmoScriptLexer lex = new AmmoScriptLexer(new ANTLRStringStream(code));		
//    	CommonTokenStream tokens = new CommonTokenStream(lex);
//    	AmmoScriptParser parser = new AmmoScriptParser(tokens);
//    	AmmoScriptParser.call_return ret;    	
//    	
//		try {
//			ret = parser.call();
//			
//			if (parser.getErrors().size() > 0)
//				throw new AmmoScriptSyntaxException(code, parser.getErrors());
//							
//	        //return createMethodDefinition( (CommonTree) ret.getTree(), tokens );
//			return ret.call;
//		} catch (RecognitionException e) {
//			throw new RuntimeException(e);
//		}	
//	}
	
	
//	private static Element createElement(Tree t, CommonTokenStream stream){
//		
//		if ( t.getType() == AmmoScriptParser.CLASSDEF ||
//			 t.getType() == AmmoScriptParser.VARDEF ||
//			 t.getType() == AmmoScriptParser.METHODDEF)
//			return createDefinition(t, stream);
//		
//		if ( t.getType() == AmmoScriptParser.IDENTIFIER)
//			return createCall(t, stream);
//		
//		if ( t.getType() == AmmoScriptParser.CALL )
//			return createCall(t, stream);
//		
//		if ( t.getType() == AmmoScriptParser.SWITCHCASE )
//			throw new RuntimeException(":)");
//		
//		throw new RuntimeException("Unknown element");
//	}


//	private static Call createCall(Tree t, CommonTokenStream stream) {
//		
//		System.out.println("Generate Call from: " + getCodeBack(t, stream));
//		
//		SoftLink target = createSoftLink(t.getChild(0), stream);
//		
//		HashMap<String, Element> params = new HashMap<String, Element>();
//		
//
//		for (int i = 1; i < t.getChildCount(); i++) {
//			Tree parameter = t.getChild(i);
//			
//			Tree t_id = parameter.getChild(PARAMETER_ID_INDEX);
//			Tree t_value = parameter.getChild(PARAMETER_VALUE_INDEX);
//			
//			String param_name = createSoftLink(t_id, stream).getValue();
//			Element param_value = createElement(t_value, stream);
//			
//			params.put(param_name, param_value);
//		}
//
//		return new Call("GENERATED CALL", target, params);
//	}
	
	
	// TODO: Szebb lenne, ha a switch-et már az antlr átalakítaná függvényhívássá
//	private static Call createSwitchCall(Tree t, CommonTokenStream stream) {
//		
//		System.out.println("Generate Switch from: " + getCodeBack(t, stream));
//		
//		SoftLink target = new SoftLink("switch","switch");
//		SoftLink var = createSoftLink(t.getChild(1), stream);
//		HashMap<String, Element> params = new HashMap<String, Element>();
//
//		for (int i = 1; i < t.getChildCount(); i++) {
//			Tree parameter = t.getChild(i);
//			
//			Tree t_id = parameter.getChild(PARAMETER_ID_INDEX);
//			Tree t_value = parameter.getChild(PARAMETER_VALUE_INDEX);
//			
//			String param_name = createSoftLink(t_id, stream).getValue();
//			Element param_value = createElement(t_value, stream);
//			
//			params.put(param_name, param_value);
//		}
//
//		return new Call("GENERATED CALL", target, params);
//	}

	
	
//	private static ElementDefinition createDefinition(Tree t, CommonTokenStream stream) {
//		
//		if (t.getType() == AmmoScriptParser.VARDEF)
//			return createVariableDefinition(t, stream);
//		
//		if (t.getType() == AmmoScriptParser.CLASSDEF)
//			return createClassDefinition(t, stream);
//		
//		if (t.getType() == AmmoScriptParser.METHODDEF)
//			return createMethodDefinition(t, stream);
//		
//		else
//			return createMethodDefinition(t, stream);
//	}

	
//	private static CustomClassDefinition createClassDefinition(Tree t, CommonTokenStream stream) {
//
//		System.out.println("ClassDefinition: " + getCodeBack(t, stream));
//		
//		Tree t_accesslevel = t.getChild(CLASS_ACCESSLEVEL_INDEX);
//		Tree t_name = t.getChild(CLASS_IDENTIFIER_INDEX);
//		Tree t_superiors = t.getChild(CLASS_SUPERIORS_INDEX);
//		Tree t_definitions = t.getChild(CLASS_DEFINITIONS_INDEX);
//
//		String name = createSoftLink(t_name, stream).getValue();
//
//		CustomClassDefinition classDef = new CustomClassDefinition(name);
//		
//		// AccessLevel:
//		classDef.setAccessLevel( getAccessLevel(t_accesslevel) );
//
//		// Superiors:
//		for (int i = 0; i < t_superiors.getChildCount(); i++)
//			classDef.addSuperior(createSoftLink(t_superiors.getChild(i), stream));
//
//		// Variables and methods:
//		for (int i = 0; i < t_definitions.getChildCount(); i++) {
//			ElementDefinition definition = createDefinition(t_definitions.getChild(i), stream);
//
//			if (definition instanceof VariableDefinition)
//				classDef.addVariable((VariableDefinition) definition);
//			else
//				classDef.addMethod((MethodDefinition) definition);
//
//		}
//
//		return classDef;
//	}
	
	
//	private static VariableDefinition createVariableDefinition(Tree t, CommonTokenStream stream) {
//		
//		Tree t_accesslevel = t.getChild(VAR_ACCESSLEVEL_INDEX);
//		Tree t_type = t.getChild(VAR_TYPE_INDEX);
//		Tree t_name = t.getChild(VAR_IDENTIFIER_INDEX);
//		Tree t_defaultvalue = t.getChild(VAR_DEFAULTVALUE_INDEX);
//
//		String name = createSoftLink(t_name, stream).getValue();
//
//		VariableDefinition varDef = new VariableDefinition(name);
//		varDef.setElementAccessLevel( getAccessLevel(t_accesslevel) );
//		
//		//varDef.setVarType( (Type) types.getElement( createSoftLink(t_type).getValue() ) );
//		varDef.setVarType( createSoftLink(t_type, stream) );
//		
//		if (varDef.getVarType() == null)
//			throw new RuntimeException("Class not found: " + createSoftLink(t_type, stream).getValue() );
//		
//		if (t_defaultvalue != null)
//			varDef.setDefaultValue( createElement(t_defaultvalue, stream) );
//		
//		return varDef;
//	}

	
	
//	private static MethodDefinition createMethodDefinition(Tree t, CommonTokenStream stream) {
//		
//		Tree t_accesslevel = t.getChild(METHOD_ACCESSLEVEL_INDEX);
//		Tree t_type = t.getChild(METHOD_TYPE_INDEX);
//		Tree t_name = t.getChild(METHOD_IDENTIFIER_INDEX);
//		Tree t_block = t.getChild(METHOD_BLOCK_INDEX);
//
//		int accessLevel = getAccessLevel(t_accesslevel);
//		SoftLink type = createSoftLink(t_type, stream);
//		String name = createSoftLink(t_name, stream).getValue();
//		Call call = createBlock(t_block, stream);
//		
//		call.setName(name);
//		MethodDefinition def = new MethodDefinition(call);		
//		def.setElementAccessLevel( accessLevel );
//				
//		if (!type.getValue().equals(VOID))
//			def.setReturnType( type );
//		
//		
////		StringBuffer code = new StringBuffer("");
////    	for (Object token: stream.getTokens(t.getTokenStartIndex(), t.getTokenStopIndex())){
////    		code.append( ((Token)token).getText() );
////    	}
//    	def.setSource(getCodeBack(t, stream));
//		
//    	
//		return def;
//	}

//	private static int getAccessLevel(Tree t) {
//		
//		switch (t.getType()) {
//		case AmmoScriptParser.KEYWORD_PRIVATE:
//			return Element.ACCESS_LEVEL_PRIVATE;
//		case AmmoScriptParser.KEYWORD_PROTECTED:
//			return Element.ACCESS_LEVEL_PROTECTED;
//		case AmmoScriptParser.KEYWORD_PUBLIC:
//			return Element.ACCESS_LEVEL_PUBLIC;
//		default:
//			throw new RuntimeException("Unknown access-level.");
//		}
//	}
	
//	private static Call createBlock(Tree t, CommonTokenStream stream){
//		
//		t = t.getChild(0);
//		
//		HashMap<String, Element> parameters = new HashMap<String, Element>();
//		for (int i = 1; i < t.getChildCount(); i++) {
//			Element parameter = createElement(t, stream);
//			parameters.put(parameter.getName(), parameter);
//		}
//		
//		return new Call(CALL_NAME, new SoftLink(SOFTLINK_NAME, FUNCTION_NAME_BLOCK), parameters);
//	}
	
	
//	private static Field createField(Tree t){
//		
//		switch (t.getType()) {
//			case AmmoScriptParser.STRING:
//				return createStringField(t);
//			case AmmoScriptParser.NUMBER:
//				return createIntegerField(t);
//			default:
//				throw new RuntimeException("Unknown field");
//		}
//	}
	

//	private static IntegerField createIntegerField(Tree t) {
//		return new IntegerField("GENERATED INTEGER", Integer.parseInt(t.getText()));
//	}

//	private static SoftLink createSoftLink(Tree t, CommonTokenStream stream) {
//		
//		
//		//System.out.println("Generate SoftLink from: " + getCodeBack(t, stream));
//		
//		String[] path = new String[t.getChildCount()];
//		for (int i = 0; i < t.getChildCount(); i++) {
//			path[i] = ((CommonTree)t.getChild(i)).getToken().getText();
//		}
//		
//		return new SoftLink(path[path.length-1], path);
//	}

//	private static StringField createStringField(Tree t) {
//		String str = t.getText().substring(1, t.getText().length() - 1);
//		return new StringField("GENERATED STRING", str);
//	}
	
	// TODO: Biztos van erre valami frappánsabb megoldás...
//	private static String getCodeBack(Tree t, CommonTokenStream stream){
//		StringBuffer code = new StringBuffer("");
//		List tokens = stream.getTokens(t.getTokenStartIndex(), t.getTokenStopIndex());
//		
//    	for (Object token: tokens){
//    		code.append( ((Token)token).getText() );
//    	}
//		
//    	return code.toString();
//	}
}
