// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.components.thinwebview.internal;

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;

import androidx.annotation.Nullable;

import org.chromium.base.ContextUtils;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods;
import org.chromium.build.annotations.DoNotInline;
import org.chromium.components.embedder_support.delegate.WebContentsDelegateAndroid;
import org.chromium.components.thinwebview.CompositorView;
import org.chromium.components.thinwebview.ThinWebView;
import org.chromium.components.thinwebview.ThinWebViewConstraints;
import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.base.ActivityWindowAndroid;
import org.chromium.ui.base.IntentRequestTracker;
import org.chromium.ui.base.WindowAndroid;

/**
 * An android view backed by a {@link Surface} that is able to display a live {@link WebContents}.
 */
@JNINamespace("thin_webview::android")
public class ThinWebViewImpl extends FrameLayout implements ThinWebView {
    private final CompositorView mCompositorView;
    private final WindowAndroid mWindowAndroid;
    private long mNativeThinWebViewImpl;
    private WebContents mWebContents;
    private View mContentView;
    // Passed to native and stored as a weak reference, so ensure this strong
    // reference is not optimized away by R8.
    @DoNotInline
    private WebContentsDelegateAndroid mWebContentsDelegate;

    /**
     * Creates a {@link ThinWebViewImpl} backed by a {@link Surface}.
     * @param context The Context to create this view.
     * @param constraints A set of constraints associated with this view.
     * @param intentRequestTracker The {@link IntentRequestTracker} of the current activity.
     */
    public ThinWebViewImpl(Context context, ThinWebViewConstraints constraints,
            IntentRequestTracker intentRequestTracker) {
        super(context);
        if (ContextUtils.activityFromContext(context) != null) {
            mWindowAndroid = new ActivityWindowAndroid(
                    context, /* listenToActivityState= */ true, intentRequestTracker);
        } else {
            mWindowAndroid = new WindowAndroid(context);
        }
        mCompositorView = new CompositorViewImpl(context, mWindowAndroid, constraints);

        LayoutParams layoutParams = new LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        addView(mCompositorView.getView(), layoutParams);

        mNativeThinWebViewImpl = ThinWebViewImplJni.get().init(
                ThinWebViewImpl.this, mCompositorView, mWindowAndroid);
    }

    @Override
    public View getView() {
        return this;
    }

    @Override
    public void attachWebContents(WebContents webContents, @Nullable View contentView,
            @Nullable WebContentsDelegateAndroid delegate) {
        if (mNativeThinWebViewImpl == 0) return;
        mWebContents = webContents;
        // Native code holds only a weak reference to this object.
        mWebContentsDelegate = delegate;
        setContentView(contentView);
        ThinWebViewImplJni.get().setWebContents(
                mNativeThinWebViewImpl, ThinWebViewImpl.this, webContents, delegate);
        webContents.onShow();
    }

    @Override
    public void destroy() {
        if (mNativeThinWebViewImpl == 0) return;
        if (mContentView != null) {
            removeView(mContentView);
            mContentView = null;
        }
        mCompositorView.destroy();
        ThinWebViewImplJni.get().destroy(mNativeThinWebViewImpl, ThinWebViewImpl.this);
        mNativeThinWebViewImpl = 0;
        mWindowAndroid.destroy();
    }

    @Override
    public void setAlpha(float alpha) {
        mCompositorView.setAlpha(alpha);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        if (mNativeThinWebViewImpl == 0) return;
        if (w != oldw || h != oldh) {
            ThinWebViewImplJni.get().sizeChanged(
                    mNativeThinWebViewImpl, ThinWebViewImpl.this, w, h);
        }
    }

    private void setContentView(View contentView) {
        if (mContentView == contentView) return;

        if (mContentView != null) {
            assert getChildCount() > 1;
            removeViewAt(1);
        }

        mContentView = contentView;
        if (mContentView != null) addView(mContentView, 1);
    }

    @NativeMethods
    interface Natives {
        long init(
                ThinWebViewImpl caller, CompositorView compositorView, WindowAndroid windowAndroid);
        void destroy(long nativeThinWebView, ThinWebViewImpl caller);
        void setWebContents(long nativeThinWebView, ThinWebViewImpl caller, WebContents webContents,
                WebContentsDelegateAndroid delegate);
        void sizeChanged(long nativeThinWebView, ThinWebViewImpl caller, int width, int height);
    }
}
