/*
* Copyright (C) 2015 Actor LLC. <https://actor.im>
*/
package im.actor.runtime.generic.mvvm.alg;
import com.google.j2objc.annotations.AutoreleasePool;
import java.util.ArrayList;
import java.util.List;
import im.actor.runtime.generic.mvvm.ChangeDescription;
import im.actor.runtime.storage.ListEngineItem;
// Disabling Bounds checks for speeding up calculations
/*-[
#define J2OBJC_DISABLE_ARRAY_BOUND_CHECKS 1
]-*/
public class Modifications {
public static <T extends ListEngineItem> Modification<T> noOp() {
return new Modification<T>() {
@Override
public List<ChangeDescription<T>> modify(ArrayList<T> sourceList) {
return new ArrayList<>();
}
};
}
public static <T extends ListEngineItem> Modification<T> addOrUpdate(final T item) {
ArrayList<T> res = new ArrayList<T>();
res.add(item);
return addOrUpdate(res);
}
public static <T extends ListEngineItem> Modification<T> addOrUpdate(final List<T> items) {
return new Modification<T>() {
@Override
public List<ChangeDescription<T>> modify(ArrayList<T> sourceList) {
ArrayList<ChangeDescription<T>> res = new ArrayList<>();
addOrUpdate(items, sourceList, res, false);
return res;
}
};
}
public static <T extends ListEngineItem> Modification<T> addLoadMore(final List<T> items) {
return new Modification<T>() {
@Override
public List<ChangeDescription<T>> modify(ArrayList<T> sourceList) {
ArrayList<ChangeDescription<T>> res = new ArrayList<>();
addOrUpdate(items, sourceList, res, true);
return res;
}
};
}
public static <T extends ListEngineItem> Modification<T> replace(final List<T> items) {
return new Modification<T>() {
@Override
public List<ChangeDescription<T>> modify(ArrayList<T> sourceList) {
ArrayList<ChangeDescription<T>> res = new ArrayList<>();
replace(items, sourceList, res);
return res;
}
};
}
public static <T extends ListEngineItem> Modification<T> remove(final long dstId) {
return remove(new long[]{dstId});
}
public static <T extends ListEngineItem> Modification<T> remove(final long[] dstIds) {
return new Modification<T>() {
@Override
public List<ChangeDescription<T>> modify(ArrayList<T> sourceList) {
ArrayList<ChangeDescription<T>> res = new ArrayList<>();
for (int i = 0; i < sourceList.size(); i++) {
ListEngineItem src = sourceList.get(i);
for (long aDstId : dstIds) {
if (src.getEngineId() == aDstId) {
sourceList.remove(i);
res.add(ChangeDescription.<T>remove(i));
i--;
break;
}
}
}
return res;
}
};
}
public static <T> Modification<T> clear() {
return new Modification<T>() {
@Override
public List<ChangeDescription<T>> modify(ArrayList<T> sourceList) {
ArrayList<ChangeDescription<T>> res = new ArrayList<>();
if (sourceList.size() != 0) {
res.add(ChangeDescription.<T>remove(0, sourceList.size()));
sourceList.clear();
}
return res;
}
};
}
@AutoreleasePool
private static <T extends ListEngineItem> void replace(List<T> items,
ArrayList<T> sourceList,
ArrayList<ChangeDescription<T>> changes) {
// Remove missing items
outer:
for (int i = 0; i < sourceList.size(); i++) {
long id = sourceList.get(i).getEngineId();
for (T itm : items) {
if (itm.getEngineId() == id) {
continue outer;
}
}
changes.add(ChangeDescription.<T>remove(i));
sourceList.remove(i);
i--;
}
for (T itm : items) {
addOrUpdate(itm, sourceList, changes, false);
}
}
@AutoreleasePool
private static <T extends ListEngineItem> void addOrUpdate(List<T> items,
ArrayList<T> sourceList,
ArrayList<ChangeDescription<T>> changes,
boolean isLoadMore) {
for (T toAdd : items) {
addOrUpdate(toAdd, sourceList, changes, isLoadMore);
}
}
@AutoreleasePool
private static <T extends ListEngineItem> void addOrUpdate(T item,
ArrayList<T> sourceList,
ArrayList<ChangeDescription<T>> changes,
boolean isLoadMore) {
long id = item.getEngineId();
long sortKey = item.getEngineSort();
// Finding suitable place for item
int removedIndex = -1;
int addedIndex = -1;
for (int i = 0; i < sourceList.size(); i++) {
T srcItem = sourceList.get(i);
if (srcItem.getEngineId() == id) {
if (isLoadMore) {
return;
}
// Remove old item
sourceList.remove(i);
if (addedIndex >= 0) {
removedIndex = i - 1;
} else {
removedIndex = i;
}
i--;
continue;
} else {
// TODO: Fix ADD ONLY
if ((addedIndex < 0) && sortKey > srcItem.getEngineSort()) {
addedIndex = i;
sourceList.add(i, item);
i++;
}
}
// Already founded
if (addedIndex >= 0 && removedIndex >= 0) {
break;
}
}
// If no place for insert: insert to end
if (addedIndex < 0) {
addedIndex = sourceList.size();
sourceList.add(sourceList.size(), item);
}
if (addedIndex == removedIndex) {
// If there are no movement: just update item in place
changes.add(ChangeDescription.update(addedIndex, item));
} else if (removedIndex >= 0) {
// Movement + update occurred
changes.add(ChangeDescription.update(removedIndex, item));
changes.add(ChangeDescription.<T>move(removedIndex, addedIndex));
} else {
// No old element found: add new element
changes.add(ChangeDescription.add(addedIndex, item));
}
}
}