class RemoteSerializer {

  public WatchChange decodeWatchChange(ListenResponse protoChange) {
    WatchChange watchChange;

    switch (protoChange.getResponseTypeCase()) {
      case TARGET_CHANGE:
        com.google.firestore.v1.TargetChange targetChange = protoChange.getTargetChange();
        WatchTargetChangeType changeType;
        Status cause = null;
        switch (targetChange.getTargetChangeType()) {
          case NO_CHANGE:
            changeType = WatchTargetChangeType.NoChange;
            break;
          case ADD:
            changeType = WatchTargetChangeType.Added;
            break;
          case REMOVE:
            changeType = WatchTargetChangeType.Removed;
            cause = fromStatus(targetChange.getCause());
            break;
          case CURRENT:
            changeType = WatchTargetChangeType.Current;
            break;
          case RESET:
            changeType = WatchTargetChangeType.Reset;
            break;
          case UNRECOGNIZED:
          default:
            throw new IllegalArgumentException("Unknown target change type");
        }
        watchChange =
            new WatchTargetChange(
                changeType, targetChange.getTargetIdsList(), targetChange.getResumeToken(), cause);
        break;
      case DOCUMENT_CHANGE:
        DocumentChange docChange = protoChange.getDocumentChange();
        List<Integer> added = docChange.getTargetIdsList();
        List<Integer> removed = docChange.getRemovedTargetIdsList();
        DocumentKey key = decodeKey(docChange.getDocument().getName());
        SnapshotVersion version = decodeVersion(docChange.getDocument().getUpdateTime());
        hardAssert(
            !version.equals(SnapshotVersion.NONE), "Got a document change without an update time");
        ObjectValue data = ObjectValue.fromMap(docChange.getDocument().getFieldsMap());
        Document document = new Document(key, version, data, Document.DocumentState.SYNCED);
        watchChange = new WatchChange.DocumentChange(added, removed, document.getKey(), document);
        break;
      case DOCUMENT_DELETE:
        DocumentDelete docDelete = protoChange.getDocumentDelete();
        removed = docDelete.getRemovedTargetIdsList();
        key = decodeKey(docDelete.getDocument());
        // Note that version might be unset in which case we use SnapshotVersion.NONE
        version = decodeVersion(docDelete.getReadTime());
        NoDocument doc = new NoDocument(key, version, /*hasCommittedMutations=*/ false);
        watchChange =
            new WatchChange.DocumentChange(Collections.emptyList(), removed, doc.getKey(), doc);
        break;
      case DOCUMENT_REMOVE:
        DocumentRemove docRemove = protoChange.getDocumentRemove();
        removed = docRemove.getRemovedTargetIdsList();
        key = decodeKey(docRemove.getDocument());
        watchChange = new WatchChange.DocumentChange(Collections.emptyList(), removed, key, null);
        break;
      case FILTER:
        com.google.firestore.v1.ExistenceFilter protoFilter = protoChange.getFilter();
        // TODO: implement existence filter parsing (see b/33076578)
        ExistenceFilter filter = new ExistenceFilter(protoFilter.getCount());
        int targetId = protoFilter.getTargetId();
        watchChange = new ExistenceFilterWatchChange(targetId, filter);
        break;
      case RESPONSETYPE_NOT_SET:
      default:
        throw new IllegalArgumentException("Unknown change type set");
    }

    return watchChange;
  }

  public  RemoteSerializer(DatabaseId databaseId);

}