/*
 * Copyright 2022 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.camera.viewfinder;

import static android.hardware.camera2.CameraMetadata.LENS_FACING_BACK;
import static android.hardware.camera2.CameraMetadata.LENS_FACING_EXTERNAL;
import static android.hardware.camera2.CameraMetadata.LENS_FACING_FRONT;

import android.annotation.SuppressLint;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraMetadata;
import android.util.Size;
import android.view.Surface;
import android.view.SurfaceView;
import android.view.TextureView;

import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
import androidx.camera.viewfinder.CameraViewfinder.ImplementationMode;
import androidx.camera.viewfinder.internal.surface.ViewfinderSurface;
import androidx.camera.viewfinder.internal.utils.Logger;
import androidx.camera.viewfinder.internal.utils.executor.CameraExecutors;
import androidx.camera.viewfinder.internal.utils.futures.FutureCallback;
import androidx.camera.viewfinder.internal.utils.futures.Futures;
import androidx.concurrent.futures.CallbackToFutureAdapter;
import androidx.core.util.Consumer;
import androidx.core.util.Preconditions;

import com.google.auto.value.AutoValue;
import com.google.common.util.concurrent.ListenableFuture;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;

/**
 * The request to get a {@link Surface} to display camera feed.
 *
 * <p> This request contains requirements for the surface resolution and camera
 * device information from {@link CameraCharacteristics}.
 *
 * <p> Calling {@link CameraViewfinder#requestSurfaceAsync(ViewfinderSurfaceRequest)} with this
 * request will send the request to the surface provider, which is either a {@link TextureView} or
 * {@link SurfaceView} and get a {@link ListenableFuture} of {@link Surface}.
 *
 * <p> Calling {@link ViewfinderSurfaceRequest#markSurfaceSafeToRelease()} will notify the
 * surface provider that the surface is not needed and related resources can be released.
 */
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
public class ViewfinderSurfaceRequest {

    private static final String TAG = "ViewfinderSurfaceRequest";

    @NonNull private final Size mResolution;
    @NonNull private final ViewfinderSurface mInternalViewfinderSurface;
    @NonNull private final CallbackToFutureAdapter.Completer<Void> mRequestCancellationCompleter;
    @NonNull private final ListenableFuture<Void> mSessionStatusFuture;
    @NonNull private final CallbackToFutureAdapter.Completer<Surface> mSurfaceCompleter;
    @LensFacingValue private int mLensFacing;
    @SensorOrientationDegreesValue private int mSensorOrientation;
    @Nullable
    private ImplementationMode mImplementationMode;
    @SuppressWarnings("WeakerAccess") /*synthetic accessor */
    @NonNull
    final ListenableFuture<Surface> mSurfaceFuture;

