// 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.appmenu;

import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.view.Menu;
import android.view.MenuItem;

import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.base.CommandLine;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.ChromeSwitches;
import org.chromium.chrome.browser.ShortcutHelper;
import org.chromium.chrome.browser.UrlConstants;
import org.chromium.chrome.browser.banners.AppBannerManager;
import org.chromium.chrome.browser.bookmarks.BookmarkBridge;
import org.chromium.chrome.browser.download.DownloadUtils;
import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
import org.chromium.chrome.browser.omaha.UpdateMenuItemHelper;
import org.chromium.chrome.browser.preferences.ManagedPreferencesUtils;
import org.chromium.chrome.browser.preferences.PrefServiceBridge;
import org.chromium.chrome.browser.share.ShareHelper;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.components.dom_distiller.core.DomDistillerUrlUtils;
import org.chromium.ui.base.DeviceFormFactor;

/**
 * App Menu helper that handles hiding and showing menu items based on activity state.
 */
public class AppMenuPropertiesDelegate {
    // Indices for different levels in drawable.btn_reload_stop.
    // Used only when preparing menu and refresh reload button in menu when tab
    // page load status changes.
    private static final int RELOAD_BUTTON_LEVEL_RELOAD = 0;
    private static final int RELOAD_BUTTON_LEVEL_STOP_LOADING = 1;

    protected MenuItem mReloadMenuItem;

    protected final ChromeActivity mActivity;

    protected BookmarkBridge mBookmarkBridge;

    public AppMenuPropertiesDelegate(ChromeActivity activity) {
        mActivity = activity;
    }

    /**
     * @return Whether the App Menu should be shown.
     */
    public boolean shouldShowAppMenu() {
        return mActivity.shouldShowAppMenu();
    }

