// Copyright 2016 The Chromium Authors
// 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;

import android.graphics.Color;

import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.MediumTest;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.chromium.base.test.util.CallbackHelper;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.CriteriaHelper;
import org.chromium.base.test.util.DisabledTest;
import org.chromium.base.test.util.Feature;
import org.chromium.base.test.util.Restriction;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabSelectionType;
import org.chromium.chrome.browser.tabmodel.TabModel;
import org.chromium.chrome.browser.theme.ThemeColorProvider.ThemeColorObserver;
import org.chromium.chrome.browser.theme.TopUiThemeColorProvider;
import org.chromium.chrome.browser.ui.native_page.FrozenNativePage;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import org.chromium.chrome.test.util.NewTabPageTestUtils;
import org.chromium.components.browser_ui.styles.ChromeColors;
import org.chromium.components.embedder_support.util.UrlConstants;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.content_public.browser.test.util.JavaScriptUtils;
import org.chromium.content_public.browser.test.util.TestThreadUtils;
import org.chromium.net.test.EmbeddedTestServer;
import org.chromium.ui.test.util.UiRestriction;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;

/**
 * Tests related to the Tab's theme color.
 */
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
public class TabThemeTest {
    @Rule
    public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();

    private static final String TEST_PAGE = "/chrome/test/data/android/simple.html";
    private static final String THEMED_TEST_PAGE =
            "/chrome/test/data/android/theme_color_test.html";

    // The theme_color_test.html page uses a pure red theme color.
    private static final int THEME_COLOR = 0xffff0000;

    /**
     * A WebContentsObserver for watching changes in the theme color.
     */
    private static class ThemeColorWebContentsObserver implements ThemeColorObserver {
        private CallbackHelper mCallbackHelper;
        private int mColor;

        public ThemeColorWebContentsObserver() {
            mCallbackHelper = new CallbackHelper();
        }

        @Override
        public void onThemeColorChanged(int color, boolean shouldAnimate) {
            mColor = color;
            mCallbackHelper.notifyCalled();
        }

        public CallbackHelper getCallbackHelper() {
            return mCallbackHelper;
        }

        public int getColor() {
            return mColor;
        }
    }

    @Before
    public void setUp() throws InterruptedException {
        mActivityTestRule.startMainActivityOnBlankPage();
    }

    /**
     * AssertEquals two colors as strings so the text output shows their hex value.
     */
    private void assertColorsEqual(int color1, int color2) {
        Assert.assertEquals(Integer.toHexString(color1), Integer.toHexString(color2));
    }

    private int getThemeColor() throws ExecutionException {
        return TestThreadUtils.runOnUiThreadBlocking(
                mActivityTestRule.getActivity()
                        .getRootUiCoordinatorForTesting()
                        .getTopUiThemeColorProvider()::getThemeColor);
    }

    private static int getDefaultThemeColor(Tab tab) throws ExecutionException {
        return TestThreadUtils.runOnUiThreadBlocking(() -> {
            return ChromeColors.getDefaultThemeColor(tab.getContext(), tab.isIncognito());
        });
    }

