/*
 * 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.tv.ui.hideable;

import android.content.Context;
import android.os.Looper;
import android.os.Message;
import android.support.annotation.NonNull;
import android.support.annotation.UiThread;
import android.support.annotation.VisibleForTesting;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
import com.android.tv.common.WeakHandler;

/**
 * Schedules a view element to be hidden after a delay.
 *
 * <p>When accessibility is turned on elements are not automatically hidden.
 *
 * <p>Users of this class must pass it to {@link
 * AccessibilityManager#addAccessibilityStateChangeListener(AccessibilityStateChangeListener)} and
 * {@link
 * AccessibilityManager#removeAccessibilityStateChangeListener(AccessibilityStateChangeListener)}
 * during the appropriate live cycle event, or handle calling {@link
 * #onAccessibilityStateChanged(boolean)}.
 */
@UiThread
public final class AutoHideScheduler implements AccessibilityStateChangeListener {
    private static final int MSG_HIDE = 1;

    private final HideHandler mHandler;
    private final Runnable mRunnable;

    public AutoHideScheduler(Context context, Runnable runnable) {
        this(
                runnable,
                context.getSystemService(AccessibilityManager.class),
                Looper.getMainLooper());
    }

    @VisibleForTesting
    AutoHideScheduler(Runnable runnable, AccessibilityManager accessibilityManager, Looper looper) {
        // Keep a reference here because HideHandler only has a weak reference to it.
        mRunnable = runnable;
        mHandler = new HideHandler(looper, mRunnable);
        mHandler.setAllowAutoHide(!accessibilityManager.isEnabled());
    }

    public void cancel() {
        mHandler.removeMessages(MSG_HIDE);
    }

    public void schedule(long delayMs) {
        cancel();
        if (mHandler.mAllowAutoHide) {
            mHandler.sendEmptyMessageDelayed(MSG_HIDE, delayMs);
        }
    }

    @Override
    public void onAccessibilityStateChanged(boolean enabled) {
        mHandler.onAccessibilityStateChanged(enabled);
    }

    public boolean isScheduled() {
        return mHandler.hasMessages(MSG_HIDE);
    }

    private static class HideHandler extends WeakHandler<Runnable>
            implements AccessibilityStateChangeListener {

        private boolean mAllowAutoHide;

        public HideHandler(Looper looper, Runnable hideRunner) {
            super(looper, hideRunner);
        }

        @Override
        protected void handleMessage(Message msg, @NonNull Runnable runnable) {
            switch (msg.what) {
                case MSG_HIDE:
                    if (mAllowAutoHide) {
                        runnable.run();
                    }
                    break;
                default:
                    // do nothing
            }
        }

        public void setAllowAutoHide(boolean mAllowAutoHide) {
            this.mAllowAutoHide = mAllowAutoHide;
        }

        @Override
        public void onAccessibilityStateChanged(boolean enabled) {
            mAllowAutoHide = !enabled;
        }
    }
}