    /**
     * Creates a new surface request with surface resolution, camera device, lens facing and
     * sensor orientation information.
     *
     * @param resolution The requested surface resolution. It is the output surface size
     *                   the camera is configured with, instead of {@link CameraViewfinder}
     *                   view size.
     * @param lensFacing The camera lens facing.
     * @param sensorOrientation THe camera sensor orientation.
     * @param implementationMode The {@link ImplementationMode} to apply to the viewfinder.
     */
    ViewfinderSurfaceRequest(
            @NonNull Size resolution,
            @LensFacingValue int lensFacing,
            @SensorOrientationDegreesValue int sensorOrientation,
            @Nullable ImplementationMode implementationMode) {
        mResolution = resolution;
        mLensFacing = lensFacing;
        mSensorOrientation = sensorOrientation;
        mImplementationMode = implementationMode;

        // To ensure concurrency and ordering, operations are chained. Completion can only be
        // triggered externally by the top-level completer (mSurfaceCompleter). The other future
        // completers are only completed by callbacks set up within the constructor of this class
        // to ensure correct ordering of events.

        // Cancellation listener must be called last to ensure the result can be retrieved from
        // the session listener.
        String surfaceRequestString =
                "SurfaceRequest[size: " + resolution + ", id: " + this.hashCode() + "]";
        AtomicReference<CallbackToFutureAdapter.Completer<Void>> cancellationCompleterRef =
                new AtomicReference<>(null);
        ListenableFuture<Void> requestCancellationFuture =
                CallbackToFutureAdapter.getFuture(completer -> {
                    cancellationCompleterRef.set(completer);
                    return surfaceRequestString + "-cancellation";
                });
        CallbackToFutureAdapter.Completer<Void> requestCancellationCompleter =
                Preconditions.checkNotNull(cancellationCompleterRef.get());
        mRequestCancellationCompleter = requestCancellationCompleter;

        // Surface session status future completes and is responsible for finishing the
        // cancellation listener.
        AtomicReference<CallbackToFutureAdapter.Completer<Void>> sessionStatusCompleterRef =
                new AtomicReference<>(null);
        mSessionStatusFuture = CallbackToFutureAdapter.getFuture(completer -> {
            sessionStatusCompleterRef.set(completer);
            return surfaceRequestString + "-status";
        });

        Futures.addCallback(mSessionStatusFuture, new FutureCallback<Void>() {
            @Override
            public void onSuccess(@Nullable Void result) {
                // Cancellation didn't occur, so complete the cancellation future. There
                // shouldn't ever be any standard listeners on this future, so nothing should be
                // invoked.
                Preconditions.checkState(requestCancellationCompleter.set(null));
            }

            @Override
            public void onFailure(@NonNull Throwable t) {
                if (t instanceof RequestCancelledException) {
                    // Cancellation occurred. Notify listeners.
                    Preconditions.checkState(requestCancellationFuture.cancel(false));
                } else {
                    // Cancellation didn't occur, complete the future so cancellation listeners
                    // are not invoked.
                    Preconditions.checkState(requestCancellationCompleter.set(null));
                }
            }
        }, CameraExecutors.directExecutor());

        // Create the surface future/completer. This will be used to complete the rest of the
        // future chain and can be set externally via SurfaceRequest methods.
        CallbackToFutureAdapter.Completer<Void> sessionStatusCompleter =
                Preconditions.checkNotNull(sessionStatusCompleterRef.get());
        AtomicReference<CallbackToFutureAdapter.Completer<Surface>> surfaceCompleterRef =
                new AtomicReference<>(null);
        mSurfaceFuture = CallbackToFutureAdapter.getFuture(completer -> {
            surfaceCompleterRef.set(completer);
            return surfaceRequestString + "-Surface";
        });
        mSurfaceCompleter = Preconditions.checkNotNull(surfaceCompleterRef.get());

        // Create the viewfinder surface which will be used for communicating when the
        // camera and consumer are done using the surface. Note this anonymous inner class holds
        // an implicit reference to the ViewfinderSurfaceRequest. This is by design, and ensures the
        // ViewfinderSurfaceRequest and all contained future completers will not be garbage
        // collected as long as the ViewfinderSurface is referenced externally (via
        // getViewfinderSurface()).
        mInternalViewfinderSurface = new ViewfinderSurface() {
            @NonNull
            @Override
            protected ListenableFuture<Surface> provideSurfaceAsync() {
                Logger.d(TAG,
                        "mInternalViewfinderSurface + " + mInternalViewfinderSurface + " "
                                + "provideSurface");
                return mSurfaceFuture;
            }
        };
        ListenableFuture<Void> terminationFuture =
                mInternalViewfinderSurface.getTerminationFuture();

        // Propagate surface completion to the session future.
        Futures.addCallback(mSurfaceFuture, new FutureCallback<Surface>() {
            @Override
            public void onSuccess(@Nullable Surface surface) {
                // On successful setting of a surface, defer completion of the session future to
                // the ViewfinderSurface termination future. Once that future completes, then it
                // is safe to release the Surface and associated resources.

                Futures.propagate(terminationFuture, sessionStatusCompleter);
            }

            @Override
            public void onFailure(@NonNull Throwable t) {
                // Translate cancellation into a SurfaceRequestCancelledException. Other
                // exceptions mean either the request was completed via willNotProvideSurface() or a
                // programming error occurred. In either case, the user will never see the
                // session future (an immediate future will be returned instead), so complete the
                // future so cancellation listeners are never called.
                if (t instanceof CancellationException) {
                    Preconditions.checkState(sessionStatusCompleter.setException(
                            new RequestCancelledException(
                                    surfaceRequestString + " cancelled.", t)));
                } else {
                    sessionStatusCompleter.set(null);
                }
            }
        }, CameraExecutors.directExecutor());

        // If the viewfinder surface is terminated, there are two cases:
        // 1. The surface has not yet been provided to the camera (or marked as 'will not
        //    complete'). Treat this as if the surface request has been cancelled.
        // 2. The surface was already provided to the camera. In this case the camera is now
        //    finished with the surface, so cancelling the surface future below will be a no-op.
        terminationFuture.addListener(() -> {
            Logger.d(TAG,
                    "mInternalViewfinderSurface + " + mInternalViewfinderSurface + " "
                            + "terminateFuture triggered");
            mSurfaceFuture.cancel(true);
        }, CameraExecutors.directExecutor());
    }

