content: /*
   * Copyright (C) 2015 Actor LLC. <https://actor.im>
   */
  
  package im.actor.runtime.generic.mvvm.alg;
  
  import java.util.ArrayList;
  
  import im.actor.runtime.generic.mvvm.AppleListUpdate;
  import im.actor.runtime.generic.mvvm.ChangeDescription;
  
  // Disabling Bounds checks for speeding up calculations
  
  /*-[
  #define J2OBJC_DISABLE_ARRAY_BOUND_CHECKS 1
  ]-*/
  
  public final class ChangeBuilder {
  
      private static <T> ArrayList<ChangeDescription<T>> optimize(ArrayList<ChangeDescription<T>> modifications) {
          ArrayList<ChangeDescription<T>> res = new ArrayList<ChangeDescription<T>>();
          ChangeDescription<T> desc = null;
          for (ChangeDescription<T> d : modifications) {
  
              if (d.getOperationType() == ChangeDescription.OperationType.ADD) {
                  if (desc != null) {
                      if (desc.getOperationType() == ChangeDescription.OperationType.ADD) {
                          if (desc.getIndex() + desc.getLength() == d.getIndex()) {
                              desc = ChangeDescription.mergeAdd(desc, d);
                          } else {
                              res.add(desc);
                              desc = null;
                          }
                      } else {
                          res.add(desc);
                          desc = null;
                      }
                  }
              } else {
                  if (desc != null) {
                      res.add(desc);
                      desc = null;
                  }
              }
  
              if (desc == null) {
                  desc = d;
                  // res.add(d);
              }
          }
          if (desc != null) {
              res.add(desc);
          }
          return res;
      }
  
      public static <T> ArrayList<ChangeDescription<T>> processAndroidModifications(
              ArrayList<ChangeDescription<T>> modifications, ArrayList<T> initialList) {
          modifications = optimize(modifications);
  
          return modifications;
      }
  
      public static <T> AppleListUpdate processAppleModifications(
              ArrayList<ChangeDescription<T>> modifications, ArrayList<T> initialList, boolean isLoadMore) {
  
          modifications = optimize(modifications);
  
          ArrayList<State<T>> states = new ArrayList<State<T>>();
          ArrayList<State<T>> current = new ArrayList<State<T>>();
  
          for (int i = 0; i < initialList.size(); i++) {
              State<T> state = new State<T>();
              state.startingIndex = i;
              states.add(state);
              current.add(state);
          }
          for (ChangeDescription<T> m : modifications) {
              if (m.getOperationType() == ChangeDescription.OperationType.REMOVE) {
                  for (int i = 0; i < m.getLength(); i++) {
                      State<T> state = current.remove(m.getIndex());
                      state.wasDeleted = true;
                  }
              } else if (m.getOperationType() == ChangeDescription.OperationType.ADD) {
                  int index = m.getIndex();
                  for (T itm : m.getItems()) {
                      State<T> state = new State<T>();
                      state.wasAdded = true;
                      state.item = itm;
                      current.add(index++, state);
                  }
              } else if (m.getOperationType() == ChangeDescription.OperationType.MOVE) {
                  State<T> state = current.remove(m.getIndex());
                  current.add(m.getDestIndex(), state);
                  if (!state.wasAdded) {
                      state.wasMoved = true;
                  }
              } else if (m.getOperationType() == ChangeDescription.OperationType.UPDATE) {
                  int index = m.getIndex();
                  for (T itm : m.getItems()) {
                      State<T> state = current.get(index++);
                      state.item = itm;
                      if (!state.wasAdded) {
                          state.wasUpdated = true;
                      }
                  }
              }
          }
  
          ArrayList<Integer> removed = new ArrayList<Integer>();
          ArrayList<Move> moved = new ArrayList<Move>();
          ArrayList<Integer> added = new ArrayList<Integer>();
          ArrayList<Integer> updated = new ArrayList<Integer>();
  
          // Building deletions
          for (int i = 0; i < states.size(); i++) {
              State<T> s = states.get(i);
              if (s.wasDeleted) {
                  removed.add(s.startingIndex);
              }
          }
  
          // Building add/update/move
          for (int i = 0; i < current.size(); i++) {
              State<T> s = current.get(i);
              if (s.wasMoved && s.startingIndex != i) {
                  moved.add(new Move(s.startingIndex, i));
              }
              if (s.wasUpdated) {
                  updated.add(i);
              }
              if (s.wasAdded) {
                  added.add(i);
              }
          }
  
          return new AppleListUpdate(removed, added, moved, updated, isLoadMore);
      }
  
      private ChangeBuilder() {
  
      }
  
      private static class State<T> {
          private boolean wasDeleted;
          private int startingIndex = -1;
          private boolean wasUpdated;
          private boolean wasMoved;
          private boolean wasAdded;
          private T item;
      }
  }
  