static jstring AlphabeticIndex_getBucketLabel(JNIEnv* env, jclass, jlong peer, jint index) {
  if (index < 0) {
    jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "Invalid index: %d", index);
    return NULL;
  }

  // Iterate to the nth bucket.
  AlphabeticIndex* ai = fromPeer(peer);
  UErrorCode status = U_ZERO_ERROR;
  ai->resetBucketIterator(status);
  if (maybeThrowIcuException(env, "AlphabeticIndex::resetBucketIterator", status)) {
    return NULL;
  }
  for (jint i = 0; i <= index; ++i) {
    if (!ai->nextBucket(status)) {
      jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "Invalid index: %d", index);
      return NULL;
    }
    if (maybeThrowIcuException(env, "AlphabeticIndex::nextBucket", status)) {
      return NULL;
    }
  }

  // Return "" for the underflow/inflow/overflow buckets.
  if (ai->getBucketLabelType() != U_ALPHAINDEX_NORMAL) {
    return env->NewStringUTF("");
  }

  const UnicodeString& label(ai->getBucketLabel());
  return env->NewString(label.getBuffer(), label.length());
}
static void AlphabeticIndex_addLabelRange(JNIEnv* env, jclass, jlong peer,
                                          jint codePointStart, jint codePointEnd) {
  AlphabeticIndex* ai = fromPeer(peer);
  UErrorCode status = U_ZERO_ERROR;
  ai->addLabels(UnicodeSet(codePointStart, codePointEnd), status);
  maybeThrowIcuException(env, "AlphabeticIndex::addLabels", status);
}
static jint AlphabeticIndex_getBucketCount(JNIEnv* env, jclass, jlong peer) {
  AlphabeticIndex* ai = fromPeer(peer);
  UErrorCode status = U_ZERO_ERROR;
  jint result = ai->getBucketCount(status);
  if (maybeThrowIcuException(env, "AlphabeticIndex::getBucketCount", status)) {
    return -1;
  }
  return result;
}
static jlong AlphabeticIndex_buildImmutableIndex(JNIEnv* env, jclass, jlong peer) {
  AlphabeticIndex* ai = fromPeer(peer);
  UErrorCode status = U_ZERO_ERROR;
  AlphabeticIndex::ImmutableIndex* ii = ai->buildImmutableIndex(status);
  if (maybeThrowIcuException(env, "AlphabeticIndex::buildImmutableIndex", status)) {
    return 0;
  }
  return reinterpret_cast<uintptr_t>(ii);
}
static jstring Transliterator_transliterate(JNIEnv* env, jclass, jlong peer, jstring javaString) {
  Transliterator* t = fromPeer(peer);
  ScopedJavaUnicodeString string(env, javaString);
  if (!string.valid()) {
    return NULL;
  }

  UnicodeString& s(string.unicodeString());
  t->transliterate(s);
  return env->NewString(s.getBuffer(), s.length());
}
static jint AlphabeticIndex_getBucketIndex(JNIEnv* env, jclass, jlong peer, jstring javaString) {
  AlphabeticIndex* ai = fromPeer(peer);
  ScopedJavaUnicodeString string(env, javaString);
  if (!string.valid()) {
    return -1;
  }
  UErrorCode status = U_ZERO_ERROR;
  jint result = ai->getBucketIndex(string.unicodeString(), status);
  if (maybeThrowIcuException(env, "AlphabeticIndex::getBucketIndex", status)) {
    return -1;
  }
  return result;
}
static void Transliterator_destroy(JNIEnv*, jclass, jlong peer) {
  delete fromPeer(peer);
}
static void AlphabeticIndex_addLabels(JNIEnv* env, jclass, jlong peer, jstring javaLocale) {
  AlphabeticIndex* ai = fromPeer(peer);
  UErrorCode status = U_ZERO_ERROR;
  ai->addLabels(getLocale(env, javaLocale), status);
  maybeThrowIcuException(env, "AlphabeticIndex::addLabels", status);
}
static void AlphabeticIndex_destroy(JNIEnv*, jclass, jlong peer) {
  delete fromPeer(peer);
}
MessagesMessages TelegramCache::readMessages(const Peer &peer, int offset, int limit) const
{
    MessagesMessages result(MessagesMessages::typeMessagesMessages);

    const QString folderPath = getMessageFolder(peer);
    QStringList files = QDir(folderPath).entryList(QDir::Files);
    qStableSort(files.begin(), files.end(), fileListDeSort);

    files = files.mid(offset, limit);

    QHash<QByteArray, Chat> chats;
    QHash<QByteArray, User> users;

    QList<Message> messages;
    Q_FOREACH(const QString &f, files)
    {
        const QString path = folderPath + "/" + f;
        const QMap<QString, QVariant> &map = readMap(path);
        if(map.isEmpty())
            continue;

        const Message &msg = Message::fromMap(map);
        const Peer &toPeer = msg.toId();
        const QByteArray &toKey = TelegramTools::identifier(toPeer);
        switch(static_cast<int>(toPeer.classType()))
        {
        case Peer::typePeerChannel:
        case Peer::typePeerChat:
            if(!chats.contains(toKey))
                chats[toKey] = readChat(toPeer);
            break;
        case Peer::typePeerUser:
            if(!users.contains(toKey))
                users[toKey] = readUser(toPeer);
            break;
        }

        if(msg.fromId())
        {
            Peer fromPeer(Peer::typePeerUser);
            fromPeer.setUserId(msg.fromId());
            const QByteArray &fromKey = TelegramTools::identifier(fromPeer);
            switch(static_cast<int>(fromPeer.classType()))
            {
            case Peer::typePeerChannel:
            case Peer::typePeerChat:
                if(!chats.contains(fromKey))
                    chats[fromKey] = readChat(fromPeer);
                break;
            case Peer::typePeerUser:
                if(!users.contains(fromKey))
                    users[fromKey] = readUser(fromPeer);
                break;
            }
        }

        messages << msg;
    }

    result.setMessages(messages);
    result.setChats(chats.values());
    result.setUsers(users.values());
    result.setCount(messages.count());

    return result;
}
static void AlphabeticIndex_setMaxLabelCount(JNIEnv* env, jclass, jlong peer, jint count) {
  AlphabeticIndex* ai = fromPeer(peer);
  UErrorCode status = U_ZERO_ERROR;
  ai->setMaxLabelCount(count, status);
  maybeThrowIcuException(env, "AlphabeticIndex::setMaxLabelCount", status);
}
static jint AlphabeticIndex_getMaxLabelCount(JNIEnv*, jclass, jlong peer) {
  AlphabeticIndex* ai = fromPeer(peer);
  return ai->getMaxLabelCount();
}