
package de.ivu.elect.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.annotation.Nonnull;

public class ListUtil {

  /**
   * @return Liefert den Eintrag in der Liste nach der aktuellen Entity (currentEntity) zurück. Liefert die aktuelle Entity (currentEntity) zurück, falls es keinen oder keinen nächsten Eintrag gibt.
   */
  public static <T> T next(@Nonnull List<T> list, T currentEntity) {
    if (list.isEmpty()) {
      return currentEntity;
    }

    int indexOfCurrentEntity = list.indexOf(currentEntity);
    if (indexOfCurrentEntity != -1) {
      int nextIndex = indexOfCurrentEntity + 1;
      if (nextIndex < list.size()) {
        return list.get(nextIndex);
      }
    }
    return currentEntity;
  }

  /**
   * @return Liefert den Eintrag in der Liste nach der aktuellen Entity (currentEntity) zurück. Liefert die erste Entity in der Liste zurück, falls es keinen nächsten Eintrag gibt. Liefert die
   *         aktuelle Entity (currentEntity) zurück, falls es keinen Eintrag gibt.
   */
  public static <T> T nextOrFirst(@Nonnull List<T> list, T currentEntity) {
    if (list.isEmpty()) {
      return currentEntity;
    }

    int indexOfCurrentEntity = list.indexOf(currentEntity);
    if (indexOfCurrentEntity != -1) {
      int nextIndex = indexOfCurrentEntity + 1;
      if (nextIndex < list.size()) {
        return list.get(nextIndex);
      }
    }
    return list.get(0);
  }

  /**
   * @return Liefert den Eintrag in der Liste vor der aktuellen Entity (currentEntity) zurück. Liefert die aktuelle Entity (currentEntity) zurück, falls es keinen oder keinen vorherigen Eintrag gibt.
   */
  public static <T> T previous(@Nonnull List<T> list, T currentEntity) {
    if (list.isEmpty()) {
      return currentEntity;
    }

    int indexOfCurrentEntity = list.indexOf(currentEntity);
    if (indexOfCurrentEntity != -1) {
      int nextIndex = indexOfCurrentEntity - 1;
      if (nextIndex >= 0) {
        return list.get(nextIndex);
      }
    }
    return currentEntity;
  }

  /**
   * Zerlegt eine Liste in Blöcke fester Größe. Achtung: Es werden Sets benutzt, bei doppelten Einträgen ist das Verhaten schwierig voraus zu sehen.
   *
   * @return Die <code>tokens</code>, zerlegt in Teilstuecke fester Länge <code>blockSize</code>.
   */
  public static <T> List<Set<T>> bildeBloecke(@Nonnull Collection<T> tokens, int blockSize) {
    List<Set<T>> result = new ArrayList<>();
    Set<T> aktuellerBlock = null;
    for (T token : tokens) {
      if (aktuellerBlock == null || aktuellerBlock.size() >= blockSize) {
        aktuellerBlock = new HashSet<>();
        result.add(aktuellerBlock);
      }
      aktuellerBlock.add(token);
    }
    return result;
  }

  /**
   * Zerlegt eine Liste in Blöcke fester Größe.
   *
   * @return Die <code>tokens</code>, zerlegt in Teilstuecke fester Länge <code>blockSize</code>.
   */
  public static <T> List<List<T>> bildeBloeckeAlsListen(@Nonnull Collection<T> tokens, int blockSize) {
    List<List<T>> result = new ArrayList<>();
    List<T> aktuellerBlock = null;
    for (T token : tokens) {
      if (aktuellerBlock == null || aktuellerBlock.size() >= blockSize) {
        aktuellerBlock = new ArrayList<>();
        result.add(aktuellerBlock);
      }
      aktuellerBlock.add(token);
    }
    return result;
  }

  /**
   * @return Die <code>liste</code>, zerlegt in Teilstuecke variabler Laenge. Teilstueck <code>0 &lt;= i &lt; teilstueckGroessen.size()</code> hat Laenge <code>teilstueckGroessen.get(i)</code>.
   *         Ueberzaehlige Elemente in <code>liste</code> werden in ein letztes, zusaetzliches Teilstueck gelegt.
   */
  public static <T> List<List<T>> zerlege(@Nonnull List<T> liste, @Nonnull List<Integer> teilstueckGroessen) {
    List<List<T>> result = new ArrayList<>(teilstueckGroessen.size());

    int startIndexDesTeilstuecks = 0;
    for (int teilstueckGroesse : teilstueckGroessen) {
      if (startIndexDesTeilstuecks >= liste.size()) {
        break;
      }

      int endIndexDesTeilstuecks = Math.min(liste.size(), startIndexDesTeilstuecks + teilstueckGroesse);

      result.add(new ArrayList<>(liste.subList(startIndexDesTeilstuecks, endIndexDesTeilstuecks)));
      startIndexDesTeilstuecks += teilstueckGroesse;
    }

    if (startIndexDesTeilstuecks < liste.size()) {
      result.add(new ArrayList<>(liste.subList(startIndexDesTeilstuecks, liste.size())));
    }

    return result;
  }

  @Deprecated // JAVA 8 verwenden
  public static <T> List<T> unionAll(@Nonnull Collection< ? extends Collection< ? extends T>> collections) {
    List<T> result = new ArrayList<>();
    for (Collection< ? extends T> collection : collections) {
      result.addAll(collection);
    }
    return result;
  }

  public static <T> boolean isFirstElement(@Nonnull List<T> list, T elemet) {
    return list.indexOf(elemet) == 0;
  }

  public static <T> boolean isLastElement(@Nonnull List<T> list, T elemet) {
    return list.indexOf(elemet) == list.size() - 1;
  }

  public static <T> T lastOrNull(@Nonnull List<T> list) {
    if (list.isEmpty()) {
      return null;
    }
    return list.get(list.size() - 1);
  }
}
