/*
 * Copyright (C) 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.systemui.car.wm.cluster;

import static android.car.cluster.ClusterHomeManager.CONFIG_DISPLAY_BOUNDS;
import static android.car.cluster.ClusterHomeManager.CONFIG_DISPLAY_ID;

import android.car.Car;
import android.car.cluster.ClusterHomeManager;
import android.car.cluster.ClusterState;
import android.graphics.Rect;
import android.util.Slog;
import android.view.Display;
import android.view.SurfaceControl;
import android.window.DisplayAreaInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;

import com.android.systemui.CoreStartable;
import com.android.systemui.car.CarServiceProvider;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;

import java.util.Optional;
import java.util.concurrent.Executor;

import javax.inject.Inject;

/***
 * Controls the RootTDA of cluster display per CLUSTER_DISPLAY_STATE message.
 */
@SysUISingleton
public class ClusterDisplayController implements CoreStartable {
    private static final String TAG =
            com.android.systemui.car.wm.cluster.ClusterDisplayController.class.getSimpleName();
    private static final boolean DBG = false;

    private final RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
    private final CarServiceProvider mCarServiceProvider;
    private final Executor mMainExecutor;

    private ClusterHomeManager mClusterHomeManager;
    private WindowContainerToken mRootTDAToken;
    private ClusterState mClusterState;

    @Inject
    public ClusterDisplayController(
            Optional<RootTaskDisplayAreaOrganizer> rootTDAOrganizer,
            CarServiceProvider carServiceProvider, @Main Executor mainExecutor) {
        mRootTDAOrganizer = rootTDAOrganizer.orElse(null);
        mCarServiceProvider = carServiceProvider;
        mMainExecutor = mainExecutor;
    }

    @Override
    public void start() {
        if (mRootTDAOrganizer == null) {
            Slog.w(TAG, "ClusterDisplayController is disabled because of no "
                    + "RootTaskDisplayAreaOrganizer");
            return;
        }
        mCarServiceProvider.addListener(mCarServiceOnConnectedListener);
    }

    private final CarServiceProvider.CarServiceOnConnectedListener mCarServiceOnConnectedListener =
            new CarServiceProvider.CarServiceOnConnectedListener() {
                @Override
                public void onConnected(Car car) {
                    mClusterHomeManager = (ClusterHomeManager) car.getCarManager(
                            Car.CLUSTER_HOME_SERVICE);
                    if (mClusterHomeManager == null) {
                        Slog.w(TAG, "ClusterHomeManager is disabled");
                        return;
                    }
                    mClusterHomeManager.registerClusterStateListener(mMainExecutor,
                            mClusterStateListener);

                    mClusterState = mClusterHomeManager.getClusterState();
                    if (mClusterState.displayId != Display.INVALID_DISPLAY) {
                        mRootTDAOrganizer.registerListener(mClusterState.displayId,
                                mRootTDAListener);
                    }
                }
            };

    private final ClusterHomeManager.ClusterStateListener mClusterStateListener =
            new ClusterHomeManager.ClusterStateListener() {
                @Override
                public void onClusterStateChanged(ClusterState state, int changes) {
                    // Need to update mClusterState first, since mClusterState will be referenced
                    // during
                    // registerListener() -> onDisplayAreaAppeared() -> resizeTDA().
                    mClusterState = state;
                    if (DBG) {
                        Slog.d(TAG, "onClusterStateChanged: changes=" + changes
                                + ", displayId=" + state.displayId);
                    }
                    if ((changes & CONFIG_DISPLAY_ID) != 0) {
                        if (state.displayId != Display.INVALID_DISPLAY) {
                            mRootTDAOrganizer.registerListener(state.displayId, mRootTDAListener);
                        } else {
                            mRootTDAOrganizer.unregisterListener(mRootTDAListener);
                        }
                    }
                    if ((changes & CONFIG_DISPLAY_BOUNDS) != 0 && mRootTDAToken != null) {
                        resizeTDA(mRootTDAToken, state.bounds, state.displayId);
                    }
                }
            };

    private final RootTaskDisplayAreaOrganizer.RootTaskDisplayAreaListener mRootTDAListener =
            new RootTaskDisplayAreaOrganizer.RootTaskDisplayAreaListener() {
                @Override
                public void onDisplayAreaAppeared(DisplayAreaInfo displayAreaInfo) {
                    if (DBG) Slog.d(TAG, "onDisplayAreaAppeared: " + displayAreaInfo);
                    if (mClusterState != null
                            && mClusterState.displayId != Display.INVALID_DISPLAY) {
                        resizeTDA(displayAreaInfo.token, mClusterState.bounds,
                                mClusterState.displayId);
                    }
                    mRootTDAToken = displayAreaInfo.token;
                }

                @Override
                public void onDisplayAreaVanished(DisplayAreaInfo displayAreaInfo) {
                    if (DBG) Slog.d(TAG, "onDisplayAreaVanished: " + displayAreaInfo);
                    mRootTDAToken = null;
                }

                @Override
                public void onDisplayAreaInfoChanged(DisplayAreaInfo displayAreaInfo) {
                    if (DBG) Slog.d(TAG, "onDisplayAreaInfoChanged: " + displayAreaInfo);
                    mRootTDAToken = displayAreaInfo.token;
                }
            };

    private void resizeTDA(WindowContainerToken token, Rect bounds, int displayId) {
        if (DBG) {
            Slog.d(TAG, "resizeTDA: token=" + token + ", bounds=" + bounds
                    + ", displayId=" + displayId);
        }
        WindowContainerTransaction wct = new WindowContainerTransaction();
        wct.setBounds(token, bounds);
        wct.setAppBounds(token, bounds);
        mRootTDAOrganizer.applyTransaction(wct);

        SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
        mRootTDAOrganizer.setPosition(tx, displayId, bounds.left, bounds.top);
        tx.apply();
    }
}