    /**
     * Allows the delegate to show and hide items before the App Menu is shown. It is called every
     * time the menu is shown. This assumes that the provided menu contains all the items expected
     * in the application menu (i.e. that the main menu has been inflated into it).
     * @param menu Menu that will be used as the source for the App Menu pop up.
     */
    public void prepareMenu(Menu menu) {
        // Exactly one of these will be true, depending on the type of menu showing.
        boolean isPageMenu;
        boolean isOverviewMenu;
        boolean isTabletEmptyModeMenu;

        boolean isOverview = mActivity.isInOverviewMode();
        boolean isIncognito = mActivity.getCurrentTabModel().isIncognito();
        Tab currentTab = mActivity.getActivityTab();

        // Determine which menu to show.
        if (mActivity.isTablet()) {
            boolean hasTabs = mActivity.getCurrentTabModel().getCount() != 0;
            isPageMenu = hasTabs && !isOverview;
            isOverviewMenu = hasTabs && isOverview;
            isTabletEmptyModeMenu = !hasTabs;
        } else {
            isPageMenu = !isOverview;
            isOverviewMenu = isOverview;
            isTabletEmptyModeMenu = false;
        }

        menu.setGroupVisible(R.id.PAGE_MENU, isPageMenu);
        menu.setGroupVisible(R.id.OVERVIEW_MODE_MENU, isOverviewMenu);
        menu.setGroupVisible(R.id.TABLET_EMPTY_MODE_MENU, isTabletEmptyModeMenu);

        if (isPageMenu && currentTab != null) {
            String url = currentTab.getUrl();
            boolean isChromeScheme = url.startsWith(UrlConstants.CHROME_SCHEME)
                    || url.startsWith(UrlConstants.CHROME_NATIVE_SCHEME);
            boolean isFileScheme = url.startsWith(UrlConstants.FILE_SCHEME);
            boolean isContentScheme = url.startsWith(UrlConstants.CONTENT_SCHEME);
            boolean shouldShowIconRow = !mActivity.isTablet()
                    || mActivity.getWindow().getDecorView().getWidth()
                            < DeviceFormFactor.getMinimumTabletWidthPx(mActivity);

            // Update the icon row items (shown in narrow form factors).
            menu.findItem(R.id.icon_row_menu_id).setVisible(shouldShowIconRow);
            if (shouldShowIconRow) {
                // Disable the "Forward" menu item if there is no page to go to.
                MenuItem forwardMenuItem = menu.findItem(R.id.forward_menu_id);
                forwardMenuItem.setEnabled(currentTab.canGoForward());

                mReloadMenuItem = menu.findItem(R.id.reload_menu_id);
                mReloadMenuItem.setIcon(R.drawable.btn_reload_stop);
                loadingStateChanged(currentTab.isLoading());

                MenuItem bookmarkMenuItem = menu.findItem(R.id.bookmark_this_page_id);
                updateBookmarkMenuItem(bookmarkMenuItem, currentTab);

                MenuItem offlineMenuItem = menu.findItem(R.id.offline_page_id);
                if (offlineMenuItem != null) {
                    if (DownloadUtils.isDownloadHomeEnabled()) {
                        offlineMenuItem.setEnabled(
                                DownloadUtils.isAllowedToDownloadPage(currentTab));

                        Drawable drawable = offlineMenuItem.getIcon();
                        if (drawable != null) {
                            int iconTint = ApiCompatibilityUtils.getColor(
                                    mActivity.getResources(), R.color.light_normal_color);
                            drawable.mutate();
                            drawable.setColorFilter(iconTint, PorterDuff.Mode.SRC_ATOP);
                        }
                    } else {
                        offlineMenuItem.setVisible(false);
                    }
                }
            }

            menu.findItem(R.id.downloads_menu_id)
                    .setVisible(DownloadUtils.isDownloadHomeEnabled());

            menu.findItem(R.id.update_menu_id).setVisible(
                    UpdateMenuItemHelper.getInstance().shouldShowMenuItem(mActivity));

            menu.findItem(R.id.move_to_other_window_menu_id).setVisible(
                    MultiWindowUtils.getInstance().isOpenInOtherWindowSupported(mActivity));

            MenuItem recentTabsMenuItem = menu.findItem(R.id.recent_tabs_menu_id);
            recentTabsMenuItem.setVisible(!isIncognito);
            recentTabsMenuItem.setTitle(R.string.menu_recent_tabs);

            MenuItem allBookmarksMenuItem = menu.findItem(R.id.all_bookmarks_menu_id);
            allBookmarksMenuItem.setTitle(mActivity.getString(R.string.menu_bookmarks));

            // Don't allow "chrome://" pages to be shared.
            menu.findItem(R.id.share_row_menu_id).setVisible(!isChromeScheme);

            ShareHelper.configureDirectShareMenuItem(
                    mActivity, menu.findItem(R.id.direct_share_menu_id));

            // Disable find in page on the native NTP.
            menu.findItem(R.id.find_in_page_id).setVisible(
                    !currentTab.isNativePage() && currentTab.getWebContents() != null);

            // Hide 'Add to homescreen' for the following:
            // * chrome:// pages - Android doesn't know how to direct those URLs.
            // * incognito pages - To avoid problems where users create shortcuts in incognito
            //                      mode and then open the webapp in regular mode.
            // * file:// - After API 24, file: URIs are not supported in VIEW intents and thus
            //             can not be added to the homescreen.
            // * content:// - Accessing external content URIs requires the calling app to grant
            //                access to the resource via FLAG_GRANT_READ_URI_PERMISSION, and that
            //                is not persisted when adding to the homescreen.
            // * If creating shortcuts it not supported by the current home screen.
            MenuItem homescreenItem = menu.findItem(R.id.add_to_homescreen_id);
            boolean homescreenItemVisible = ShortcutHelper.isAddToHomeIntentSupported(mActivity)
                    && !isChromeScheme && !isFileScheme && !isContentScheme && !isIncognito;
            if (homescreenItemVisible) {
                homescreenItem.setTitle(AppBannerManager.getHomescreenLanguageOption());
            }
            homescreenItem.setVisible(homescreenItemVisible);

            // Hide request desktop site on all chrome:// pages except for the NTP. Check request
            // desktop site if it's activated on this page.
            MenuItem requestItem = menu.findItem(R.id.request_desktop_site_id);
            requestItem.setVisible(!isChromeScheme || currentTab.isNativePage());
            requestItem.setChecked(currentTab.getUseDesktopUserAgent());
            requestItem.setTitleCondensed(requestItem.isChecked()
                    ? mActivity.getString(R.string.menu_request_desktop_site_on)
                    : mActivity.getString(R.string.menu_request_desktop_site_off));

            // Only display reader mode settings menu option if the current page is in reader mode.
            menu.findItem(R.id.reader_mode_prefs_id)
                    .setVisible(DomDistillerUrlUtils.isDistilledPage(currentTab.getUrl()));

            // Only display the Enter VR button if VR Shell Dev environment is enabled.
            menu.findItem(R.id.enter_vr_id).setVisible(
                    CommandLine.getInstance().hasSwitch(ChromeSwitches.ENABLE_VR_SHELL_DEV));
        }

        if (isOverviewMenu) {
            if (isIncognito) {
                // Hide normal close all tabs item.
                menu.findItem(R.id.close_all_tabs_menu_id).setVisible(false);
                // Enable close incognito tabs only if there are incognito tabs.
                menu.findItem(R.id.close_all_incognito_tabs_menu_id).setEnabled(true);
            } else {
                // Hide close incognito tabs item.
                menu.findItem(R.id.close_all_incognito_tabs_menu_id).setVisible(false);
                // Enable close all tabs if there are normal tabs or incognito tabs.
                menu.findItem(R.id.close_all_tabs_menu_id).setEnabled(
                        mActivity.getTabModelSelector().getTotalTabCount() > 0);
            }
        }

        // Disable new incognito tab when it is blocked (e.g. by a policy).
        // findItem(...).setEnabled(...)" is not enough here, because of the inflated
        // main_menu.xml contains multiple items with the same id in different groups
        // e.g.: new_incognito_tab_menu_id.
        disableEnableMenuItem(menu, R.id.new_incognito_tab_menu_id,
                true,
                PrefServiceBridge.getInstance().isIncognitoModeEnabled(),
                PrefServiceBridge.getInstance().isIncognitoModeManaged());
        mActivity.prepareMenu(menu);
    }

