/*
 * Copyright 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.paging.samples.java;

import androidx.annotation.experimental.UseExperimental;
import androidx.concurrent.futures.ResolvableFuture;
import androidx.paging.ExperimentalPagingApi;
import androidx.paging.ListenableFutureRemoteMediator;
import androidx.paging.LoadType;
import androidx.paging.PagingState;
import androidx.paging.samples.shared.ExampleGuavaBackendService;
import androidx.paging.samples.shared.RemoteKey;
import androidx.paging.samples.shared.RemoteKeyDao;
import androidx.paging.samples.shared.RoomDb;
import androidx.paging.samples.shared.User;
import androidx.paging.samples.shared.UserDao;

import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;

import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.util.concurrent.Executor;

import retrofit2.HttpException;

@SuppressWarnings("unused")
@UseExperimental(markerClass = ExperimentalPagingApi.class)
class RemoteMediatorGuavaPageKeyedSample extends ListenableFutureRemoteMediator<Integer, User> {
    private String mQuery;
    private ExampleGuavaBackendService mNetworkService;
    private RoomDb mDatabase;
    private UserDao mUserDao;
    private RemoteKeyDao mRemoteKeyDao;
    private Executor mBgExecutor;

    RemoteMediatorGuavaPageKeyedSample(String query, ExampleGuavaBackendService networkService,
            RoomDb database, Executor bgExecutor) {
        mQuery = query;
        mNetworkService = networkService;
        mDatabase = database;
        mUserDao = database.userDao();
        mRemoteKeyDao = database.remoteKeyDao();
        mBgExecutor = bgExecutor;
    }

    @NotNull
    @Override
    public ListenableFuture<MediatorResult> loadFuture(@NotNull LoadType loadType,
            @NotNull PagingState<Integer, User> state) {
        // The network load method takes an optional `after=<user.id>` parameter. For every
        // page after the first, we pass the last user ID to let it continue from where it
        // left off. For REFRESH, pass `null` to load the first page.
        ResolvableFuture<RemoteKey> remoteKeyFuture = ResolvableFuture.create();
        switch (loadType) {
            case REFRESH:
                remoteKeyFuture.set(new RemoteKey(mQuery, null));
                break;
            case PREPEND:
                // In this example, we never need to prepend, since REFRESH will always load the
                // first page in the list. Immediately return, reporting end of pagination.
                return Futures.immediateFuture(new MediatorResult.Success(true));
            case APPEND:
                User lastItem = state.lastItemOrNull();

                // We must explicitly check if the last item is `null` when appending,
                // since passing `null` to networkService is only valid for initial load.
                // If lastItem is `null` it means no items were loaded after the initial
                // REFRESH and there are no more items to load.
                if (lastItem == null) {
                    return Futures.immediateFuture(new MediatorResult.Success(true));
                }

                // Query remoteKeyDao for the next RemoteKey.
                remoteKeyFuture.setFuture(mRemoteKeyDao.remoteKeyByQueryFuture(mQuery));
                break;
        }

        return Futures.transformAsync(remoteKeyFuture, remoteKey -> {
            // We must explicitly check if the page key is `null` when appending,
            // since `null` is only valid for initial load. If we receive `null`
            // for APPEND, that means we have reached the end of pagination and
            // there are no more items to load.
            if (loadType != LoadType.REFRESH && remoteKey.getNextKey() == null) {
                return Futures.immediateFuture(new MediatorResult.Success(true));
            }

            ListenableFuture<MediatorResult> networkResult = Futures.transform(
                    mNetworkService.searchUsers(mQuery, remoteKey.getNextKey()),
                    response -> {
                        mDatabase.runInTransaction(() -> {
                            if (loadType == LoadType.REFRESH) {
                                mUserDao.deleteByQuery(mQuery);
                                mRemoteKeyDao.deleteByQuery(mQuery);
                            }

                            // Update RemoteKey for this query.
                            mRemoteKeyDao.insertOrReplace(
                                    new RemoteKey(mQuery, response.getNextKey()));

                            // Insert new users into database, which invalidates the current
                            // PagingData, allowing Paging to present the updates in the DB.
                            mUserDao.insertAll(response.getUsers());
                        });

                        return new MediatorResult.Success(response.getNextKey() == null);
                    }, mBgExecutor);

            ListenableFuture<MediatorResult> ioCatchingNetworkResult = Futures.catching(
                    networkResult, IOException.class, MediatorResult.Error::new,
                    mBgExecutor);

            return Futures.catching(ioCatchingNetworkResult, HttpException.class,
                    MediatorResult.Error::new, mBgExecutor);

        }, mBgExecutor);
    }
}

@@ -710,61 +710,8 @@
 al;%0A
-import androidx.concurrent.futures.ResolvableFuture;%0A
 impo
@@ -2638,22 +2638,22 @@
         
-Resolv
+Listen
 ableFutu
@@ -2688,33 +2688,12 @@
 e = 
-ResolvableFuture.create()
+null
 ;%0A  
@@ -2775,20 +2775,42 @@
 eyFuture
-.set
+ = Futures.immediateFuture
 (new Rem
@@ -3806,19 +3806,11 @@
 ture
-.setFuture(
+ = 
 mRem
@@ -3849,17 +3849,16 @@
 (mQuery)
-)
 ;%0A      
@@ -3877,32 +3877,72 @@
 eak;%0A        %7D%0A%0A
+        //noinspection UnstableApiUsage%0A
         return F
@@ -3991,24 +3991,262 @@
 oteKey -%3E %7B%0A
+            if (remoteKey == null) %7B%0A                throw new IllegalStateException(%22RemoteKey should never be null, since we would %22%0A                        + %22normally return MediatorResult.Success(true) immediately.%22);%0A            %7D%0A%0A
             
@@ -4695,32 +4695,76 @@
 %0A            %7D%0A%0A
+            //noinspection UnstableApiUsage%0A
             List
@@ -4938,24 +4938,197 @@
 sponse -%3E %7B%0A
+                        if (response == null) %7B%0A                            throw new IllegalArgumentException(%22Response from network was null%22);%0A                        %7D%0A%0A
             
@@ -6013,32 +6013,82 @@
  mBgExecutor);%0A%0A
+            @SuppressWarnings(%22UnstableApiUsage%22)%0A
             List
@@ -6268,32 +6268,76 @@
  mBgExecutor);%0A%0A
+            //noinspection UnstableApiUsage%0A
             retu

Fix lint errors in paging:samples