/*
 * Copyright (C) 2023 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.launcher3.apppairs

import android.content.Context
import android.graphics.Canvas
import android.graphics.Rect
import android.util.AttributeSet
import android.view.Gravity
import android.widget.FrameLayout
import androidx.annotation.OpenForTesting
import com.android.launcher3.DeviceProfile
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener
import com.android.launcher3.icons.BitmapInfo
import com.android.launcher3.model.data.AppPairInfo
import com.android.launcher3.views.ActivityContext

/**
 * A FrameLayout marking the area on an [AppPairIcon] where the visual icon will be drawn. One of
 * two child UI elements on an [AppPairIcon], along with a BubbleTextView holding the text title.
 */
@OpenForTesting
open class AppPairIconGraphic
@JvmOverloads
constructor(context: Context, attrs: AttributeSet? = null) :
    FrameLayout(context, attrs), OnDeviceProfileChangeListener {
    private val TAG = "AppPairIconGraphic"

    companion object {
        /** Composes a drawable for this icon, consisting of a background and 2 app icons. */
        @JvmStatic
        fun composeDrawable(
            appPairInfo: AppPairInfo,
            p: AppPairIconDrawingParams,
        ): AppPairIconDrawable {
            // Generate new icons, using themed flag since the icon is drawn on homescreen
            val appIcon1 = appPairInfo.getFirstApp().newIcon(p.context, BitmapInfo.FLAG_THEMED)
            val appIcon2 = appPairInfo.getSecondApp().newIcon(p.context, BitmapInfo.FLAG_THEMED)
            appIcon1.setBounds(0, 0, p.memberIconSize.toInt(), p.memberIconSize.toInt())
            appIcon2.setBounds(0, 0, p.memberIconSize.toInt(), p.memberIconSize.toInt())

            // If icons are unlaunchable due to screen size, manually override disabled appearance.
            // (otherwise, leave disabled state alone; icons will naturally inherit the app's state)
            val (isApp1Launchable, isApp2Launchable) = appPairInfo.isLaunchable(p.context)
            if (!isApp1Launchable) appIcon1?.isDisabled = true
            if (!isApp2Launchable) appIcon2?.isDisabled = true

            // Create icon drawable.
            val fullIconDrawable = AppPairIconDrawable(p, appIcon1, appIcon2)
            fullIconDrawable.setBounds(0, 0, p.iconSize, p.iconSize)

            return fullIconDrawable
        }
    }

    private lateinit var parentIcon: AppPairIcon
    private lateinit var drawParams: AppPairIconDrawingParams
    lateinit var drawable: AppPairIconDrawable

    fun init(icon: AppPairIcon, container: Int) {
        parentIcon = icon
        drawParams = AppPairIconDrawingParams(context, container)
        drawable = composeDrawable(icon.info, drawParams)

        // Center the drawable area in the larger icon canvas
        val lp: LayoutParams = layoutParams as LayoutParams
        lp.gravity = Gravity.CENTER_HORIZONTAL
        lp.height = drawParams.iconSize
        lp.width = drawParams.iconSize
        layoutParams = lp
    }

    override fun onAttachedToWindow() {
        super.onAttachedToWindow()
        getActivityContext().addOnDeviceProfileChangeListener(this)
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        getActivityContext().removeOnDeviceProfileChangeListener(this)
    }

    private fun getActivityContext(): ActivityContext {
        return ActivityContext.lookupContext(context)
    }

    /** When device profile changes, update orientation */
    override fun onDeviceProfileChanged(dp: DeviceProfile) {
        drawParams.updateOrientation(dp)
        redraw()
    }

    /**
     * When the icon is temporary moved to a different colored surface, update the background color.
     * Calling this method with [null] reverts the icon back to its default color.
     */
    fun onTemporaryContainerChange(newContainer: Int?) {
        drawParams.updateBgColor(newContainer ?: parentIcon.container)
        redraw()
    }

    /**
     * Gets this icon graphic's visual bounds, with respect to the parent icon's coordinate system.
     */
    fun getIconBounds(outBounds: Rect) {
        outBounds.set(0, 0, drawParams.backgroundSize.toInt(), drawParams.backgroundSize.toInt())
        outBounds.offset(
            // x-coordinate in parent's coordinate system
            ((parentIcon.width - drawParams.backgroundSize) / 2).toInt(),
            // y-coordinate in parent's coordinate system
            (parentIcon.paddingTop + drawParams.standardIconPadding + drawParams.outerPadding)
                .toInt(),
        )
    }

    /** Updates the icon drawable and redraws it */
    fun redraw() {
        drawable = composeDrawable(parentIcon.info, drawParams)
        invalidate()
    }

    override fun dispatchDraw(canvas: Canvas) {
        super.dispatchDraw(canvas)
        drawable.draw(canvas)
    }

    /** Sets the scale of the icon background while hovered. */
    fun setHoverScale(scale: Float) {
        drawParams.hoverScale = scale
        redraw()
    }

    /** Gets the scale of the icon background while hovered. */
    fun getHoverScale(): Float {
        return drawParams.hoverScale
    }
}