    @Override
    @SuppressWarnings("GenericException") // super.finalize() throws Throwable
    protected void finalize() throws Throwable {
        mInternalViewfinderSurface.close();
        super.finalize();
    }

    /**
     * Returns the resolution of the requested {@link Surface}.
     *
     * <p>The value is set by {@link Builder#Builder(Size)}.
     *
     * The surface which fulfills this request must have the resolution specified here in
     * order to fulfill the resource requirements of the camera.
     *
     * @return The guaranteed supported resolution.
     * @see SurfaceTexture#setDefaultBufferSize(int, int)
     */
    @NonNull
    public Size getResolution() {
        return mResolution;
    }

    /**
     * Returns the sensor orientation.
     *
     * <p>The value is set by {@link Builder#setSensorOrientation(int)}, which can be retrieved from
     * {@link CameraCharacteristics} by key {@link CameraCharacteristics#SENSOR_ORIENTATION}.
     *
     * @return The sensor orientation.
     */
    @SensorOrientationDegreesValue
    public int getSensorOrientation() {
        return mSensorOrientation;
    }

    /**
     * Returns the camera lens facing.
     *
     * <p>The value is set by {@link Builder#setLensFacing(int)}, which can be retrieved from
     * {@link CameraCharacteristics} by key {@link CameraCharacteristics#LENS_FACING}.
     *
     * @return The lens facing.
     */
    @LensFacingValue
    public int getLensFacing() {
        return mLensFacing;
    }

    /**
     * Returns the {@link ImplementationMode}.
     *
     * <p>The value is set by {@link Builder#setImplementationMode(ImplementationMode)}.
     *
     * @return {@link ImplementationMode}. The value will be null if it's not set via
     * {@link Builder#setImplementationMode(ImplementationMode)}.
     */
    @Nullable
    public ImplementationMode getImplementationMode() {
        return mImplementationMode;
    }

    /**
     * Closes the viewfinder surface to mark it as safe to release.
     *
     * <p> This method should be called by the user when the requested surface is not needed and
     * related resources can be released.
     */
    public void markSurfaceSafeToRelease() {
        mInternalViewfinderSurface.close();
    }

    @NonNull
    ViewfinderSurface getViewfinderSurface() {
        return mInternalViewfinderSurface;
    }

    @SuppressLint("PairedRegistration")
    void addRequestCancellationListener(@NonNull Executor executor,
            @NonNull Runnable listener) {
        mRequestCancellationCompleter.addCancellationListener(listener, executor);
    }

