package de.dhbwstuttgart.strucTypes4.algo;

import de.dhbwstuttgart.strucTypes4.syntaxtree.*;
import de.dhbwstuttgart.strucTypes4.typePlaceHolder.TPH;
import de.dhbwstuttgart.strucTypes4.typePlaceHolder.TPHFactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by sebastian on 09.04.17.
 */

public class Construct {

    List<ConstraintAbstract> resultConstraints = new ArrayList<>();
    List<Interface> resultInterfaces = new ArrayList<>();


    // Mapping der alten Typvariablen zu neuen
    private Map<TPH,TPH> altZuNeuTPH = new HashMap<>();
    private String nameInterface = "";




    public Construct(List<ConstraintAbstract> constraints ) {
        constructInterfacesSortet(constraints);
    }


    private void constructInterfacesSortet(List<ConstraintAbstract> constraints) {

        Map<TPH, List<ConstraintAbstract>> sortetConst = sortiereConstraints(constraints);

        resultInterfaces = new ArrayList<>();

        // Erstelle Interfaces und Neue Subtype Constraints
        for (TPH key : sortetConst.keySet() ) {
            ConstructInterfaceForType constructInterfaceForType = new ConstructInterfaceForType(sortetConst.get(key) , altZuNeuTPH, key);
            resultInterfaces.addAll(constructInterfaceForType.resultInterfaces);
            resultConstraints.addAll(constructInterfaceForType.subTypeconstraints);
        }


        // KopiereSubconstraints
        for (ConstraintAbstract  c: constraints  ) {
            if (c.getClass().equals(ConstraintSubType.class)) {
                resultConstraints.add(c);
            }
        }

        // Kopiere ConstraintSubTypeInterface
        for (ConstraintAbstract c: constraints) {
            if (c.getClass().equals(ConstraintSubTypeInterface.class)) {
                resultConstraints.add(c);
            }
        }
    }





    private Map<TPH,List<ConstraintAbstract>> sortiereConstraints(List<ConstraintAbstract> constraints) {

        Map<TPH,List<ConstraintAbstract>> constraintMap = new ConcurrentHashMap<>();

        for (ConstraintAbstract con : constraints) {

            TPH receiver = null;

            // Suche den Receiver der Metohde oder des Feldes
            if (con.getClass().equals(ConstraintMethod.class)) {
                ConstraintMethod cm = (ConstraintMethod) con;
                //receiver = cm.getReceiver();
            }
            else if (con.getClass().equals(ConstraintField.class)) {
                ConstraintField cf = (ConstraintField) con;
                //receiver = cf.getReceiver();
             }


             // wenn Receiver vorhanden -> füge ihn in die Map ein;
             if (receiver != null ) {
                if (constraintMap.containsKey(receiver)) {
                    constraintMap.get(receiver).add(con);
                }
                else {
                    List<ConstraintAbstract> clist = new ArrayList<>();
                    clist.add(con);
                    constraintMap.put(receiver,clist);
                }
            }
        }

        return constraintMap;
    }







    public List<ConstraintAbstract> getResultConstraints() {
        return resultConstraints;
    }

    public void setResultConstraints(List<ConstraintAbstract> resultConstraints) {
        this.resultConstraints = resultConstraints;
    }

    public List<Interface> getResultInterfaces() {
        return resultInterfaces;
    }

    public void setResultInterfaces(List<Interface> resultInterfaces) {
        this.resultInterfaces = resultInterfaces;
    }
}




class ConstructInterfaceForType {

    List<Interface> resultInterfaces = new ArrayList<>();
    List<ConstraintAbstract> subTypeconstraints = new ArrayList<>();
    Map<TPH,TPH> altZuNeuTPH;

    public ConstructInterfaceForType(List<ConstraintAbstract> cons , Map<TPH,TPH> altZuNeu ,TPH receiver ) {
        this.altZuNeuTPH = altZuNeu;
        constructInterfaces(cons , receiver);
    }




    public void constructInterfaces(List<ConstraintAbstract> constraints , TPH receiver) {

        List<MethodInterface> mlist = new ArrayList<>();
        List<FieldInterface> flist = new ArrayList<>();
        List<TPH> generics = new ArrayList<>();
        String nameInterface = "";



        // generate Interface Fields
        for (ConstraintAbstract c : constraints) {

            if (c.getClass().equals(ConstraintField.class)) {
                ConstraintField constraintField = (ConstraintField) c;

                //FieldInterface field = new FieldInterface(changeTPH(constraintField.getCaller()),constraintField.nameField);
                //flist.add(field);

                nameInterface = nameInterface.concat(constraintField.nameField);

            }
        }


        // generate Interface Methods   // Wichtig ist es die Typvariablen in den Methoden auszutauschnen
        for (ConstraintAbstract c : constraints) {
            if (c.getClass().equals(ConstraintMethod.class)) {
                ConstraintMethod constraintMethod = (ConstraintMethod) c;

                // Erstelle generics (erster ist return, alle weitere Arg der Methode
                //
                //generics.add(changeTPH(constraintMethod.returnType));
                //generics.addAll(changeTPHList(constraintMethod.argsBetaTypes));


                //MethodInterface methodInterface = new MethodInterface(constraintMethod.methodName , changeTPH(constraintMethod.returnType) , changeTPHList(constraintMethod.argsBetaTypes));
                //mlist.add(methodInterface);

                nameInterface  = "";



                // Ergebnis Subtype Relation
                List<TPH> genericsFuerConstraint = new ArrayList<>();
                //genericsFuerConstraint.add(constraintMethod.returnType);
                //genericsFuerConstraint.addAll(constraintMethod.argsBetaTypes);
                //subTypeconstraints.add(new ConstraintSubTypeInterface( TPHFactory.makeTPH(), genericsFuerConstraint, constraintMethod.getReceiver()));



            }
        }

        // Ergebnis Interface
       // Interface anInterface = new Interface( receiver, flist , mlist, generics);
        //resultInterfaces.add(anInterface);
    }




    private TPH changeTPH(TPH alt) {
        if (altZuNeuTPH.containsKey(alt)) {
            return  altZuNeuTPH.get(alt);
        }
        else {
            altZuNeuTPH.put(alt, TPHFactory.makeTPH());
            return altZuNeuTPH.get(alt);
        }
    }

    private List<TPH> changeTPHList(List<TPH> list) {
        List<TPH> list1 = new ArrayList<>();
        list.forEach((x) -> list1.add(changeTPH(x)));
        return list1;
    }


}

