package org.phoenicis.javafx.views.common;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.transformation.TransformationList;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* Created by marc on 01.04.17.
*/
public class ExpandedList<E, F> extends TransformationList<E, F> {
private final Function<? super F, List<? extends E>> expander;
private List<List<? extends E>> expandedValues;
public ExpandedList(ObservableList<? extends F> source, Function<? super F, List<? extends E>> expander) {
super(source);
this.expander = expander;
this.expandedValues = source.stream().map(expander).collect(Collectors.toList());
}
@Override
public int getSourceIndex(int index) {
if (index < 0 || index >= size()) {
throw new IndexOutOfBoundsException();
}
int sum = 0;
int sourceIndex = -1;
for (int i = 0; i < getSource().size(); i++) {
if (index < sum) {
break;
}
sum += expandedValues.get(i).size();
sourceIndex++;
}
return sourceIndex;
}
@Override
public E get(int index) {
if (index < 0 || index >= size()) {
throw new IndexOutOfBoundsException();
}
E result = null;
int start = 0;
for (List<? extends E> values : expandedValues) {
if (start + values.size() > index) {
result = values.get(index - start);
break;
} else {
start += values.size();
}
}
return result;
}
@Override
public int size() {
return expandedValues.stream().mapToInt(List::size).sum();
}
private int getFirstIndex(int sourceIndex) {
int position = 0;
for (int i = 0; i < sourceIndex; i++) {
position += expandedValues.get(i).size();
}
return position;
}
private int getLastIndexPlusOne(int sourceIndex) {
int position = 0;
for (int i = 0; i <= sourceIndex; i++) {
position += expandedValues.get(i).size();
}
return position;
}
private void permutate(ListChangeListener.Change<? extends F> c) {
int from = c.getFrom();
int to = c.getTo();
if (to > from) {
List<? extends E> beforePermutation = expandedValues.stream().flatMap(List::stream)
.collect(Collectors.toList());
List<List<? extends E>> valuesClone = new ArrayList<List<? extends E>>(expandedValues);
for (int i = from; i < to; ++i) {
int firstOldIndex = getFirstIndex(i);
int lastOldIndexPlusOne = getLastIndexPlusOne(i);
int firstNewIndex = getFirstIndex(c.getPermutation(i));
int numberOfElements = lastOldIndexPlusOne - firstOldIndex;
valuesClone.set(i, expandedValues.get(c.getPermutation(i)));
}
this.expandedValues = valuesClone;
List<? extends E> afterPermutation = expandedValues.stream().flatMap(List::stream)
.collect(Collectors.toList());
int[] perm = beforePermutation.stream().mapToInt(afterPermutation::indexOf).toArray();
nextPermutation(from, to, perm);
}
}
private void update(ListChangeListener.Change<? extends F> c) {
int from = c.getFrom();
int to = c.getTo();
if (to > from) {
for (int i = from; i < to; ++i) {
int firstOldIndex = getFirstIndex(i);
int lastOldIndexPlusOne = getLastIndexPlusOne(i);
List<? extends E> oldValues = expandedValues.get(i);
List<? extends E> newValues = expander.apply(getSource().get(i));
expandedValues.set(i, newValues);
if (oldValues.size() > newValues.size()) {
for (int count = 0; count < newValues.size(); count++) {
nextUpdate(firstOldIndex + count);
}
nextRemove(firstOldIndex, oldValues.subList(newValues.size(), oldValues.size()));
}
if (oldValues.size() < newValues.size()) {
for (int count = 0; count < oldValues.size(); count++) {
nextUpdate(firstOldIndex + count);
}
nextAdd(firstOldIndex + oldValues.size(), firstOldIndex + newValues.size());
}
if (oldValues.size() == newValues.size()) {
for (int count = 0; count < oldValues.size(); count++) {
nextUpdate(firstOldIndex + count);
}
}
}
}
}
private void addRemove(ListChangeListener.Change<? extends F> c) {
int from = c.getFrom();
int to = c.getTo();
for (int index = from + c.getRemovedSize() - 1; index >= from; index--) {
int firstOldIndex = getFirstIndex(index);
nextRemove(firstOldIndex, expandedValues.remove(index));
}
for (int index = from; index < from + c.getAddedSize(); index++) {
int lastOldIndex = getLastIndexPlusOne(index - 1);
List<? extends E> newValues = expander.apply(getSource().get(index));
expandedValues.add(index, newValues);
nextAdd(lastOldIndex, lastOldIndex + newValues.size());
}
}
@Override
protected void sourceChanged(ListChangeListener.Change<? extends F> c) {
beginChange();
while (c.next()) {
if (c.wasPermutated()) {
permutate(c);
} else if (c.wasUpdated()) {
update(c);
} else {
addRemove(c);
}
}
endChange();
}
}