/*
 * 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.camera.core;

import android.view.Surface;

import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.camera.core.impl.ImageReaderProxy;

import java.util.concurrent.Executor;

/**
 * An {@link ImageReaderProxy} that wraps another ImageReaderProxy to safely wait until all
 * produced {@link ImageProxy} are closed before closing the ImageReaderProxy.
 */
class SafeCloseImageReaderProxy implements ImageReaderProxy {
    // Lock to synchronize acquired ImageProxys and close.
    private final Object mLock = new Object();

    @GuardedBy("mLock")
    private volatile int mOutstandingImages = 0;
    @GuardedBy("mLock")
    private volatile boolean mIsClosed = false;

    // The wrapped instance of ImageReaderProxy
    @GuardedBy("mLock")
    private final ImageReaderProxy mImageReaderProxy;

    @Nullable
    private final Surface mSurface;

    // Called after images are closed to check if the ImageReaderProxy should be closed
    private ForwardingImageProxy.OnImageCloseListener mImageCloseListener = (image) -> {
        synchronized (mLock) {
            mOutstandingImages--;
            if (mIsClosed && mOutstandingImages == 0) {
                close();
            }
        }
    };

    SafeCloseImageReaderProxy(@NonNull ImageReaderProxy imageReaderProxy) {
        mImageReaderProxy = imageReaderProxy;
        mSurface = imageReaderProxy.getSurface();
    }

    @Nullable
    @Override
    public ImageProxy acquireLatestImage() {
        synchronized (mLock) {
            return wrapImageProxy(mImageReaderProxy.acquireLatestImage());
        }
    }

    @Nullable
    @Override
    public ImageProxy acquireNextImage() {
        synchronized (mLock) {
            return wrapImageProxy(mImageReaderProxy.acquireNextImage());
        }
    }

    /**
     * @inheritDoc <p>This will directly close the wrapped {@link ImageReaderProxy} without
     * waiting for
     * outstanding {@link ImageProxy} to be closed. Typically, when using {@link
     * SafeCloseImageReaderProxy} this should not be directly called. Instead call {@link
     * #safeClose()} which safely waits for all ImageProxy to close before closing the wrapped
     * ImageReaderProxy.
     */
    @Override
    public void close() {
        synchronized (mLock) {
            if (mSurface != null) {
                mSurface.release();
            }
            mImageReaderProxy.close();
        }
    }

    @GuardedBy("mLock")
    @Nullable
    private ImageProxy wrapImageProxy(@Nullable ImageProxy imageProxy) {
        synchronized (mLock) {
            if (imageProxy != null) {
                mOutstandingImages++;
                SingleCloseImageProxy singleCloseImageProxy =
                        new SingleCloseImageProxy(imageProxy);
                singleCloseImageProxy.addOnImageCloseListener(mImageCloseListener);
                return singleCloseImageProxy;
            } else {
                return null;
            }
        }
    }

    /**
     * Close the underlying {@link ImageReaderProxy} safely by deferring the close until the last
     * {@link ImageProxy} has been closed.
     *
     * <p>Once this has been called, no more additional ImageProxy can be acquired from the
     * {@link SafeCloseImageReaderProxy}.
     */
    @GuardedBy("mLock")
    void safeClose() {
        synchronized (mLock) {
            mIsClosed = true;
            mImageReaderProxy.clearOnImageAvailableListener();

            if (mOutstandingImages == 0) {
                close();
            }
        }
    }

    @Override
    public int getHeight() {
        synchronized (mLock) {
            return mImageReaderProxy.getHeight();
        }
    }

    @Override
    public int getWidth() {
        synchronized (mLock) {
            return mImageReaderProxy.getWidth();
        }
    }

    @Override
    public int getImageFormat() {
        synchronized (mLock) {
            return mImageReaderProxy.getImageFormat();
        }
    }

    @Override
    public int getMaxImages() {
        synchronized (mLock) {
            return mImageReaderProxy.getMaxImages();
        }
    }

    @Nullable
    @Override
    public Surface getSurface() {
        synchronized (mLock) {
            return mImageReaderProxy.getSurface();
        }
    }

    @Override
    public void setOnImageAvailableListener(@NonNull OnImageAvailableListener listener,
            @NonNull Executor executor) {
        synchronized (mLock) {
            mImageReaderProxy.setOnImageAvailableListener(
                    imageReader -> listener.onImageAvailable(this), executor);
        }
    }

    @Override
    public void clearOnImageAvailableListener() {
        synchronized (mLock) {
            mImageReaderProxy.clearOnImageAvailableListener();
        }
    }
}

@@ -1257,25 +1257,16 @@
 private 
-volatile 
 int mOut
@@ -1325,17 +1325,8 @@
 ate 
-volatile 
 bool
@@ -1627,16 +1627,22 @@
 private 
+final 
 Forwardi
@@ -3201,43 +3201,8 @@
 ) %7B%0A
-        synchronized (mLock) %7B%0A    
     
@@ -3223,36 +3223,32 @@
 roxy != null) %7B%0A
-    
             mOut
@@ -3277,20 +3277,16 @@
         
-    
 SingleCl
@@ -3343,20 +3343,16 @@
         
-    
 new Sing
@@ -3394,20 +3394,16 @@
         
-  
-  
 singleCl
@@ -3470,28 +3470,24 @@
             
-    
 return singl
@@ -3512,20 +3512,16 @@
         
-   
- 
 %7D else %7B
@@ -3521,20 +3521,16 @@
  else %7B%0A
-    
         
@@ -3546,30 +3546,16 @@
 n null;%0A
-            %7D%0A
         
@@ -3853,24 +3853,24 @@
 Proxy%7D.%0A
+
      */%0A
     @Gua
@@ -3865,32 +3865,8 @@
  */%0A
-    @GuardedBy(%22mLock%22)%0A
     

Fix redundant synchronized/GuardedBy