/*
 * Copyright 2021 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.car.rotary;

import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.text.TextUtils;
import android.view.accessibility.AccessibilityNodeInfo;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;

import com.android.internal.util.dump.DualDumpOutputStream;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * A helper class to support apps using {@link android.view.SurfaceView} for off-process rendering.
 * <p>
 * There are two kinds of apps involved in the off-process rendering process: the client apps and
 * the host app. A client app holds a {@link android.view.SurfaceView} and delegates its rendering
 * process to the host app. The host app uses the data provided by the client app to render the app
 * content in the surface provided by the SurfaceView.
 * <p>
 * Although both the client app and the host app have independent <strong>view</strong> hierarchies,
 * their <strong>node</strong> hierarchies are connected. The node hierarchy of the host app is
 * embedded into the node hierarchy of the client app. To be more specific, the root node of the
 * host app is the only child of the SurfaceView node, which is a leaf node of the client app.
 */
class SurfaceViewHelper {

    /** The intent action to be used by the host app to bind to the RendererService. */
    private static final String RENDER_ACTION = "android.car.template.host.RendererService";

    /** Package names of the client apps. */
    private final Set<CharSequence> mClientApps = new HashSet<>();

    /** Package name of the host app. */
    @Nullable
    @VisibleForTesting
    String mHostApp;

    /** Initializes the package name of the host app. */
    void initHostApp(@NonNull PackageManager packageManager) {
        List<ResolveInfo> rendererServices = packageManager.queryIntentServices(
                new Intent(RENDER_ACTION), PackageManager.GET_RESOLVED_FILTER);
        if (rendererServices == null || rendererServices.isEmpty()) {
            L.v("No host app found");
            return;
        }
        mHostApp = rendererServices.get(0).serviceInfo.packageName;
        L.v("Host app has been initialized: " + mHostApp);
    }

    /** Clears the package name of the host app if the given {@code packageName} matches. */
    void clearHostApp(@NonNull String packageName) {
        if (packageName.equals(mHostApp)) {
            mHostApp = null;
            L.v("Host app has been set to null");
        }
    }

    /** Returns whether it supports AAOS template apps. */
    boolean supportTemplateApp() {
        return !TextUtils.isEmpty(mHostApp);
    }

    /** Adds the package name of the client app. */
    void addClientApp(@NonNull CharSequence clientAppPackageName) {
        mClientApps.add(clientAppPackageName);
    }

    /** Returns whether the given {@code node} represents a view of the host app. */
    boolean isHostNode(@NonNull AccessibilityNodeInfo node) {
        return !TextUtils.isEmpty(mHostApp) && mHostApp.equals(node.getPackageName());
    }

    /** Returns whether the given {@code node} represents a view of the client app. */
    boolean isClientNode(@NonNull AccessibilityNodeInfo node) {
        return mClientApps.contains(node.getPackageName());
    }

    void dump(@NonNull DualDumpOutputStream dumpOutputStream, boolean dumpAsProto,
            @NonNull String fieldName, long fieldId) {
        long fieldToken = dumpOutputStream.start(fieldName, fieldId);
        dumpOutputStream.write("hostApp", RotaryProtos.SurfaceViewHelper.HOST_APP, mHostApp);
        DumpUtils.writeCharSequences(dumpOutputStream, dumpAsProto, "clientApps",
                RotaryProtos.SurfaceViewHelper.CLIENT_APPS, mClientApps);
        dumpOutputStream.end(fieldToken);
    }

}
