/*
 * Copyright (C) 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 android.car.test.util;

import android.annotation.Nullable;
import android.car.ICarResultReceiver;
import android.os.Bundle;
import android.util.Log;

import com.android.internal.util.Preconditions;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * Implementation of {@link ICarResultReceiver} that blocks waiting for the result.
 */
public final class BlockingResultReceiver extends ICarResultReceiver.Stub {

    private static final String TAG = BlockingResultReceiver.class.getSimpleName();

    private final CountDownLatch mLatch = new CountDownLatch(1);
    private final long mTimeoutMs;

    private int mResultCode;
    @Nullable private Bundle mResultData;

    /**
     * Default constructor.
     *
     * @param timeoutMs how long to wait for before failing.
     */
    public BlockingResultReceiver(long timeoutMs) {
        mTimeoutMs = timeoutMs;
    }

    @Override
    public void send(int resultCode, Bundle resultData) {
        Log.d(TAG, "send() received: code=" + resultCode + ", data=" + resultData + ", count="
                + mLatch.getCount());
        Preconditions.checkState(mLatch.getCount() == 1,
                "send() already called (code=" + mResultCode + ", data=" + mResultData);
        mResultCode = resultCode;
        mResultData = resultData;
        mLatch.countDown();
    }

    private void assertCalled() throws InterruptedException {
        boolean called = mLatch.await(mTimeoutMs, TimeUnit.MILLISECONDS);
        Log.d(TAG, "assertCalled(): " + called);
        Preconditions.checkState(called, "receiver not called in " + mTimeoutMs + " ms");
    }

    /**
     * Gets the {@code resultCode} or fails if it times out before {@code send()} is called.
     */
    public int getResultCode() throws InterruptedException {
        assertCalled();
        return mResultCode;
    }

    /**
     * Gets the {@code resultData} or fails if it times out before {@code send()} is called.
     */
    @Nullable
    public Bundle getResultData() throws InterruptedException {
        assertCalled();
        return mResultData;
    }
}