    /**
     * Test that the toolbar has the correct color set.
     */
    @Test
    @Feature({"Toolbar-Theme-Color"})
    @MediumTest
    @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
    @DisabledTest(message = "Flaky: https://crbug.com/1355516")
    public void testThemeColorIsCorrect() throws ExecutionException, TimeoutException {
        EmbeddedTestServer testServer = EmbeddedTestServer.createAndStartServer(
                ApplicationProvider.getApplicationContext());

        final Tab tab = mActivityTestRule.getActivity().getActivityTab();

        ThemeColorWebContentsObserver colorObserver = new ThemeColorWebContentsObserver();
        CallbackHelper themeColorHelper = colorObserver.getCallbackHelper();
        TestThreadUtils.runOnUiThreadBlocking(() -> {
            mActivityTestRule.getActivity()
                    .getRootUiCoordinatorForTesting()
                    .getTopUiThemeColorProvider()
                    .addThemeColorObserver(colorObserver);
        });

        // Navigate to a themed page.
        int curCallCount = themeColorHelper.getCallCount();
        mActivityTestRule.loadUrl(testServer.getURL(THEMED_TEST_PAGE));
        themeColorHelper.waitForCallback(curCallCount, 1);
        assertColorsEqual(THEME_COLOR, getThemeColor());

        // Setting page theme color to white is forbidden.
        JavaScriptUtils.executeJavaScriptAndWaitForResult(
                mActivityTestRule.getActivity().getActivityTab().getWebContents(),
                "document.querySelector(meta).setAttribute('content', 'white');");
        themeColorHelper.waitForCallback(curCallCount, 1);
        assertColorsEqual(THEME_COLOR, colorObserver.getColor());
        assertColorsEqual(THEME_COLOR, getThemeColor());

        // Navigate to a native page from a themed page.
        mActivityTestRule.loadUrl("chrome://newtab/");
        // WebContents does not set theme color for native pages, so don't wait for the call.
        assertColorsEqual(getDefaultThemeColor(tab), getThemeColor());

        // Navigate to a themed page from a native page.
        curCallCount = themeColorHelper.getCallCount();
        mActivityTestRule.loadUrl(testServer.getURL(THEMED_TEST_PAGE));
        themeColorHelper.waitForCallback(curCallCount, 1);
        assertColorsEqual(THEME_COLOR, colorObserver.getColor());
        assertColorsEqual(THEME_COLOR, getThemeColor());

        // Navigate to a non-native non-themed page.
        curCallCount = themeColorHelper.getCallCount();
        mActivityTestRule.loadUrl(testServer.getURL(TEST_PAGE));
        themeColorHelper.waitForCallback(curCallCount, 1);
        assertColorsEqual(getDefaultThemeColor(tab), colorObserver.getColor());
        assertColorsEqual(getDefaultThemeColor(tab), getThemeColor());

        // Navigate to a themed page from a non-native page.
        curCallCount = themeColorHelper.getCallCount();
        mActivityTestRule.loadUrl(testServer.getURL(THEMED_TEST_PAGE));
        themeColorHelper.waitForCallback(curCallCount, 1);
        assertColorsEqual(THEME_COLOR, colorObserver.getColor());
        assertColorsEqual(THEME_COLOR, getThemeColor());
    }

    @Test
    @Feature({"Toolbar-Theme-Color"})
    @MediumTest
    @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
    public void testOverlayColorOnFrozenNativePages() throws ExecutionException, TimeoutException {
        EmbeddedTestServer testServer = EmbeddedTestServer.createAndStartServer(
                ApplicationProvider.getApplicationContext());

        final Tab tab = mActivityTestRule.getActivity().getActivityTab();

        final TopUiThemeColorProvider colorProvider = mActivityTestRule.getActivity()
                                                              .getRootUiCoordinatorForTesting()
                                                              .getTopUiThemeColorProvider();

        ThemeColorObserver colorObserver = (color, animate) -> {
            Assert.assertNotSame("Theme color should never be 0 or TRANSPARENT!", Color.TRANSPARENT,
                    colorProvider.getSceneLayerBackground(tab));
        };

        TestThreadUtils.runOnUiThreadBlocking(
                () -> colorProvider.addThemeColorObserver(colorObserver));

        // Load the ntp.
        TestThreadUtils.runOnUiThreadBlocking(
                () -> tab.loadUrl(new LoadUrlParams(UrlConstants.NTP_URL)));

        NewTabPageTestUtils.waitForNtpLoaded(tab);

        // Load a second tab so we can freeze the first.
        mActivityTestRule.loadUrlInNewTab(testServer.getURL(THEMED_TEST_PAGE));

        // Freeze the ntp on the first tab.
        TestThreadUtils.runOnUiThreadBlocking(() -> tab.freezeNativePage());

        CriteriaHelper.pollUiThread(() -> tab.getNativePage() instanceof FrozenNativePage);

        // Switch back to the now frozen tab.
        TestThreadUtils.runOnUiThreadBlocking(() -> {
            TabModel model = mActivityTestRule.getActivity()
                                     .getTabModelSelectorSupplier()
                                     .get()
                                     .getCurrentModel();
            model.setIndex(model.indexOf(tab), TabSelectionType.FROM_USER, true);
        });

        Assert.assertEquals("The tab should still be frozen!",
                mActivityTestRule.getActivity()
                        .getActivityTabProvider()
                        .get()
                        .getNativePage()
                        .getClass(),
                FrozenNativePage.class);

        TestThreadUtils.runOnUiThreadBlocking(() -> {
            Assert.assertNotSame("Theme color should never be 0 or TRANSPARENT!", Color.TRANSPARENT,
                    colorProvider.getSceneLayerBackground(tab));
        });
    }
}
