package de.dhbwstuttgart.typeinference.constraints;


import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder;
import de.dhbwstuttgart.typeinference.unify.GuavaSetOperations;
import de.dhbwstuttgart.typeinference.unify.model.UnifyPair;

import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

public class ConstraintSet<A> {
    Constraint<A> undConstraints = new Constraint<>();
    List<Set<Constraint<A>>> oderConstraints = new ArrayList<>();

    public void addUndConstraint(A p){
        undConstraints.add(p);
    }

    public void addOderConstraint(Set<Constraint<A>> methodConstraints) {
        oderConstraints.add(methodConstraints);
    }

    public void addAll(ConstraintSet constraints) {
        this.undConstraints.addAll(constraints.undConstraints);
        this.oderConstraints.addAll(constraints.oderConstraints);
    }

    @Override
    public String toString(){
        return cartesianProduct().toString();
    }

    public Set<List<Constraint<A>>> cartesianProduct(){
        Set<Constraint<A>> toAdd = new HashSet<>();
        toAdd.add(undConstraints);
        List<Set<Constraint<A>>> allConstraints = new ArrayList<>();
        allConstraints.add(toAdd);
        allConstraints.addAll(oderConstraints);
        return new GuavaSetOperations().cartesianProduct(allConstraints);
    }

    public <B> ConstraintSet<B> map(Function<? super A,? extends B> o) {
        ConstraintSet<B> ret = new ConstraintSet<>();
        ret.undConstraints = undConstraints.stream().map(o).collect(Collectors.toCollection(Constraint<B>::new));
        List<Set<Constraint<B>>> newOder = new ArrayList<>();
        for(Set<Constraint<A>> oderConstraint : oderConstraints){
            newOder.add(
                    oderConstraint.parallelStream().map((Constraint<A> as) ->
                            as.stream().map(o).collect(Collectors.toCollection(Constraint<B>::new))).collect(Collectors.toSet())
            );
        }
        ret.oderConstraints = newOder;
        return ret;
    }
    
    public void forEach (Consumer<? super A> c) {
        undConstraints.stream().forEach(c);
        for(Set<Constraint<A>> oderConstraint : oderConstraints){
            oderConstraint.parallelStream().forEach((Constraint<A> as) ->
                            as.stream().forEach(c));
        }
    }
    
    public Set<A> getAll () {
    	Set<A> ret = new HashSet<>();
        ret.addAll(undConstraints);
        for(Set<Constraint<A>> oderConstraint : oderConstraints){
            oderConstraint.parallelStream().forEach((Constraint<A> as) -> ret.addAll(as));
        }
        return ret;
    }
    
    public List<Set<Constraint<A>>> getOderConstraints() {
    	return oderConstraints;
    }
    
    public Set<A> getUndConstraints() {
    	return undConstraints;
    }
}