    /**
     * Completes the request for a {@link Surface} if it has not already been
     * completed or cancelled.
     *
     * <p>Once the camera no longer needs the provided surface, the {@code resultListener} will be
     * invoked with a {@link Result} containing {@link Result#RESULT_SURFACE_USED_SUCCESSFULLY}.
     * At this point it is safe to release the surface and any underlying resources. Releasing
     * the surface before receiving this signal may cause undesired behavior on lower API levels.
     *
     * <p>If the request is cancelled by the camera before successfully attaching the
     * provided surface to the camera, then the {@code resultListener} will be invoked with a
     * {@link Result} containing {@link Result#RESULT_REQUEST_CANCELLED}.
     *
     * <p>If the request was previously completed via {@link #willNotProvideSurface()}, then
     * {@code resultListener} will be invoked with a {@link Result} containing
     * {@link Result#RESULT_WILL_NOT_PROVIDE_SURFACE}.
     *
     * <p>Upon returning from this method, the surface request is guaranteed to be complete.
     * However, only the {@code resultListener} provided to the first invocation of this method
     * should be used to track when the provided {@link Surface} is no longer in use by the
     * camera, as subsequent invocations will always invoke the {@code resultListener} with a
     * {@link Result} containing {@link Result#RESULT_SURFACE_ALREADY_PROVIDED}.
     *
     * @param surface        The surface which will complete the request.
     * @param executor       Executor used to execute the {@code resultListener}.
     * @param resultListener Listener used to track how the surface is used by the camera in
     *                       response to being provided by this method.
     *
     */
    void provideSurface(
            @NonNull Surface surface,
            @NonNull Executor executor,
            @NonNull Consumer<Result> resultListener) {
        if (mSurfaceCompleter.set(surface) || mSurfaceFuture.isCancelled()) {
            // Session will be pending completion (or surface request was cancelled). Return the
            // session future.
            Futures.addCallback(mSessionStatusFuture, new FutureCallback<Void>() {
                @Override
                public void onSuccess(@Nullable Void result) {
                    resultListener.accept(Result.of(Result.RESULT_SURFACE_USED_SUCCESSFULLY,
                            surface));
                }

                @Override
                public void onFailure(@NonNull Throwable t) {
                    Preconditions.checkState(t instanceof RequestCancelledException, "Camera "
                            + "surface session should only fail with request "
                            + "cancellation. Instead failed due to:\n" + t);
                    resultListener.accept(Result.of(Result.RESULT_REQUEST_CANCELLED, surface));
                }
            }, executor);
        } else {
            // Surface request is already complete
            Preconditions.checkState(mSurfaceFuture.isDone());
            try {
                mSurfaceFuture.get();
                // Getting this far means the surface was already provided.
                executor.execute(
                        () -> resultListener.accept(
                                Result.of(Result.RESULT_SURFACE_ALREADY_PROVIDED, surface)));
            } catch (InterruptedException | ExecutionException e) {
                executor.execute(
                        () -> resultListener.accept(
                                Result.of(Result.RESULT_WILL_NOT_PROVIDE_SURFACE, surface)));
            }
        }
    }

    /**
     * Signals that the request will never be fulfilled.
     *
     * <p>This may be called in the case where the application may be shutting down and a
     * surface will never be produced to fulfill the request.
     *
     * <p>This will be called by CameraViewfinder as soon as it is known that the request will not
     * be fulfilled. Failure to complete the SurfaceRequest via {@code willNotProvideSurface()}
     * or {@link #provideSurface(Surface, Executor, Consumer)} may cause long delays in shutting
     * down the camera.
     *
     * <p>Upon returning from this method, the request is guaranteed to be complete, regardless
     * of the return value. If the request was previously successfully completed by
     * {@link #provideSurface(Surface, Executor, Consumer)}, invoking this method will return
     * {@code false}, and will have no effect on how the surface is used by the camera.
     *
     * @return {@code true} if this call to {@code willNotProvideSurface()} successfully
     * completes the request, i.e., the request has not already been completed via
     * {@link #provideSurface(Surface, Executor, Consumer)} or by a previous call to
     * {@code willNotProvideSurface()} and has not already been cancelled by the camera.
     *
     */
    boolean willNotProvideSurface() {
        return mSurfaceCompleter.setException(
                new ViewfinderSurface.SurfaceUnavailableException("Surface request "
                        + "will not complete."));
    }

