package de.dhbwstuttgart.typeinference.unify;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.RecursiveTask;

public class ConcurrentSetMergeTask<T> extends RecursiveTask<Set<T>> {

  public static <E> Set<E> merge(List<Set<E>> list) {
    if (list.isEmpty()) {
      return new HashSet<>();
    }
    var task = new ConcurrentSetMergeTask<>(list, 0, list.size());
    return task.compute();
  }

  private static final int LIST_THRESHOLD = 3;
  private static final int ELEMENT_THRESHOLD = 1000;

  private final List<Set<T>> list;
  private final int start;
  private final int end;

  private ConcurrentSetMergeTask(List<Set<T>> list, int start, int end) {
    this.list = list;
    this.start = start;
    this.end = end;
  }

  @Override
  protected Set<T> compute() {
    int size = end - start;

    int totalElements = 0;
    for (int i = start+1; i < end; i++) {
      totalElements += list.get(i).size();
    }

    // size will always be at least one
    if (true || size <= LIST_THRESHOLD || totalElements < ELEMENT_THRESHOLD) {
      Set<T> result = this.list.get(start);
      for (int i = start+1; i < end; i++) {
        result.addAll(list.get(i));
      }
      return result;
    } else {
      int mid = start + (size / 2);
      ConcurrentSetMergeTask<T> leftTask = new ConcurrentSetMergeTask<>(list, start, mid);
      ConcurrentSetMergeTask<T> rightTask = new ConcurrentSetMergeTask<>(list, mid, end);

      leftTask.fork();
      Set<T> rightResult = rightTask.compute();
      Set<T> leftResult = leftTask.join();

      // Merge results
      leftResult.addAll(rightResult);
      return leftResult;
    }
  }
}
