package de.dhbwstuttgart.strucTypes5.algo;

/**
 * Created by sebastian on 21.04.17.
 */



import de.dhbwstuttgart.parser.NullToken;
import de.dhbwstuttgart.strucTypes5.constraints.ConstraintAbstract;
import de.dhbwstuttgart.strucTypes5.constraints.ConstraintField;
import de.dhbwstuttgart.strucTypes5.constraints.ConstraintMethod;
import de.dhbwstuttgart.strucTypes5.typeVars.GenericNames;
import de.dhbwstuttgart.strucTypes5.typeVars.TypeVar;
import de.dhbwstuttgart.strucTypes5.typeVars.TypeVarStore;
import de.dhbwstuttgart.syntaxtree.*;
import de.dhbwstuttgart.syntaxtree.statement.Block;
import de.dhbwstuttgart.syntaxtree.statement.Receiver;
import de.dhbwstuttgart.syntaxtree.statement.literal.Null;
import de.dhbwstuttgart.syntaxtree.type.GenericRefType;
import de.dhbwstuttgart.syntaxtree.type.RefType;
import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder;
import de.dhbwstuttgart.typecheck.JavaClassName;

import java.util.*;

/**
 * Takes a Typled Class and a Constraint Set for construct the Interfaces
 * => typed class = cl + typVarStore + List with Constraints
 */
public class Construct {

    List<ClassOrInterface> interfaceList = new ArrayList<>();
    List<ConstraintAbstract> remainingConstraints = new ArrayList<>();


    public Construct(ClassOrInterface classOrInterface, TypeVarStore typeVarStore , List<ConstraintAbstract> constraints) {
        // Generate Interfaces for Field und MethodConstraints;
        // First of all The Interfaces must be sortet
        // Other Constraints must be remain in the new List

        // Sort the Constriants
        Map<TypeVar, Set<ConstraintAbstract>> constraintSet =  sortConstriants(constraints);

        // Generate a Interface for every StructuralType
        for (TypeVar typeVar  : constraintSet.keySet()) {
            interfaceList.add(generateInterface(typeVar , constraintSet.get(typeVar)));
        }
    }





    /**
     *
     * @param constraints
     * @return
     *
     * Method sortet Constraints for ReceiverType for the Structural Type.
     * Other Constraints as the Field or MethodsConstraints are copied to the global List remainungConstraints
     *
     */
    private Map<TypeVar,Set<ConstraintAbstract>> sortConstriants(List<ConstraintAbstract> constraints) {
        Map<TypeVar, Set<ConstraintAbstract>> sortetConstraints  = new HashMap<>();

        for (ConstraintAbstract constraint : constraints) {

            if (constraint instanceof ConstraintMethod) {
                ConstraintMethod constraintMethod = (ConstraintMethod) constraint;

                if (sortetConstraints.containsKey(constraintMethod.getReceiver())) {
                    sortetConstraints.get(constraintMethod.getReceiver()).add(constraintMethod);
                }
                else {
                    Set<ConstraintAbstract> constraintSet = new HashSet<>();
                    constraintSet.add(constraint);
                    sortetConstraints.put(constraintMethod.getReceiver() , constraintSet);
                }

            }
            else if (constraint instanceof ConstraintField ) {
                ConstraintField constraintField = (ConstraintField) constraint;


                if (sortetConstraints.containsKey(constraintField.getReceiver())) {
                    sortetConstraints.get(constraintField.getReceiver()).add(constraintField);
                }
                else {
                    Set<ConstraintAbstract> constraintSet = new HashSet<>();
                    constraintSet.add(constraint);
                    sortetConstraints.put(constraintField.getReceiver() , constraintSet);
                }

            }

            else {
                remainingConstraints.add(constraint);
            }
        }
        return sortetConstraints;
    }


    /**
     * Method Generates Interfaces for the Structural Type.
     * The Method also adds the new Constraints to the remaining Constraints List.
     *
     */
    private ClassOrInterface generateInterface(TypeVar receiver , Set<ConstraintAbstract> constraintSet) {

        List<Field>  fields = new ArrayList<>();
        List<Method> methods = new ArrayList<>();

        List<GenericTypeVar> genericRefTypes = new ArrayList<>();
        GenericDeclarationList generics = new GenericDeclarationList(genericRefTypes , new NullToken());




        for (ConstraintAbstract constraint : constraintSet) {
            if (constraint instanceof ConstraintField) {
                ConstraintField cf = (ConstraintField) constraint;
                // Field(String name, RefTypeOrTPHOrWildcardOrGeneric type, int modifier, Token offset)

                RefType refType = new RefType(new JavaClassName( cf.getReceiver().toString()), new NullToken());
                Field newField = new Field(cf.getNameOfField() ,refType , 0, new NullToken()  );
                fields.add(newField);
            }
            else if (constraint instanceof  ConstraintMethod) {
                ConstraintMethod cm = (ConstraintMethod) constraint;

                // 	public Method(String name, RefTypeOrTPHOrWildcardOrGeneric returnType, int modifiers, ParameterList parameterList, Block block,
                //GenericDeclarationList gtvDeclarations, Token offset)

                String name = cm.getMethodName();


                RefType returnType = new RefType(new JavaClassName(GenericNames.makeNewGenericName()) , new NullToken() );


                int modifers = 0;

                List<FormalParameter> formalParameters = new ArrayList<>();
                ParameterList parameterList = new ParameterList(formalParameters, new NullToken());


                Block block = null;
                GenericDeclarationList genericDeclarationList = null;


                Method m = new Method(name, returnType, modifers, parameterList, block , genericDeclarationList, new NullToken());
                methods.add(m);
            }
        }


        //Konstruktor:
        //   public ClassOrInterface(int modifiers, JavaClassName name, List<Field> fielddecl, List<Method> methods, GenericDeclarationList genericClassParameters,
        // RefTypeOrTPHOrWildcardOrGeneric superClass, Boolean isInterface, List<? extends RefTypeOrTPHOrWildca
        int modifers = 0;
        JavaClassName name = new JavaClassName(receiver.toString());
        // Fields and Methos from List
        GenericDeclarationList genericClassParameters = null;
        RefTypeOrTPHOrWildcardOrGeneric superClass = null;
        Boolean isInterface = true;
        List<RefTypeOrTPHOrWildcardOrGeneric> implementetInterfaces = new ArrayList<>();

        ClassOrInterface newInterface = new ClassOrInterface(modifers, name, fields,methods, genericClassParameters, superClass , isInterface, implementetInterfaces ,new NullToken());
        return newInterface;
    }






}