    /**
     * Builder for {@link ViewfinderSurfaceRequest}.
     */
    public static final class Builder {

        @NonNull private final Size mResolution;
        @LensFacingValue private int mLensFacing = LENS_FACING_BACK;
        @SensorOrientationDegreesValue private int mSensorOrientation = 0;
        @Nullable private ImplementationMode mImplementationMode;

        /**
         * Constructor for {@link Builder}.
         *
         * <p>Creates a builder with viewfinder resolution.
         *
         * @param resolution viewfinder resolution.
         */
        public Builder(@NonNull Size resolution) {
            mResolution = resolution;
        }

        /**
         * Constructor for {@link Builder}.
         *
         * <p>Creates a builder with other builder instance. The returned builder will be
         * pre-populated with the state of the provided builder.
         *
         * @param builder {@link Builder} instance.
         */
        public Builder(@NonNull Builder builder) {
            mResolution = builder.mResolution;
            mImplementationMode = builder.mImplementationMode;
            mLensFacing = builder.mLensFacing;
            mSensorOrientation = builder.mSensorOrientation;
        }

        /**
         * Constructor for {@link Builder}.
         *
         * <p>Creates a builder with other {@link ViewfinderSurfaceRequest} instance. The
         * returned builder will be pre-populated with the state of the provided
         * {@link ViewfinderSurfaceRequest} instance.
         *
         * @param surfaceRequest {@link ViewfinderSurfaceRequest} instance.
         */
        public Builder(@NonNull ViewfinderSurfaceRequest surfaceRequest) {
            mResolution = surfaceRequest.getResolution();
            mImplementationMode = surfaceRequest.getImplementationMode();
            mLensFacing = surfaceRequest.getLensFacing();
            mSensorOrientation = surfaceRequest.getSensorOrientation();
        }

        /**
         * Sets the {@link ImplementationMode}.
         *
         * <p><b>Possible values:</b></p>
         * <ul>
         *   <li>{@link ImplementationMode#PERFORMANCE PERFORMANCE}</li>
         *   <li>{@link ImplementationMode#COMPATIBLE COMPATIBLE}</li>
         * </ul>
         *
         * <p>If not set or setting to null, the {@link ImplementationMode} set via {@code app
         * :implementationMode} in layout xml will be used for {@link CameraViewfinder}. If not
         * set in the layout xml, the default value {@link ImplementationMode#PERFORMANCE} will
         * be used in {@link CameraViewfinder}.
         *
         * @param implementationMode The {@link ImplementationMode}.
         * @return This builder.
         */
        @NonNull
        public Builder setImplementationMode(@Nullable ImplementationMode implementationMode) {
            mImplementationMode = implementationMode;
            return this;
        }

        /**
         * Sets the lens facing.
         *
         * <p><b>Possible values:</b></p>
         * <ul>
         *   <li>{@link CameraMetadata#LENS_FACING_FRONT FRONT}</li>
         *   <li>{@link CameraMetadata#LENS_FACING_BACK BACK}</li>
         *   <li>{@link CameraMetadata#LENS_FACING_EXTERNAL EXTERNAL}</li>
         * </ul>
         *
         * <p>The value can be retrieved from {@link CameraCharacteristics} by key
         * {@link CameraCharacteristics#LENS_FACING}. If not set,
         * {@link CameraMetadata#LENS_FACING_BACK} will be used by default.
         *
         * @param lensFacing The lens facing.
         * @return This builder.
         */
        @NonNull
        public Builder setLensFacing(@LensFacingValue int lensFacing) {
            mLensFacing = lensFacing;
            return this;
        }

