/*
 * Copyright (C) 2020 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.notification.row

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.AnimatorSet
import android.animation.ValueAnimator
import android.app.Dialog
import android.content.Context
import android.content.pm.ShortcutInfo
import android.graphics.Color
import android.graphics.PixelFormat
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import android.text.SpannableStringBuilder
import android.text.style.BulletSpan
import android.view.Gravity
import android.view.View
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.view.Window
import android.view.WindowInsets.Type.statusBars
import android.view.WindowManager
import android.view.animation.Interpolator
import android.view.animation.PathInterpolator
import android.widget.ImageView
import android.widget.TextView
import com.android.systemui.Prefs
import com.android.systemui.R
import com.android.systemui.animation.Interpolators.LINEAR_OUT_SLOW_IN
import com.android.systemui.people.widget.PeopleSpaceWidgetManager
import com.android.systemui.statusbar.notification.row.NotificationConversationInfo.OnConversationSettingsClickListener
import com.android.systemui.statusbar.phone.ShadeController
import javax.inject.Inject

/**
 * Controller to handle presenting the priority conversations onboarding dialog
 */
class PriorityOnboardingDialogController @Inject constructor(
    val view: View,
    val context: Context,
    private val ignoresDnd: Boolean,
    private val showsAsBubble: Boolean,
    val icon: Drawable,
    private val onConversationSettingsClickListener: OnConversationSettingsClickListener,
    val badge: Drawable,
    private val peopleSpaceWidgetManager: PeopleSpaceWidgetManager,
    private val shadeController: ShadeController
) {

    private lateinit var dialog: Dialog
    private lateinit var shortcutInfo: ShortcutInfo
    private val OVERSHOOT: Interpolator = PathInterpolator(0.4f, 0f, 0.2f, 1.4f)
    private val IMPORTANCE_ANIM_DELAY = 150L
    private val IMPORTANCE_ANIM_GROW_DURATION = 250L
    private val IMPORTANCE_ANIM_SHRINK_DURATION = 200L
    private val IMPORTANCE_ANIM_SHRINK_DELAY = 25L

    fun init(info: ShortcutInfo) {
        shortcutInfo = info
        initDialog()
    }

    fun show() {
        dialog.show()
    }

    private fun done() {
        // Log that the user has seen the onboarding
        Prefs.putBoolean(context, Prefs.Key.HAS_SEEN_PRIORITY_ONBOARDING_IN_S, true)
        dialog.dismiss()
        shadeController.animateCollapsePanels()
        peopleSpaceWidgetManager.requestPinAppWidget(shortcutInfo)
    }

    private fun settings() {
        // Log that the user has seen the onboarding
        Prefs.putBoolean(context, Prefs.Key.HAS_SEEN_PRIORITY_ONBOARDING_IN_S, true)
        dialog.dismiss()
        onConversationSettingsClickListener?.onClick()
    }

    class Builder @Inject constructor() {
        private lateinit var view: View
        private lateinit var context: Context
        private var ignoresDnd = false
        private var showAsBubble = false
        private lateinit var icon: Drawable
        private lateinit var onConversationSettingsClickListener:
                OnConversationSettingsClickListener
        private lateinit var badge: Drawable
        private lateinit var peopleSpaceWidgetManager: PeopleSpaceWidgetManager
        private lateinit var shadeController: ShadeController

        fun setView(v: View): Builder {
            view = v
            return this
        }

        fun setContext(c: Context): Builder {
            context = c
            return this
        }

        fun setIgnoresDnd(ignore: Boolean): Builder {
            ignoresDnd = ignore
            return this
        }

        fun setShowsAsBubble(bubble: Boolean): Builder {
            showAsBubble = bubble
            return this
        }

        fun setIcon(draw: Drawable): Builder {
            icon = draw
            return this
        }
        fun setBadge(badge: Drawable): Builder {
            this.badge = badge
            return this
        }

        fun setOnSettingsClick(onClick: OnConversationSettingsClickListener): Builder {
            onConversationSettingsClickListener = onClick
            return this
        }

        fun setShadeController(shadeController: ShadeController): Builder {
            this.shadeController = shadeController
            return this
        }

        fun setPeopleSpaceWidgetManager(peopleSpaceWidgetManager: PeopleSpaceWidgetManager):
                Builder {
            this.peopleSpaceWidgetManager = peopleSpaceWidgetManager
            return this
        }

        fun build(): PriorityOnboardingDialogController {
            val controller = PriorityOnboardingDialogController(
                    view, context, ignoresDnd, showAsBubble, icon,
                    onConversationSettingsClickListener, badge, peopleSpaceWidgetManager,
                    shadeController)
            return controller
        }
    }

    private fun initDialog() {
        dialog = Dialog(context)

        if (dialog.window == null) {
            throw IllegalStateException("Need a window for the onboarding dialog to show")
        }

        dialog.window?.requestFeature(Window.FEATURE_NO_TITLE)
        // Prevent a11y readers from reading the first element in the dialog twice
        dialog.setTitle("\u00A0")
        dialog.apply {
            setContentView(view)
            setCanceledOnTouchOutside(true)

            findViewById<TextView>(R.id.done_button)?.setOnClickListener {
                done()
            }

            findViewById<TextView>(R.id.settings_button)?.setOnClickListener {
                settings()
            }

            findViewById<ImageView>(R.id.conversation_icon)?.setImageDrawable(icon)
            findViewById<ImageView>(R.id.icon)?.setImageDrawable(badge)
            val mImportanceRingView = findViewById<ImageView>(R.id.conversation_icon_badge_ring)
            val conversationIconBadgeBg = findViewById<ImageView>(R.id.conversation_icon_badge_bg)

            val ring: GradientDrawable = mImportanceRingView.drawable as GradientDrawable
            ring.mutate()
            val bg = conversationIconBadgeBg.drawable as GradientDrawable
            bg.mutate()
            val ringColor = context.getResources()
                    .getColor(com.android.internal.R.color.conversation_important_highlight)
            val standardThickness = context.resources.getDimensionPixelSize(
                    com.android.internal.R.dimen.importance_ring_stroke_width)
            val largeThickness = context.resources.getDimensionPixelSize(
                    com.android.internal.R.dimen.importance_ring_anim_max_stroke_width)
            val standardSize = context.resources.getDimensionPixelSize(
                    com.android.internal.R.dimen.importance_ring_size)
            val baseSize = standardSize - standardThickness * 2
            val largeSize = baseSize + largeThickness * 2
            val bgSize = context.resources.getDimensionPixelSize(
                    com.android.internal.R.dimen.conversation_icon_size_badged)

            val animatorUpdateListener: ValueAnimator.AnimatorUpdateListener =
                    ValueAnimator.AnimatorUpdateListener { animation ->
                val strokeWidth = animation.animatedValue as Int
                ring.setStroke(strokeWidth, ringColor)
                val newSize = baseSize + strokeWidth * 2
                ring.setSize(newSize, newSize)
                mImportanceRingView.invalidate()
            }

            val growAnimation: ValueAnimator = ValueAnimator.ofInt(0, largeThickness)
            growAnimation.interpolator = LINEAR_OUT_SLOW_IN
            growAnimation.duration = IMPORTANCE_ANIM_GROW_DURATION
            growAnimation.addUpdateListener(animatorUpdateListener)

            val shrinkAnimation: ValueAnimator =
                    ValueAnimator.ofInt(largeThickness, standardThickness)
            shrinkAnimation.duration = IMPORTANCE_ANIM_SHRINK_DURATION
            shrinkAnimation.startDelay = IMPORTANCE_ANIM_SHRINK_DELAY
            shrinkAnimation.interpolator = OVERSHOOT
            shrinkAnimation.addUpdateListener(animatorUpdateListener)
            shrinkAnimation.addListener(object : AnimatorListenerAdapter() {
                override fun onAnimationStart(animation: Animator?) {
                    // Shrink the badge bg so that it doesn't peek behind the animation
                    bg.setSize(baseSize, baseSize)
                    conversationIconBadgeBg.invalidate()
                }

                override fun onAnimationEnd(animation: Animator?) {
                    // Reset bg back to normal size
                    bg.setSize(bgSize, bgSize)
                    conversationIconBadgeBg.invalidate()
                }
            })

            val anims = AnimatorSet()
            anims.startDelay = IMPORTANCE_ANIM_DELAY
            anims.playSequentially(growAnimation, shrinkAnimation)

            val gapWidth = dialog.context.getResources().getDimensionPixelSize(
                    R.dimen.conversation_onboarding_bullet_gap_width)
            val description = SpannableStringBuilder()
            description.append(context.getText(R.string.priority_onboarding_show_at_top_text),
                    BulletSpan(gapWidth), /* flags */0)
            description.append(System.lineSeparator())
            description.append(context.getText(R.string.priority_onboarding_show_avatar_text),
                    BulletSpan(gapWidth), /* flags */0)
            if (showsAsBubble) {
                description.append(System.lineSeparator())
                description.append(context.getText(
                        R.string.priority_onboarding_appear_as_bubble_text),
                        BulletSpan(gapWidth), /* flags */0)
            }
            if (ignoresDnd) {
                description.append(System.lineSeparator())
                description.append(context.getText(R.string.priority_onboarding_ignores_dnd_text),
                        BulletSpan(gapWidth), /* flags */0)
            }
            findViewById<TextView>(R.id.behaviors).setText(description)

            window?.apply {
                setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
                addFlags(wmFlags)
                setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL)
                setWindowAnimations(com.android.internal.R.style.Animation_InputMethod)

                attributes = attributes.apply {
                    format = PixelFormat.TRANSLUCENT
                    title = PriorityOnboardingDialogController::class.java.simpleName
                    gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL
                    fitInsetsTypes = attributes.fitInsetsTypes and statusBars().inv()
                    width = MATCH_PARENT
                    height = WRAP_CONTENT
                }
            }
            anims.start()
        }
    }

    private val wmFlags = (WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS
            or WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
            or WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED)
}
