/*
 * Copyright (C) 2022 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.systemui.statusbar.phone

import android.service.notification.StatusBarNotification
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.StatusBarIconView.STATE_DOT
import com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
import org.mockito.Mockito.`when` as whenever

/** Tests for {@link NotificationIconContainer}. */
@SmallTest
@RunWith(AndroidTestingRunner::class)
@RunWithLooper
class NotificationIconContainerTest : SysuiTestCase() {

    private val iconContainer = NotificationIconContainer(context, /* attrs= */ null)

    @Test
    fun calculateWidthFor_zeroIcons_widthIsZero() {
        assertEquals(
            /* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 0f),
            /* actual= */ 0f
        )
    }

    @Test
    fun calculateWidthFor_oneIcon_widthForOneIcon() {
        iconContainer.setActualPaddingStart(10f)
        iconContainer.setActualPaddingEnd(10f)
        iconContainer.setIconSize(10)

        assertEquals(
            /* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 1f),
            /* actual= */ 30f
        )
    }

    @Test
    fun calculateWidthFor_fourIcons_widthForFourIcons() {
        iconContainer.setActualPaddingStart(10f)
        iconContainer.setActualPaddingEnd(10f)
        iconContainer.setIconSize(10)

        assertEquals(
            /* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 4f),
            /* actual= */ 60f
        )
    }

    @Test
    fun calculateWidthFor_fiveIcons_widthForFourIcons() {
        iconContainer.setActualPaddingStart(10f)
        iconContainer.setActualPaddingEnd(10f)
        iconContainer.setIconSize(10)
        assertEquals(
            /* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 5f),
            /* actual= */ 60f
        )
    }

    @Test
    fun calculateIconXTranslations_shortShelfOneIcon_atCorrectXWithoutOverflowDot() {
        iconContainer.setActualPaddingStart(10f)
        iconContainer.setActualPaddingEnd(10f)
        iconContainer.setIconSize(10)

        val icon = mockStatusBarIcon()
        iconContainer.addView(icon)
        assertEquals(1, iconContainer.childCount)

        val iconState = iconContainer.getIconState(icon)
        iconState.iconAppearAmount = 1f

        val width = iconContainer.calculateWidthFor(/* numIcons= */ 1f)
        iconContainer.setActualLayoutWidth(width.toInt())

        iconContainer.calculateIconXTranslations()
        assertEquals(10f, iconState.xTranslation)
        assertFalse(iconContainer.areIconsOverflowing())
    }

    @Test
    fun calculateIconXTranslations_shortShelfFourIcons_atCorrectXWithoutOverflowDot() {
        iconContainer.setActualPaddingStart(10f)
        iconContainer.setActualPaddingEnd(10f)
        iconContainer.setIconSize(10)

        val iconOne = mockStatusBarIcon()
        val iconTwo = mockStatusBarIcon()
        val iconThree = mockStatusBarIcon()
        val iconFour = mockStatusBarIcon()

        iconContainer.addView(iconOne)
        iconContainer.addView(iconTwo)
        iconContainer.addView(iconThree)
        iconContainer.addView(iconFour)
        assertEquals(4, iconContainer.childCount)

        val width = iconContainer.calculateWidthFor(/* numIcons= */ 4f)
        iconContainer.setActualLayoutWidth(width.toInt())

        iconContainer.calculateIconXTranslations()
        assertEquals(10f, iconContainer.getIconState(iconOne).xTranslation)
        assertEquals(20f, iconContainer.getIconState(iconTwo).xTranslation)
        assertEquals(30f, iconContainer.getIconState(iconThree).xTranslation)
        assertEquals(40f, iconContainer.getIconState(iconFour).xTranslation)

        assertFalse(iconContainer.areIconsOverflowing())
    }

    @Test
    fun calculateIconXTranslations_shortShelfFiveIcons_atCorrectXWithOverflowDot() {
        iconContainer.setActualPaddingStart(10f)
        iconContainer.setActualPaddingEnd(10f)
        iconContainer.setIconSize(10)

        val iconOne = mockStatusBarIcon()
        val iconTwo = mockStatusBarIcon()
        val iconThree = mockStatusBarIcon()
        val iconFour = mockStatusBarIcon()
        val iconFive = mockStatusBarIcon()

        iconContainer.addView(iconOne)
        iconContainer.addView(iconTwo)
        iconContainer.addView(iconThree)
        iconContainer.addView(iconFour)
        iconContainer.addView(iconFive)
        assertEquals(5, iconContainer.childCount)

        val width = iconContainer.calculateWidthFor(/* numIcons= */ 5f)
        iconContainer.setActualLayoutWidth(width.toInt())

        iconContainer.calculateIconXTranslations()
        assertEquals(10f, iconContainer.getIconState(iconOne).xTranslation)
        assertEquals(20f, iconContainer.getIconState(iconTwo).xTranslation)
        assertEquals(30f, iconContainer.getIconState(iconThree).xTranslation)
        assertTrue(iconContainer.areIconsOverflowing())
    }

    @Test
    fun calculateIconXTranslations_givenWidthEnoughForThreeIcons_atCorrectXWithoutOverflowDot() {
        iconContainer.setActualPaddingStart(0f)
        iconContainer.setActualPaddingEnd(0f)
        iconContainer.setActualLayoutWidth(30)
        iconContainer.setIconSize(10)

        val iconOne = mockStatusBarIcon()
        val iconTwo = mockStatusBarIcon()
        val iconThree = mockStatusBarIcon()

        iconContainer.addView(iconOne)
        iconContainer.addView(iconTwo)
        iconContainer.addView(iconThree)
        assertEquals(3, iconContainer.childCount)

        iconContainer.calculateIconXTranslations()
        assertEquals(0f, iconContainer.getIconState(iconOne).xTranslation)
        assertEquals(10f, iconContainer.getIconState(iconTwo).xTranslation)
        assertEquals(20f, iconContainer.getIconState(iconThree).xTranslation)
        assertFalse(iconContainer.areIconsOverflowing())
    }