        /**
         * Sets the sensor orientation.
         *
         * <p><b>Range of valid values:</b><br>
         * 0, 90, 180, 270</p>
         *
         * <p>The value can be retrieved from {@link CameraCharacteristics} by key
         * {@link CameraCharacteristics#SENSOR_ORIENTATION}. If it is not
         * set, 0 will be used by default.
         *
         * @param sensorOrientation
         * @return this builder.
         */
        @NonNull
        public Builder setSensorOrientation(@SensorOrientationDegreesValue int sensorOrientation) {
            mSensorOrientation = sensorOrientation;
            return this;
        }

        /**
         * Builds the {@link ViewfinderSurfaceRequest}.
         * @return the instance of {@link ViewfinderSurfaceRequest}.
         */
        @NonNull
        public ViewfinderSurfaceRequest build() {
            if (mLensFacing != LENS_FACING_FRONT
                    && mLensFacing != LENS_FACING_BACK
                    && mLensFacing != LENS_FACING_EXTERNAL) {
                throw new IllegalArgumentException("Lens facing value: " + mLensFacing + " is "
                        + "invalid");
            }

            if (mSensorOrientation != 0
                    && mSensorOrientation != 90
                    && mSensorOrientation != 180
                    && mSensorOrientation != 270) {
                throw new IllegalArgumentException("Sensor orientation value: "
                        + mSensorOrientation + " is invalid");
            }

            return new ViewfinderSurfaceRequest(
                    mResolution,
                    mLensFacing,
                    mSensorOrientation,
                    mImplementationMode);
        }
    }

    static final class RequestCancelledException extends RuntimeException {
        RequestCancelledException(@NonNull String message, @NonNull Throwable cause) {
            super(message, cause);
        }
    }

    /**
     * Result of providing a surface to a {@link ViewfinderSurfaceRequest} via
     * {@link #provideSurface(Surface, Executor, Consumer)}.
     *
     */
    @AutoValue
    abstract static class Result {

        /**
         * Possible result codes.
         *
         */
        @IntDef({RESULT_SURFACE_USED_SUCCESSFULLY, RESULT_REQUEST_CANCELLED, RESULT_INVALID_SURFACE,
                RESULT_SURFACE_ALREADY_PROVIDED, RESULT_WILL_NOT_PROVIDE_SURFACE})
        @Retention(RetentionPolicy.SOURCE)
        @RestrictTo(Scope.LIBRARY)
        public @interface ResultCode {
        }

        /**
         * Provided surface was successfully used by the camera and eventually detached once no
         * longer needed by the camera.
         *
         * <p>This result denotes that it is safe to release the {@link Surface} and any underlying
         * resources.
         *
         * <p>For compatibility reasons, the {@link Surface} object should not be reused by
         * future {@link ViewfinderSurfaceRequest SurfaceRequests}, and a new surface should be
         * created instead.
         */
        public static final int RESULT_SURFACE_USED_SUCCESSFULLY = 0;

        /**
         * Provided surface was never attached to the camera due to the
         * {@link ViewfinderSurfaceRequest} being cancelled by the camera.
         *
         * <p>It is safe to release or reuse {@link Surface}, assuming it was not previously
         * attached to a camera via {@link #provideSurface(Surface, Executor, Consumer)}. If
         * reusing the surface for a future surface request, it should be verified that the
         * surface still matches the resolution specified by
         * {@link ViewfinderSurfaceRequest#getResolution()}.
         */
        public static final int RESULT_REQUEST_CANCELLED = 1;

