content: /*
   * Copyright (C) 2015 Actor LLC. <https://actor.im>
   */
  
  package im.actor.runtime.generic.storage;
  
  import com.google.j2objc.annotations.AutoreleasePool;
  
  import java.io.IOException;
  import java.util.ArrayList;
  import java.util.List;
  
  import im.actor.runtime.actors.Actor;
  import im.actor.runtime.bser.Bser;
  import im.actor.runtime.bser.BserCreator;
  import im.actor.runtime.bser.BserObject;
  import im.actor.runtime.storage.ListEngineDisplayLoadCallback;
  import im.actor.runtime.storage.ListEngineItem;
  import im.actor.runtime.storage.ListEngineRecord;
  import im.actor.runtime.storage.ListStorageDisplayEx;
  
  // Disabling Bounds checks for speeding up calculations
  
  /*-[
  #define J2OBJC_DISABLE_ARRAY_BOUND_CHECKS 1
  ]-*/
  
  class AsyncStorageActor<T extends BserObject & ListEngineItem> extends Actor {
  
      private final ListStorageDisplayEx storage;
      private final BserCreator<T> creator;
  
      public AsyncStorageActor(ListStorageDisplayEx storage, BserCreator<T> creator) {
          this.storage = storage;
          this.creator = creator;
      }
  
      @AutoreleasePool
      public void addOrUpdate(List<T> items) {
          if (items.size() == 1) {
              T item = items.get(0);
              storage.updateOrAdd(new ListEngineRecord(item.getEngineId(), item.getEngineSort(),
                      item.getEngineSearch(), item.toByteArray()));
          } else if (items.size() > 0) {
              List<ListEngineRecord> updated = new ArrayList<>();
              for (T i : items) {
                  updated.add(new ListEngineRecord(i.getEngineId(), i.getEngineSort(),
                          i.getEngineSearch(), i.toByteArray()));
              }
              storage.updateOrAdd(updated);
          }
      }
  
      @AutoreleasePool
      public void replace(List<T> items) {
          List<ListEngineRecord> updated = new ArrayList<>();
          for (T i : items) {
              updated.add(new ListEngineRecord(i.getEngineId(), i.getEngineSort(),
                      i.getEngineSearch(), i.toByteArray()));
          }
          storage.clear();
          storage.updateOrAdd(updated);
      }
  
      @AutoreleasePool
      public void remove(long[] keys) {
          if (keys.length == 1) {
              storage.delete(keys[0]);
          } else if (keys.length > 0) {
              storage.delete(keys);
          }
      }
  
      @AutoreleasePool
      public void clear() {
          storage.clear();
      }
  
      @AutoreleasePool
      public void loadItem(long key, LoadItemCallback<T> callback) {
          ListEngineRecord record = storage.loadItem(key);
          if (record != null) {
              try {
                  T res = Bser.parse(creator.createInstance(), record.getData());
                  callback.onLoaded(res);
              } catch (IOException e) {
                  e.printStackTrace();
                  callback.onLoaded(null);
              }
          } else {
              callback.onLoaded(null);
          }
      }
  
      @AutoreleasePool
      public void loadHead(LoadItemCallback<T> callback) {
          List<ListEngineRecord> records = storage.loadForward(null, 1);
  
          if (records.size() != 1) {
              callback.onLoaded(null);
              return;
          }
  
          ListEngineRecord record = records.get(0);
          try {
              callback.onLoaded(Bser.parse(creator.createInstance(), record.getData()));
          } catch (IOException e) {
              e.printStackTrace();
              callback.onLoaded(null);
          }
      }
  
      @AutoreleasePool
      public void loadCount(LoadCountCallback callback) {
          callback.onLoaded(storage.getCount());
      }
  
      @AutoreleasePool
      public void loadForward(String query, Long topSortKey, int limit, ListEngineDisplayLoadCallback<T> callback) {
          ArrayList<T> res;
          if (query == null) {
              res = convertList(storage.loadForward(topSortKey, limit));
          } else {
              res = convertList(storage.loadForward(query, topSortKey, limit));
          }
  
          callCallback(callback, res);
      }
  
      @AutoreleasePool
      public void loadBackward(String query, Long topSortKey, int limit, ListEngineDisplayLoadCallback<T> callback) {
          ArrayList<T> res;
          if (query == null) {
              res = convertList(storage.loadBackward(topSortKey, limit));
          } else {
              res = convertList(storage.loadBackward(query, topSortKey, limit));
          }
  
          callCallback(callback, res);
      }
  
      @AutoreleasePool
      public void loadCenter(Long centerSortKey, int limit, ListEngineDisplayLoadCallback<T> callback) {
          ArrayList<T> res;
          res = convertList(storage.loadCenter(centerSortKey, limit));
          callCallback(callback, res);
      }
  
      @AutoreleasePool
      private void callCallback(ListEngineDisplayLoadCallback<T> callback, List<T> res) {
          if (res.size() == 0) {
              callback.onLoaded(res, 0, 0);
          } else {
              long topSort, bottomSort;
              topSort = bottomSort = res.get(0).getEngineSort();
  
              for (T t : res) {
                  long sort = t.getEngineSort();
                  if (topSort < sort) {
                      topSort = sort;
                  }
                  if (bottomSort > sort) {
                      bottomSort = sort;
                  }
              }
  
              callback.onLoaded(res, topSort, bottomSort);
          }
      }
  
      private ArrayList<T> convertList(List<ListEngineRecord> records) {
          ArrayList<T> res = new ArrayList<T>();
          for (ListEngineRecord record : records) {
              try {
                  T loaded = Bser.parse(creator.createInstance(), record.getData());
                  res.add(loaded);
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
          return res;
      }
  
      @Override
      @AutoreleasePool
      public void onReceive(Object message) {
          if (message instanceof AddOrUpdate) {
              addOrUpdate(((AddOrUpdate) message).getItems());
          } else if (message instanceof Remove) {
              remove(((Remove) message).getKeys());
          } else if (message instanceof Clear) {
              clear();
          } else if (message instanceof Replace) {
              replace(((Replace) message).getItems());
          } else if (message instanceof LoadItem) {
              loadItem(((LoadItem) message).getKey(), ((LoadItem) message).getCallback());
          } else if (message instanceof LoadCount) {
              loadCount(((LoadCount) message).getCallback());
          } else if (message instanceof LoadHead) {
              loadHead(((LoadHead) message).getCallback());
          } else if (message instanceof LoadForward) {
              loadForward(((LoadForward) message).getQuery(), ((LoadForward) message).getTopSortKey(),
                      ((LoadForward) message).getLimit(), ((LoadForward) message).getCallback());
          } else if (message instanceof LoadBackward) {
              loadBackward(((LoadBackward) message).getQuery(), ((LoadBackward) message).getTopSortKey(),
                      ((LoadBackward) message).getLimit(), ((LoadBackward) message).getCallback());
          } else if (message instanceof LoadCenter) {
              loadCenter(((LoadCenter) message).getCenterSortKey(),
                      ((LoadCenter) message).getLimit(), ((LoadCenter) message).getCallback());
          } else {
              super.onReceive(message);
          }
      }
  
      public static class AddOrUpdate<T extends BserObject & ListEngineItem> {
          private List<T> items;
  
          public AddOrUpdate(List<T> items) {
              this.items = items;
          }
  
          public List<T> getItems() {
              return items;
          }
      }
  
      public static class Replace<T extends BserObject & ListEngineItem> {
          private List<T> items;
  
          public Replace(List<T> items) {
              this.items = items;
          }
  
          public List<T> getItems() {
              return items;
          }
      }
  
      public static class Remove {
          private long[] keys;
  
          public Remove(long[] keys) {
              this.keys = keys;
          }
  
          public long[] getKeys() {
              return keys;
          }
      }
  
      public static class Clear {
  
      }
  
      public static class LoadItem<T extends BserObject & ListEngineItem> {
          private long key;
          private LoadItemCallback<T> callback;
  
          public LoadItem(long key, LoadItemCallback<T> callback) {
              this.key = key;
              this.callback = callback;
          }
  
          public long getKey() {
              return key;
          }
  
          public LoadItemCallback<T> getCallback() {
              return callback;
          }
      }
  
      public static class LoadCount {
          private LoadCountCallback callback;
  
          public LoadCount(LoadCountCallback callback) {
              this.callback = callback;
          }
  
          public LoadCountCallback getCallback() {
              return callback;
          }
      }
  
      public static class LoadHead<T extends BserObject & ListEngineItem> {
          private LoadItemCallback<T> callback;
  
          public LoadHead(LoadItemCallback<T> callback) {
              this.callback = callback;
          }
  
          public LoadItemCallback<T> getCallback() {
              return callback;
          }
      }
  
      public static class LoadForward<T extends BserObject & ListEngineItem> {
          private String query;
          private Long topSortKey;
          private int limit;
          private ListEngineDisplayLoadCallback<T> callback;
  
          public LoadForward(String query, Long topSortKey, int limit, ListEngineDisplayLoadCallback<T> callback) {
              this.query = query;
              this.topSortKey = topSortKey;
              this.limit = limit;
              this.callback = callback;
          }
  
          public String getQuery() {
              return query;
          }
  
          public Long getTopSortKey() {
              return topSortKey;
          }
  
          public int getLimit() {
              return limit;
          }
  
          public ListEngineDisplayLoadCallback<T> getCallback() {
              return callback;
          }
      }
  
      public static class LoadBackward<T extends BserObject & ListEngineItem> {
          private String query;
          private Long topSortKey;
          private int limit;
          private ListEngineDisplayLoadCallback<T> callback;
  
          public LoadBackward(String query, Long topSortKey, int limit, ListEngineDisplayLoadCallback<T> callback) {
              this.query = query;
              this.topSortKey = topSortKey;
              this.limit = limit;
              this.callback = callback;
          }
  
          public String getQuery() {
              return query;
          }
  
          public Long getTopSortKey() {
              return topSortKey;
          }
  
          public int getLimit() {
              return limit;
          }
  
          public ListEngineDisplayLoadCallback<T> getCallback() {
              return callback;
          }
      }
  
      public static class LoadCenter<T extends BserObject & ListEngineItem> {
          private Long centerSortKey;
          private int limit;
          private ListEngineDisplayLoadCallback<T> callback;
  
          public LoadCenter(Long centerSortKey, int limit, ListEngineDisplayLoadCallback<T> callback) {
              this.centerSortKey = centerSortKey;
              this.limit = limit;
              this.callback = callback;
          }
  
          public Long getCenterSortKey() {
              return centerSortKey;
          }
  
          public int getLimit() {
              return limit;
          }
  
          public ListEngineDisplayLoadCallback<T> getCallback() {
              return callback;
          }
      }
  
      public interface LoadItemCallback<T extends BserObject & ListEngineItem> {
          void onLoaded(T item);
      }
  
      public interface LoadCountCallback {
          void onLoaded(int count);
      }
  }
  