    /**
     * Notify the delegate that the load state changed.
     * @param isLoading Whether the page is currently loading.
     */
    public void loadingStateChanged(boolean isLoading) {
        if (mReloadMenuItem != null) {
            mReloadMenuItem.getIcon().setLevel(isLoading
                    ? RELOAD_BUTTON_LEVEL_STOP_LOADING : RELOAD_BUTTON_LEVEL_RELOAD);
            mReloadMenuItem.setTitle(isLoading
                    ? R.string.accessibility_btn_stop_loading : R.string.accessibility_btn_refresh);
        }
    }

    /**
     * Notify the delegate that menu was dismissed.
     */
    public void onMenuDismissed() {
        mReloadMenuItem = null;
    }

    // Set enabled to be |enable| for all MenuItems with |id| in |menu|.
    // If |managed| is true then the "Managed By Enterprise" icon is shown next to the menu.
    private void disableEnableMenuItem(
            Menu menu, int id, boolean visible, boolean enabled, boolean managed) {
        for (int i = 0; i < menu.size(); ++i) {
            MenuItem item = menu.getItem(i);
            if (item.getItemId() == id && item.isVisible()) {
                item.setVisible(visible);
                item.setEnabled(enabled);
                if (managed) {
                    item.setIcon(ManagedPreferencesUtils.getManagedByEnterpriseIconId());
                } else {
                    item.setIcon(null);
                }
            }
        }
    }

    /**
     * @return Resource layout id for the footer if there should be one. O otherwise.
     */
    public int getFooterResourceId() {
        return 0;
    }

    /**
     * Updates the bookmarks bridge.
     *
     * @param bookmarkBridge The bookmarks bridge.
     */
    public void setBookmarkBridge(BookmarkBridge bookmarkBridge) {
        mBookmarkBridge = bookmarkBridge;
    }

    /**
     * Updates the bookmark item's visibility.
     *
     * @param bookmarkMenuItem {@link MenuItem} for adding/editing the bookmark.
     * @param currentTab        Current tab being displayed.
     */
    protected void updateBookmarkMenuItem(MenuItem bookmarkMenuItem, Tab currentTab) {
        bookmarkMenuItem.setEnabled(mBookmarkBridge.isEditBookmarksEnabled());
        if (currentTab.getBookmarkId() != Tab.INVALID_BOOKMARK_ID) {
            bookmarkMenuItem.setIcon(R.drawable.btn_star_filled);
            bookmarkMenuItem.setChecked(true);
            bookmarkMenuItem.setTitleCondensed(mActivity.getString(R.string.edit_bookmark));
        } else {
            bookmarkMenuItem.setIcon(R.drawable.btn_star);
            bookmarkMenuItem.setChecked(false);
            bookmarkMenuItem.setTitleCondensed(null);
        }
    }
}