/*
 * Copyright (C) 2018 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.server.infra;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;

import com.android.internal.infra.AbstractRemoteService;

import java.io.PrintWriter;
import java.util.List;

/**
 * A helper class used to resolve the name of the app-provided service a
 * {@link AbstractRemoteService} binds to.
 *
 * @hide
 */
public interface ServiceNameResolver {
    /**
     * Listener for name changes.
     */
    interface NameResolverListener {

        /**
         * The name change callback.
         */
        void onNameResolved(@UserIdInt int userId, @Nullable String serviceName,
                boolean isTemporary);
    }

    /**
     * Sets a callback that is called after the service is
     * {@link #setTemporaryService(int, String, int) set} or
     * {@link #resetTemporaryService(int) reset}.
     *
     * <p>Typically called after the object is constructed.
     */
    default void setOnTemporaryServiceNameChangedCallback(
            @SuppressWarnings("unused") @NonNull NameResolverListener callback) {
        // ignored by default
    }

    /**
     * Gets the default name of the service for the given user.
     *
     * <p>Typically implemented by reading a Settings property or framework resource.
     */
    @Nullable
    String getDefaultServiceName(@UserIdInt int userId);

    /**
     * Gets the default list of names of the services for the given user.
     *
     * <p>Typically implemented by reading a Settings property or framework resource.
     */
    @Nullable
    default String[] getDefaultServiceNameList(@UserIdInt int userId) {
        if (isConfiguredInMultipleMode()) {
            throw new UnsupportedOperationException("getting default service list not supported");
        } else {
            return new String[] { getDefaultServiceName(userId) };
        }
    }

    /**
     * Set the given list of services to the secure setting
     */
    default void setServiceNameList(List<String> services, int userId) {
    }

    /**
     * Returns whether the resolver is configured to connect to multiple backend services.
     * The default return type is false.
     *
     * <p>Typically implemented by reading a Settings property or framework resource.
     */
    default boolean isConfiguredInMultipleMode() {
        return false;
    }

    /**
     * Gets the current name of the service for the given user
     *
     * @return either the temporary name (set by
     * {@link #setTemporaryService(int, String, int)}, or the
     * {@link #getDefaultServiceName(int) default name}.
     */
    @Nullable
    default String getServiceName(@UserIdInt int userId) {
        return getDefaultServiceName(userId);
    }

    /**
     * Gets the current name of the service for the given user
     *
     * @return either the temporary name (set by
     * {@link #setTemporaryService(int, String, int)}, or the
     * {@link #getDefaultServiceName(int) default name}.
     */
    @Nullable
    default String[] getServiceNameList(@UserIdInt int userId) {
        return getDefaultServiceNameList(userId);
    }

    /**
     * Checks whether the current service is temporary for the given user.
     */
    default boolean isTemporary(@SuppressWarnings("unused") @UserIdInt int userId) {
        return false;
    }

    /**
     * Temporarily sets the service implementation for the given user.
     *
     * @param userId        user handle
     * @param componentName name of the new component
     * @param durationMs    how long the change will be valid (the service will be automatically
     *                      reset
     *                      to the default component after this timeout expires).
     * @throws UnsupportedOperationException if not implemented.
     */
    default void setTemporaryService(@UserIdInt int userId, @NonNull String componentName,
            int durationMs) {
        throw new UnsupportedOperationException("temporary user not supported");
    }

    /**
     * Temporarily sets the service implementation for the given user.
     *
     * @param userId         user handle
     * @param componentNames list of the names of the new component
     * @param durationMs     how long the change will be valid (the service will be automatically
     *                       reset
     *                       to the default component after this timeout expires).
     * @throws UnsupportedOperationException if not implemented.
     */
    default void setTemporaryServices(@UserIdInt int userId, @NonNull String[] componentNames,
            int durationMs) {
        throw new UnsupportedOperationException("temporary user not supported");
    }

    /**
     * Resets the temporary service implementation to the default component for the given user.
     *
     * @param userId user handle
     * @throws UnsupportedOperationException if not implemented.
     */
    default void resetTemporaryService(@UserIdInt int userId) {
        throw new UnsupportedOperationException("temporary user not supported");
    }

    /**
     * Sets whether the default service should be used when the temporary service is not set.
     *
     * <p>Typically used during CTS tests to make sure only the default service doesn't interfere
     * with the test results.
     *
     * @param userId  user handle
     * @param enabled whether the default service should be used when the temporary service is not
     *                set. If the service enabled state is already that value, the command is
     *                ignored and this
     *                method return {@code false}.
     * @return whether the enabled state changed.
     * @throws UnsupportedOperationException if not implemented.
     */
    default boolean setDefaultServiceEnabled(@UserIdInt int userId, boolean enabled) {
        throw new UnsupportedOperationException("changing default service not supported");
    }

    /**
     * Checks whether the default service should be used when the temporary service is not set.
     *
     * <p>Typically used during CTS tests to make sure only the default service doesn't interfere
     * with the test results.
     *
     * @param userId user handle
     * @throws UnsupportedOperationException if not implemented.
     */
    default boolean isDefaultServiceEnabled(@UserIdInt int userId) {
        throw new UnsupportedOperationException("checking default service not supported");
    }

    /**
     * Dumps the generic info in just one line (without calling {@code println}.
     */
    // TODO(b/117779333): support proto
    void dumpShort(@NonNull PrintWriter pw);

    /**
     * Dumps the user-specific info in just one line (without calling {@code println}.
     */
    // TODO(b/117779333): support proto
    void dumpShort(@NonNull PrintWriter pw, @UserIdInt int userId);
}
