package de.dhbwstuttgart.typeinference.unify.cartesianproduct;

import de.dhbwstuttgart.typeinference.constraints.Constraint;
import de.dhbwstuttgart.typeinference.unify.TypeUnifyTask;
import de.dhbwstuttgart.typeinference.unify.UnifyContext;
import de.dhbwstuttgart.typeinference.unify.interfaces.IFiniteClosure;
import de.dhbwstuttgart.typeinference.unify.model.PlaceholderType;
import de.dhbwstuttgart.typeinference.unify.model.UnifyPair;
import de.dhbwstuttgart.util.Tuple;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

public class UnknownVarianceCase extends VarianceCase {

  protected final int variance = 0;

  protected UnknownVarianceCase(boolean isOderConstraint, TypeUnifyTask typeUnifyTask, UnifyContext context) {
    super(isOderConstraint, typeUnifyTask, context);
  }

  @Override
  public void selectNextData(
    TypeUnifyTask typeUnifyTask,
    List<Set<UnifyPair>> nextSetAsList,
    Optional<UnifyPair> optOrigPair

  ) {
    //wenn a <. theta dann ist ein maximales Element sehr wahrscheinlich
    //wenn theta <. a dann ist ein minimales Element sehr wahrscheinlich
    if (!this.isOderConstraint && optOrigPair != null && optOrigPair.isPresent()) {
      if (optOrigPair.get().getBasePair().getLhsType() instanceof PlaceholderType) {
        a = typeUnifyTask.oup.max(nextSetAsList.iterator());
      } else {
        a = typeUnifyTask.oup.min(nextSetAsList.iterator());
      }
      nextSetAsList.remove(a);
    } else if (this.isOderConstraint) {
      a = typeUnifyTask.oup.max(nextSetAsList.iterator());
      nextSetAsList.remove(a);
      nextSetasListOderConstraints.add(((Constraint<UnifyPair>) a).getExtendConstraint());
    } else {
      a = nextSetAsList.removeFirst();
    }
  }

  @Override
  public CompletableFuture<ComputationResults> computeParallel(
    Set<Set<UnifyPair>> elems,
    Set<UnifyPair> eq,
    List<Set<Constraint<UnifyPair>>> oderConstraints,
    IFiniteClosure fc,
    int rekTiefe,
    Set<UnifyPair> methodSignatureConstraint,
    List<Set<UnifyPair>> nextSetAsList,
    Set<UnifyPair> sameEqSet,
    Set<Set<UnifyPair>> result,
    Set<Set<UnifyPair>> aParDef
  ) {
    elems.add(a);
    return typeUnifyTask.unify2(elems, eq, oderConstraints, fc, context.parallel(), rekTiefe, new HashSet<>(methodSignatureConstraint))
      .thenApply(ComputationResults::new);
  }

  @Override
  public void applyComputedResults(
    Set<Set<UnifyPair>> result,
    Set<Set<UnifyPair>> currentThreadResult,
    Set<UnifyPair> compResult,
    Set<UnifyPair> compRes
  ) {
    context.logger().debug("RES var=0 ADD:" + result.toString() + " " + currentThreadResult.toString());
    result.addAll(currentThreadResult);
  }

  @Override
  public boolean eraseInvalidSets(
    int rekTiefe,
    Set<Set<UnifyPair>> aParDef,
    List<Set<UnifyPair>> nextSetAsList
  ) {
    if (!this.isOderConstraint) {
      return true;
    } else {
      nextSetAsList.removeAll(nextSetasListOderConstraints);
      nextSetasListOderConstraints = new ArrayList<>();
      context.logger().debug("Removed: " + nextSetasListOderConstraints);
      List<Set<UnifyPair>> smallerSetasList = typeUnifyTask.oup.smallerThan(a, nextSetAsList);
      List<Set<UnifyPair>> notInherited = smallerSetasList.stream()
        .filter(x -> !((Constraint<UnifyPair>) x).isInherited())
        .collect(Collectors.toCollection(ArrayList::new));
      List<Set<UnifyPair>> notErased = new ArrayList<>();
      notInherited.forEach(x -> notErased.addAll(typeUnifyTask.oup.smallerEqThan(x, smallerSetasList)));
      List<Set<UnifyPair>> erased = new ArrayList<>(smallerSetasList);
      erased.removeAll(notErased);
      nextSetAsList.removeAll(erased);

      context.logger().debug("Removed: " + erased);

      context.logger().debug("Not Removed: " + nextSetAsList);

    }
    return false;
  }

}
