/*
 * Copyright 2024 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 com.android.test.viewembed;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.AttachedSurfaceControl;
import android.view.Choreographer;
import android.view.Gravity;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.widget.LinearLayout;

/**
 * Used to manually test that {@link android.view.SurfaceControlInputReceiver} API works.
 */
public class SurfaceInputTestActivity extends Activity {

    private static final String TAG = "SurfaceInputTestActivity";
    private SurfaceView mLocalSurfaceView;
    private SurfaceView mRemoteSurfaceView;
    private IAttachEmbeddedWindow mIAttachEmbeddedWindow;
    private SurfaceControl mParentSurfaceControl;

    private SurfaceControl mLocalSurfaceControl;

    private final ServiceConnection mConnection = new ServiceConnection() {
        // Called when the connection with the service is established
        public void onServiceConnected(ComponentName className, IBinder service) {
            Log.d(TAG, "Service Connected");
            mIAttachEmbeddedWindow = IAttachEmbeddedWindow.Stub.asInterface(service);
            loadEmbedded();
        }

        public void onServiceDisconnected(ComponentName className) {
            Log.d(TAG, "Service Disconnected");
            mIAttachEmbeddedWindow = null;
        }
    };

    @Override
    public void onAttachedToWindow() {
        super.onAttachedToWindow();
        ViewTreeObserver viewTreeObserver = getWindow().getDecorView().getViewTreeObserver();
        viewTreeObserver.addOnPreDrawListener(
                new ViewTreeObserver.OnPreDrawListener() {
                    @Override
                    public boolean onPreDraw() {
                        addLocalChildSurfaceControl(getWindow().getRootSurfaceControl());
                        viewTreeObserver.removeOnPreDrawListener(this);
                        return true;
                    }
                });
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        LinearLayout content = new LinearLayout(this);
        content.setFitsSystemWindows(true);
        mLocalSurfaceView = new SurfaceView(this);
        content.addView(mLocalSurfaceView, new LinearLayout.LayoutParams(
                500, 500, Gravity.CENTER_HORIZONTAL | Gravity.TOP));

        mRemoteSurfaceView = new SurfaceView(this);
        content.addView(mRemoteSurfaceView, new LinearLayout.LayoutParams(
                500, 500, Gravity.CENTER_HORIZONTAL | Gravity.TOP));

        setContentView(content);

        mLocalSurfaceView.setZOrderOnTop(true);
        mLocalSurfaceView.getHolder().addCallback(mLocalSurfaceViewCallback);

        mRemoteSurfaceView.setZOrderOnTop(true);
        mRemoteSurfaceView.getHolder().addCallback(mRemoteSurfaceViewHolder);

        Intent intent = new Intent(this, EmbeddedWindowService.class);
        intent.setAction(IAttachEmbeddedWindow.class.getName());
        Log.d(TAG, "bindService");
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mLocalSurfaceControl != null) {
            getWindowManager().unregisterSurfaceControlInputReceiver(mLocalSurfaceControl);
            new SurfaceControl.Transaction().remove(mLocalSurfaceControl).apply();
        }
    }

    private void addLocalChildSurfaceControl(AttachedSurfaceControl attachedSurfaceControl) {
        mLocalSurfaceControl = new SurfaceControl.Builder().setName("LocalSC")
                .setBufferSize(100, 100).build();
        attachedSurfaceControl.buildReparentTransaction(mLocalSurfaceControl)
                .setVisibility(mLocalSurfaceControl, true)
                .setCrop(mLocalSurfaceControl, new Rect(0, 0, 100, 100))
                .setPosition(mLocalSurfaceControl, 250, 1000)
                .setLayer(mLocalSurfaceControl, 1).apply();

        Paint paint = new Paint();
        paint.setColor(Color.WHITE);
        paint.setTextSize(20);

        Surface surface = new Surface(mLocalSurfaceControl);
        Canvas c = surface.lockCanvas(null);
        c.drawColor(Color.GREEN);
        c.drawText("Local SC", 0, 0, paint);
        surface.unlockCanvasAndPost(c);
        WindowManager wm = getSystemService(WindowManager.class);
        wm.registerBatchedSurfaceControlInputReceiver(
                attachedSurfaceControl.getInputTransferToken(), mLocalSurfaceControl,
                Choreographer.getInstance(), event -> {
                    Log.d(TAG, "onInputEvent-sc " + event);
                    return false;
                });
    }

    private final SurfaceHolder.Callback mLocalSurfaceViewCallback = new SurfaceHolder.Callback() {
        @Override
        public void surfaceCreated(@NonNull SurfaceHolder holder) {
            Paint paint = new Paint();
            paint.setColor(Color.WHITE);
            paint.setTextSize(40);

            Canvas c = holder.lockCanvas();
            c.drawColor(Color.RED);
            c.drawText("Local", 250, 250, paint);
            holder.unlockCanvasAndPost(c);

            WindowManager wm = getSystemService(WindowManager.class);
            wm.registerBatchedSurfaceControlInputReceiver(
                    mLocalSurfaceView.getRootSurfaceControl().getInputTransferToken(),
                    mLocalSurfaceView.getSurfaceControl(),
                    Choreographer.getInstance(), event -> {
                        Log.d(TAG, "onInputEvent-local " + event);
                        return false;
                    });
        }

        @Override
        public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width,
                int height) {

        }

        @Override
        public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
            getWindowManager().unregisterSurfaceControlInputReceiver(
                    mLocalSurfaceView.getSurfaceControl());
        }
    };

    private final SurfaceHolder.Callback mRemoteSurfaceViewHolder = new SurfaceHolder.Callback() {
        @Override
        public void surfaceCreated(@NonNull SurfaceHolder holder) {
            mParentSurfaceControl = mRemoteSurfaceView.getSurfaceControl();
            loadEmbedded();
        }

        @Override
        public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width,
                int height) {
        }

        @Override
        public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
            if (mIAttachEmbeddedWindow != null) {
                try {
                    mIAttachEmbeddedWindow.tearDownEmbeddedSurfaceControl();
                } catch (RemoteException e) {
                    Log.e(TAG, "Failed to tear down embedded SurfaceControl", e);
                }
            }
        }
    };

    private void loadEmbedded() {
        if (mParentSurfaceControl == null || mIAttachEmbeddedWindow == null) {
            return;
        }
        try {
            mIAttachEmbeddedWindow.attachEmbeddedSurfaceControl(mParentSurfaceControl,
                    getDisplayId(),
                    mRemoteSurfaceView.getRootSurfaceControl().getInputTransferToken());
        } catch (RemoteException e) {
            Log.e(TAG, "Failed to load embedded SurfaceControl", e);
        }
    }
}