        /**
         * Provided surface could not be used by the camera.
         *
         * <p>This is likely due to the {@link Surface} being closed prematurely or the resolution
         * of the surface not matching the resolution specified by
         * {@link ViewfinderSurfaceRequest#getResolution()}.
         */
        public static final int RESULT_INVALID_SURFACE = 2;

        /**
         * Surface was not attached to the camera through this invocation of
         * {@link #provideSurface(Surface, Executor, Consumer)} due to the
         * {@link ViewfinderSurfaceRequest} already being complete with a surface.
         *
         * <p>The {@link ViewfinderSurfaceRequest} has already been completed by a previous
         * invocation of {@link #provideSurface(Surface, Executor, Consumer)}.
         *
         * <p>It is safe to release or reuse the {@link Surface}, assuming it was not previously
         * attached to a camera via {@link #provideSurface(Surface, Executor, Consumer)}.
         */
        public static final int RESULT_SURFACE_ALREADY_PROVIDED = 3;

        /**
         * Surface was not attached to the camera through this invocation of
         * {@link #provideSurface(Surface, Executor, Consumer)} due to the
         * {@link ViewfinderSurfaceRequest} already being marked as "will not provide surface".
         *
         * <p>The {@link ViewfinderSurfaceRequest} has already been marked as 'will not provide
         * surface' by a previous invocation of {@link #willNotProvideSurface()}.
         *
         * <p>It is safe to release or reuse the {@link Surface}, assuming it was not previously
         * attached to a camera via {@link #provideSurface(Surface, Executor, Consumer)}.
         */
        public static final int RESULT_WILL_NOT_PROVIDE_SURFACE = 4;

        /**
         * Creates a result from the given result code and surface.
         *
         * <p>Can be used to compare to results returned to {@code resultListener} in
         * {@link #provideSurface(Surface, Executor, Consumer)}.
         *
         * @param code    One of {@link #RESULT_SURFACE_USED_SUCCESSFULLY},
         *                {@link #RESULT_REQUEST_CANCELLED}, {@link #RESULT_INVALID_SURFACE},
         *                {@link #RESULT_SURFACE_ALREADY_PROVIDED}, or
         *                {@link #RESULT_WILL_NOT_PROVIDE_SURFACE}.
         * @param surface The {@link Surface} used to complete the {@link ViewfinderSurfaceRequest}.
         */
        @NonNull
        static Result of(@ResultCode int code, @NonNull Surface surface) {
            return new AutoValue_ViewfinderSurfaceRequest_Result(code, surface);
        }

        /**
         * Returns the result of invoking {@link #provideSurface(Surface, Executor, Consumer)}
         * with the surface from {@link #getSurface()}.
         *
         * @return One of {@link #RESULT_SURFACE_USED_SUCCESSFULLY},
         * {@link #RESULT_REQUEST_CANCELLED}, {@link #RESULT_INVALID_SURFACE}, or
         * {@link #RESULT_SURFACE_ALREADY_PROVIDED}, {@link #RESULT_WILL_NOT_PROVIDE_SURFACE}.
         */
        @ResultCode
        public abstract int getResultCode();

        /**
         * The surface used to complete a {@link ViewfinderSurfaceRequest} with
         * {@link #provideSurface(Surface, Executor, Consumer)}.
         *
         * @return the surface.
         */
        @NonNull
        public abstract Surface getSurface();

        // Ensure Result can't be subclassed outside the package
        Result() {
        }
    }

    /**
     * Valid integer sensor orientation degrees values.
     */
    @IntDef({0, 90, 180, 270})
    @Retention(RetentionPolicy.SOURCE)
    @interface SensorOrientationDegreesValue {
    }

    /**
     * Valid integer sensor orientation degrees values.
     */
    @IntDef({LENS_FACING_FRONT, LENS_FACING_BACK, LENS_FACING_EXTERNAL})
    @Retention(RetentionPolicy.SOURCE)
    @interface LensFacingValue {
    }
}
