// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.browser.tab;

import android.annotation.SuppressLint;
import android.os.Handler;
import android.os.Message;

import org.chromium.chrome.browser.UrlConstants;
import org.chromium.chrome.browser.device.DeviceClassManager;
import org.chromium.chrome.browser.util.AccessibilityUtil;
import org.chromium.components.dom_distiller.core.DomDistillerUrlUtils;
import org.chromium.components.security_state.ConnectionSecurityLevel;
import org.chromium.content.browser.ContentViewCore;
import org.chromium.content_public.browser.WebContents;

/**
 * Determines the desired visibility of the browser controls based on the current state of a given
 * tab.
 */
public class TabStateBrowserControlsVisibilityDelegate
        implements BrowserControlsVisibilityDelegate {
    protected static final int MSG_ID_ENABLE_FULLSCREEN_AFTER_LOAD = 1;
    /** The maximum amount of time to wait for a page to load before entering fullscreen. */
    private static final long MAX_FULLSCREEN_LOAD_DELAY_MS = 3000;

    private static boolean sDisableLoadingCheck;

    protected final Tab mTab;

    private boolean mIsFullscreenWaitingForLoad;

    /**
     * Basic constructor.
     * @param tab The associated {@link Tab}.
     */
    public TabStateBrowserControlsVisibilityDelegate(Tab tab) {
        mTab = tab;

        mTab.addObserver(new EmptyTabObserver() {
            @SuppressLint("HandlerLeak")
            private Handler mHandler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    if (msg == null) return;
                    if (msg.what == MSG_ID_ENABLE_FULLSCREEN_AFTER_LOAD) {
                        enableFullscreenAfterLoad();
                    }
                }
            };

            private long getLoadDelayMs() {
                return sDisableLoadingCheck ? 0 : MAX_FULLSCREEN_LOAD_DELAY_MS;
            }

            private void enableFullscreenAfterLoad() {
                if (!mIsFullscreenWaitingForLoad) return;

                mIsFullscreenWaitingForLoad = false;
                mTab.updateFullscreenEnabledState();
            }

            private void cancelEnableFullscreenLoadDelay() {
                mHandler.removeMessages(MSG_ID_ENABLE_FULLSCREEN_AFTER_LOAD);
                mIsFullscreenWaitingForLoad = false;
            }

            private void scheduleEnableFullscreenLoadDelayIfNecessary() {
                if (mIsFullscreenWaitingForLoad
                        && !mHandler.hasMessages(MSG_ID_ENABLE_FULLSCREEN_AFTER_LOAD)) {
                    mHandler.sendEmptyMessageDelayed(
                            MSG_ID_ENABLE_FULLSCREEN_AFTER_LOAD, getLoadDelayMs());
                }
            }

            @Override
            public void onWebContentsSwapped(Tab tab, boolean didStartLoad, boolean didFinishLoad) {
                if (!didStartLoad) return;

                // As we may have missed the main frame commit notification for the
                // swapped web contents, schedule the enabling of fullscreen now.
                scheduleEnableFullscreenLoadDelayIfNecessary();
            }

            @Override
            public void onDidFinishNavigation(Tab tab, String url, boolean isInMainFrame,
                    boolean isErrorPage, boolean hasCommitted, boolean isSameDocument,
                    boolean isFragmentNavigation, Integer pageTransition, int errorCode,
                    int httpStatusCode) {
                if (!hasCommitted || !isInMainFrame) return;
                mHandler.removeMessages(MSG_ID_ENABLE_FULLSCREEN_AFTER_LOAD);
                mHandler.sendEmptyMessageDelayed(
                        MSG_ID_ENABLE_FULLSCREEN_AFTER_LOAD, getLoadDelayMs());
                mTab.updateFullscreenEnabledState();
            }

            @Override
            public void onPageLoadStarted(Tab tab, String url) {
                mIsFullscreenWaitingForLoad = !DomDistillerUrlUtils.isDistilledPage(url);
                mTab.updateFullscreenEnabledState();
            }

            @Override
            public void onPageLoadFinished(Tab tab) {
                // Handle the case where a commit or prerender swap notification failed to arrive
                // and the enable fullscreen message was never enqueued.
                scheduleEnableFullscreenLoadDelayIfNecessary();
            }

            @Override
            public void onPageLoadFailed(Tab tab, int errorCode) {
                cancelEnableFullscreenLoadDelay();
                mTab.updateFullscreenEnabledState();
            }

            @Override
            public void onHidden(Tab tab) {
                cancelEnableFullscreenLoadDelay();
            }

            @Override
            public void onDestroyed(Tab tab) {
                super.onDestroyed(tab);

                // Remove pending handler actions to prevent memory leaks.
                mHandler.removeCallbacksAndMessages(null);
            }
        });
    }

    @Override
    public boolean isHidingBrowserControlsEnabled() {
        WebContents webContents = mTab.getWebContents();
        if (webContents == null || webContents.isDestroyed()) return false;

        String url = mTab.getUrl();
        boolean enableHidingBrowserControls = url != null;
        enableHidingBrowserControls &= !url.startsWith(UrlConstants.CHROME_URL_PREFIX);
        enableHidingBrowserControls &= !url.startsWith(UrlConstants.CHROME_NATIVE_URL_PREFIX);

        int securityState = mTab.getSecurityLevel();
        enableHidingBrowserControls &= (securityState != ConnectionSecurityLevel.DANGEROUS
                && securityState != ConnectionSecurityLevel.SECURITY_WARNING);

        enableHidingBrowserControls &= !AccessibilityUtil.isAccessibilityEnabled();

        ContentViewCore cvc = mTab.getContentViewCore();
        enableHidingBrowserControls &= cvc == null || !cvc.isFocusedNodeEditable();
        enableHidingBrowserControls &= !mTab.isShowingErrorPage();
        enableHidingBrowserControls &= !webContents.isShowingInterstitialPage();
        enableHidingBrowserControls &= !mTab.isRendererUnresponsive();
        enableHidingBrowserControls &= (mTab.getFullscreenManager() != null);
        enableHidingBrowserControls &= DeviceClassManager.enableFullscreen();
        enableHidingBrowserControls &= !mIsFullscreenWaitingForLoad;

        return enableHidingBrowserControls;
    }

    @Override
    public boolean isShowingBrowserControlsEnabled() {
        if (mTab.getFullscreenManager() == null) return true;
        return !mTab.getFullscreenManager().getPersistentFullscreenMode();
    }

    /**
     * Disables the logic that prevents hiding the top controls during page load for testing.
     */
    public static void disablePageLoadDelayForTests() {
        sDisableLoadingCheck = true;
    }
}