package de.dhbwstuttgart.strucTypes5.ausgabe;

import de.dhbwstuttgart.strucTypes5.typeVars.TypeVarStore;
import de.dhbwstuttgart.syntaxtree.ClassOrInterface;
import de.dhbwstuttgart.syntaxtree.Field;
import de.dhbwstuttgart.syntaxtree.FormalParameter;
import de.dhbwstuttgart.syntaxtree.Method;
import de.dhbwstuttgart.syntaxtree.statement.*;
import de.dhbwstuttgart.syntaxtree.type.RefType;
import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;

import java.sql.Ref;
import java.util.List;

/**
 * Created by sebastian on 20.04.17.
 */
public class Class2String {



    // Select Mode
    public static final Integer WITH_BLANK = 1;
    public static final Integer WITH_TPH = 2;
    public static final Integer WITH_TYPEVARS = 3;

    //
    private Integer aktMode;

    private TypeVarStore typeVarStore;



    public String generateStringBlank(ClassOrInterface cl) {
        aktMode = WITH_BLANK;
        return generateString(cl);
    }



    public String generateStringTPH(ClassOrInterface cl) {
        aktMode = WITH_TPH;
        return generateString(cl);
    }



    public String generateStringTypeVars(ClassOrInterface cl , TypeVarStore typeVarStore) {
        aktMode = WITH_TYPEVARS;
        this.typeVarStore = typeVarStore;
        return generateString(cl);
    }



    private String generateString(ClassOrInterface cl) {
        String name = cl.getClassName().toString();
        String fieldsString = generateFieldsString(cl.getFieldDecl());
        String methodsString = generateMethodsString(cl.getMethods());
        return String.format( "class %s { \n %s  \n %s  }" , name, fieldsString, methodsString);
    }


    private String generateFieldsString(List<Field> fieldList) {
        String fieldString = "";
        for (Field f : fieldList ) {
            fieldString = fieldString.concat(generateFieldString(f) + "\n" );
        }
        return fieldString;
    }

    private String generateFieldString(Field f) {
        return String.format("%s : %s; " , f.getName() , tS(f.getType())  );
    }


    private String generateMethodsString(List<Method> methods) {
        String resultMethodsString = "";
        for (Method m : methods)  {
            resultMethodsString = resultMethodsString.concat(generateMethodString(m) + "\n");
        }
        return resultMethodsString;
    }


    private String generateMethodString(Method m) {
        String mName = m.getName();
        String argsString = generateFormalParameters(m.getParameterList().getFormalparalist());
        String returnString =  generateReturnString(m.get_Block().getStatements());
        //String returnString = "";
        return String.format( "%s  %s ( %s ) { return  %s  } " , tS(m.getType()) , mName , argsString , returnString);
    }


    private String generateFormalParameters(List<FormalParameter> formalParameterList) {
        String result = "";
        for (FormalParameter formalParameter : formalParameterList) {
           result =  result.concat(generateFormalParameter(formalParameter));
        }
        return result;
    }

    private String generateFormalParameter(FormalParameter fp) {
        return String.format(" %s : %s  " , fp.getName() , tS(fp.getType()));
    }


    // Erstellt String für Return Statement
    private String generateReturnString(List<Statement> statements) {

        if (statements.get(0) instanceof Return) {
            Return returnStatement = (Return) statements.get(0);
            Expression expression = returnStatement.get_Expression();
            return generateExpressionString(expression);
        }
        else {
            return "Fehler ???";
        }
    }


    // Erstellt rekursiv Strings für die auftauchenden Expressions
    private String generateExpressionString(Expression expression) {

        if (expression instanceof LocalVar) {
            LocalVar localVar = (LocalVar) expression;
            return String.format(  " [%s : %s ] " ,  localVar.get_expression() , tS(localVar.getType()) );
        }
        else if (expression instanceof FieldVar ) {
            FieldVar fieldVar = (FieldVar) expression;
            String receiverString = generateExpressionString(fieldVar.getReceiver());
            return String.format(" [%s.%s : %s  ]  " , receiverString ,  fieldVar.getFieldVarName() , tS(fieldVar.getType())   );
        }
        else if (expression instanceof  MethodCall) {
            MethodCall methodCall = (MethodCall) expression;
            String receiverString = generateExpressionString(methodCall.get_Receiver());

            String arguments = "";
            for (Expression arg : methodCall.get_ArgList().getArguments()) {
                String expArg = generateExpressionString(arg);
                arguments = arguments.concat(expArg + ",");
            }
            return String.format( "[%s.%s(%s) : %s ]  " , receiverString, methodCall.get_Name() , arguments , tS(methodCall.getType()));
        }
        else if (expression instanceof Receiver){
            Receiver receiver = (Receiver) expression;
            return generateExpressionString(receiver.get_Expression());
        }
        else if (expression instanceof NewClass) {
            NewClass newClass = (NewClass) expression;

            String arguments = "";
            for (Expression arg : newClass.getArgumentList().getArguments()) {
                String expArg = generateExpressionString(arg);
                arguments = arguments.concat(expArg + ",");
            }
            return String.format("[new %s( %s ): %s ]" , newClass.getType() , arguments , newClass.getType() );
        }
        else {
            return "Expression nicht bekannt";
        }
    }



    private String tS(RefTypeOrTPHOrWildcardOrGeneric ref) {
        String resultString = "";

        if (aktMode.equals(WITH_TPH)) {
            resultString = ref.toString();
        }
        else if (aktMode.equals(WITH_TYPEVARS)) {
            // Typ Ist Bekannt
            if (ref instanceof RefType) {
                return ((RefType) ref).getName().toString();
            }

            //System.out.println(ref);

            // Typ ist nicht bekannt
            if (typeVarStore.getTypeVarByTPH(ref) == null) {
                resultString = "";
                System.err.println("Fehler bei " +ref);
                return "";
            }
            else {
                resultString = typeVarStore.getTypeVarByTPH(ref).toString();

            }
        }
        else if (aktMode.equals(WITH_BLANK)) {
            resultString = "";
        }

        return resultString;
    }







}
