content: /*
   * Copyright (C) 2015 Actor LLC. <https://actor.im>
   */
  
  package im.actor.runtime.js.storage;
  
  import com.google.gwt.storage.client.Storage;
  
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.Comparator;
  import java.util.List;
  
  import im.actor.runtime.Log;
  import im.actor.runtime.bser.DataInput;
  import im.actor.runtime.bser.DataOutput;
  import im.actor.runtime.storage.ListEngineRecord;
  import im.actor.runtime.storage.ListStorage;
  
  import static im.actor.runtime.crypto.Base64Utils.fromBase64;
  import static im.actor.runtime.crypto.Base64Utils.toBase64;
  
  public class JsListStorage implements ListStorage {
  
      private static final String TAG = "JsListStorage";
  
      private final Storage storage;
      private final String prefix;
      private final ArrayList<Index> index = new ArrayList<Index>();
      private final Comparator<Index> comparator = new Comparator<Index>() {
          @Override
          public int compare(Index o1, Index o2) {
              return -compare(o1.getSortKey(), o2.getSortKey());
          }
  
          int compare(long x, long y) {
              return (x < y) ? -1 : ((x == y) ? 0 : 1);
          }
      };
  
      public JsListStorage(String prefix, Storage storage) {
          this.storage = storage;
          this.prefix = prefix;
  
          String indexData = storage.getItem("list_" + prefix + "_index");
          if (indexData != null) {
              try {
                  byte[] data = fromBase64(indexData);
                  DataInput dataInput = new DataInput(data, 0, data.length);
                  int count = dataInput.readInt();
                  for (int i = 0; i < count; i++) {
                      long id = dataInput.readLong();
                      long order = dataInput.readLong();
                      index.add(new Index(id, order));
                  }
              } catch (Exception e) {
                  Log.e(TAG, e);
              }
          }
  
          updateIndex();
      }
  
      @Override
      public void updateOrAdd(ListEngineRecord record) {
          // Update Index
          for (Index i : index) {
              if (i.getId() == record.getKey()) {
                  index.remove(i);
                  break;
              }
          }
          index.add(new Index(record.getKey(), record.getOrder()));
          updateIndex();
  
          // Save record
          storage.setItem(getId(record.getKey()), toBase64(record.getData()));
      }
  
      @Override
      public void updateOrAdd(List<ListEngineRecord> items) {
          // Update Index
          for (ListEngineRecord record : items) {
              for (Index i : index) {
                  if (i.getId() == record.getKey()) {
                      index.remove(i);
                      break;
                  }
              }
              index.add(new Index(record.getKey(), record.getOrder()));
          }
          updateIndex();
  
          // Save records
          for (ListEngineRecord record : items) {
              storage.setItem(getId(record.getKey()), toBase64(record.getData()));
          }
      }
  
      @Override
      public void delete(long key) {
          for (Index i : index) {
              if (i.getId() == key) {
                  index.remove(i);
                  storage.removeItem(getId(key));
                  updateIndex();
                  break;
              }
          }
      }
  
      @Override
      public void delete(long[] keys) {
          for (long key : keys) {
              for (Index i : index) {
                  if (i.getId() == key) {
                      index.remove(i);
                      storage.removeItem(getId(key));
                      updateIndex();
                      break;
                  }
              }
          }
      }
  
      @Override
      public void clear() {
          for (Index i : index) {
              storage.removeItem(getId(i.getId()));
          }
          index.clear();
          updateIndex();
      }
  
      @Override
      public ListEngineRecord loadItem(long key) {
          Index indexValue = null;
          for (Index i : index) {
              if (i.getId() == key) {
                  indexValue = i;
                  break;
              }
          }
  
          if (indexValue == null) {
              return null;
          }
  
          String item = storage.getItem(getId(key));
          if (item != null) {
              byte[] res = fromBase64(item);
              return new ListEngineRecord(key, indexValue.getSortKey(), null, res);
          }
          return null;
      }
  
      @Override
      public List<ListEngineRecord> loadAllItems() {
          // TODO: Implement
          return null;
      }
  
      @Override
      public boolean isEmpty() {
          return getCount() == 0;
      }
  
      @Override
      public int getCount() {
          return index.size();
      }
  
      public Long getHeadId() {
          if (index.size() > 0) {
              return index.get(0).getId();
          } else {
              return null;
          }
      }
  
      public long[] getOrderedIds() {
          long[] res = new long[index.size()];
          for (int i = 0; i < res.length; i++) {
              res[i] = index.get(i).getId();
          }
          return res;
      }
  
      public long[] getPrevIdsInclusive(long sortKey) {
          ArrayList<Long> res = new ArrayList<Long>();
          for (Index i : index) {
              if (i.getSortKey() <= sortKey) {
                  res.add(i.getId());
              }
          }
          long[] res2 = new long[res.size()];
          for (int i = 0; i < res2.length; i++) {
              res2[i] = res.get(i);
          }
          return res2;
      }
  
      public long[] getPrevIdsExclusive(long sortKey) {
          ArrayList<Long> res = new ArrayList<Long>();
          for (Index i : index) {
              if (i.getSortKey() < sortKey) {
                  res.add(i.getId());
              }
          }
          long[] res2 = new long[res.size()];
          for (int i = 0; i < res2.length; i++) {
              res2[i] = res.get(i);
          }
          return res2;
      }
  
      private String getId(long id) {
          return "list_" + prefix + "_" + id;
      }
  
      private void updateIndex() {
          Collections.sort(index, comparator);
          DataOutput dataOutput = new DataOutput();
          dataOutput.writeInt(index.size());
  
          for (Index i : index) {
              dataOutput.writeLong(i.getId());
              dataOutput.writeLong(i.getSortKey());
          }
  
          storage.setItem("list_" + prefix + "_index", toBase64(dataOutput.toByteArray()));
      }
  
      private class Index {
          private long id;
          private long sortKey;
  
          private Index(long id, long sortKey) {
              this.id = id;
              this.sortKey = sortKey;
          }
  
          public long getId() {
              return id;
          }
  
          public long getSortKey() {
              return sortKey;
          }
      }
  }