    @Test
    fun calculateIconXTranslations_givenWidthNotEnoughForFourIcons_atCorrectXWithOverflowDot() {
        iconContainer.setActualPaddingStart(0f)
        iconContainer.setActualPaddingEnd(0f)
        iconContainer.setActualLayoutWidth(35)
        iconContainer.setIconSize(10)

        val iconOne = mockStatusBarIcon()
        val iconTwo = mockStatusBarIcon()
        val iconThree = mockStatusBarIcon()
        val iconFour = mockStatusBarIcon()

        iconContainer.addView(iconOne)
        iconContainer.addView(iconTwo)
        iconContainer.addView(iconThree)
        iconContainer.addView(iconFour)
        assertEquals(4, iconContainer.childCount)

        iconContainer.calculateIconXTranslations()
        assertEquals(0f, iconContainer.getIconState(iconOne).xTranslation)
        assertEquals(10f, iconContainer.getIconState(iconTwo).xTranslation)
        assertEquals(STATE_DOT, iconContainer.getIconState(iconThree).visibleState)
        assertEquals(STATE_HIDDEN, iconContainer.getIconState(iconFour).visibleState)
        assertTrue(iconContainer.areIconsOverflowing())
    }

    @Test
    fun shouldForceOverflow_appearingAboveSpeedBump_true() {
        val forceOverflow =
            iconContainer.shouldForceOverflow(
                /* i= */ 1,
                /* speedBumpIndex= */ 0,
                /* iconAppearAmount= */ 1f,
                /* maxVisibleIcons= */ 5
            )
        assertTrue(forceOverflow)
    }

    @Test
    fun shouldForceOverflow_moreThanMaxVisible_true() {
        val forceOverflow =
            iconContainer.shouldForceOverflow(
                /* i= */ 10,
                /* speedBumpIndex= */ 11,
                /* iconAppearAmount= */ 0f,
                /* maxVisibleIcons= */ 5
            )
        assertTrue(forceOverflow)
    }

    @Test
    fun shouldForceOverflow_belowSpeedBumpAndLessThanMaxVisible_false() {
        val forceOverflow =
            iconContainer.shouldForceOverflow(
                /* i= */ 0,
                /* speedBumpIndex= */ 11,
                /* iconAppearAmount= */ 0f,
                /* maxVisibleIcons= */ 5
            )
        assertFalse(forceOverflow)
    }

    @Test
    fun isOverflowing_lastChildXLessThanLayoutEnd_false() {
        val isOverflowing =
            iconContainer.isOverflowing(
                /* isLastChild= */ true,
                /* translationX= */ 0f,
                /* layoutEnd= */ 10f,
                /* iconSize= */ 2f,
            )
        assertFalse(isOverflowing)
    }

    @Test
    fun isOverflowing_lastChildXEqualToLayoutEnd_true() {
        val isOverflowing =
            iconContainer.isOverflowing(
                /* isLastChild= */ true,
                /* translationX= */ 10f,
                /* layoutEnd= */ 10f,
                /* iconSize= */ 2f,
            )
        assertTrue(isOverflowing)
    }

    @Test
    fun isOverflowing_lastChildXGreaterThanDotX_true() {
        val isOverflowing =
            iconContainer.isOverflowing(
                /* isLastChild= */ true,
                /* translationX= */ 9f,
                /* layoutEnd= */ 10f,
                /* iconSize= */ 2f,
            )
        assertTrue(isOverflowing)
    }

    @Test
    fun isOverflowing_lastChildXGreaterThanLayoutEnd_true() {
        val isOverflowing =
            iconContainer.isOverflowing(
                /* isLastChild= */ true,
                /* translationX= */ 20f,
                /* layoutEnd= */ 10f,
                /* iconSize= */ 2f,
            )
        assertTrue(isOverflowing)
    }

    @Test
    fun isOverflowing_notLastChildXLessThanDotX_false() {
        val isOverflowing =
            iconContainer.isOverflowing(
                /* isLastChild= */ false,
                /* translationX= */ 0f,
                /* layoutEnd= */ 10f,
                /* iconSize= */ 2f,
            )
        assertFalse(isOverflowing)
    }

    @Test
    fun isOverflowing_notLastChildXGreaterThanDotX_true() {
        val isOverflowing =
            iconContainer.isOverflowing(
                /* isLastChild= */ false,
                /* translationX= */ 20f,
                /* layoutEnd= */ 10f,
                /* iconSize= */ 2f,
            )
        assertTrue(isOverflowing)
    }

    @Test
    fun isOverflowing_notLastChildXEqualToDotX_true() {
        val isOverflowing =
            iconContainer.isOverflowing(
                /* isLastChild= */ false,
                /* translationX= */ 8f,
                /* layoutEnd= */ 10f,
                /* iconSize= */ 2f,
            )
        assertTrue(isOverflowing)
    }

    private fun mockStatusBarIcon(): StatusBarIconView {
        val iconView = mock(StatusBarIconView::class.java)
        whenever(iconView.width).thenReturn(10)

        val icon = mock(android.graphics.drawable.Icon::class.java)
        whenever(iconView.sourceIcon).thenReturn(icon)

        val sbn = mock(StatusBarNotification::class.java)
        whenever(sbn.groupKey).thenReturn("groupKey")
        whenever(iconView.notification).thenReturn(sbn)
        return iconView
    }
}
