package de.dhbwstuttgart.strucTypes4.syntaxtree;

import de.dhbwstuttgart.strucTypes4.algo.*;
import de.dhbwstuttgart.strucTypes4.typeVars.TypeVarAbstract;
import de.dhbwstuttgart.strucTypes4.typeVars.TypeVarStore;
import de.dhbwstuttgart.strucTypes4.typeVars.TypeVarType;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Created by sebastian on 07.04.17.
 */
public class ExprMCall extends  ExpressionAbstract {

    String name;
    List<ExpressionAbstract> args;

    public ExprMCall(String name, List<ExpressionAbstract> args) {
        this.name = name;
        this.args = args;
    }


    public List<ConstraintAbstract> typeExpr (Map<String, TypeVarAbstract> ass , TypeVarAbstract receiver , TypeVarAbstract returnType, TypeVarStore typeVarStore) {
        List<ConstraintAbstract> constraints = new ArrayList<>();



        if  (receiver.getClass().equals(TypeVarType.class)) {
            TypeVarType type = (TypeVarType) receiver;


            Class c = type.getTypeClass();

            Method m = c.mType(name);

             List<Argument> arguments = m.getArgs();

             List<TypeVarAbstract> typeVars = new ArrayList<>();
             // Ermitteln der TypeVars der alten Klasse
             for (Argument argument : arguments ) {
                 typeVars.add(c.getTypeVarStore().getTypeVarByTPH(argument.getTypePlaceHolder()));
             }

             // Ermitteln der Return typen in der Klasse Main mt
            for (ExpressionAbstract arg : args) {
                 constraints.addAll(TypeExpr.typeExpr(arg,ass,null,null,typeVarStore));
            }

            // Hinzufügen von Constraints
            for (int i = 0; i < args.size() ; i++) {
                 TypeVarAbstract tvInNewKlasse  = typeVars.get(i);
                 TypeVarAbstract typeInAktKlasse = typeVarStore.getTypeVarByTPH(args.get(i).getTypePlaceHolder());
                 constraints.add(new ConstraintSubType(tvInNewKlasse , typeInAktKlasse));
            }

            constraints.addAll(c.getConstraintSet());


            return constraints;
        }
        // Receivertyp ist TypVariable
        else {

            // Typeexpr  Der Argumente der Methode
            args.forEach(x -> constraints.addAll( TypeExpr.typeExpr(  x,ass,null,null,typeVarStore) ));

            // Liste mit Typen der Argumente der Methode sammeln
            List<TypeVarAbstract> types = new ArrayList<>();
            args.forEach(x -> types.add(typeVarStore.getTypeVarByTPH(x.getTypePlaceHolder())));

            // Neue Liste mit neuen Beta - Argumenten erstellen
            List<TypeVarAbstract> betaTypes = new ArrayList<>();
            args.forEach(x -> betaTypes.add( typeVarStore.makeFreshTypeVar() ));


            // Erstellen der Substyp constraints
            for (int i = 0 ; i < types.size() ; i++) {
                ConstraintSubType con =  new ConstraintSubType(types.get(i) , betaTypes.get(i) );
                constraints.add(con);
            }

            constraints.add( new ConstraintMethod(receiver , name, types , returnType , betaTypes  ));
            return constraints;

        }
    }


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<ExpressionAbstract> getArgs() {
        return args;
    }

    public void setArgs(List<ExpressionAbstract> args) {
        this.args = args;
    }

    @Override
    public String toString() {
        return "." + name + "(" + args.toString() +")";
    }


    @Override
    public String toCodeTPH() {
        String argsString = "";
        for (ExpressionAbstract a : args) {
           argsString =  argsString.concat(a.toCodeTPH());
        }

        return String.format(" .%s ( %s )" , name , argsString );
    }


    @Override
    public String toCodeTyped(TypeVarStore typeVarStore) {
        String argsString = "";
        for (ExpressionAbstract a : args) {
            argsString =  argsString.concat(a.toCodeTyped(typeVarStore));
        }

        return String.format(" .%s ( %s )" , name , argsString );
    }





}
