/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.focus.shortcut

import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.util.TypedValue
import androidx.core.content.ContextCompat
import androidx.core.graphics.createBitmap
import androidx.core.net.toUri
import mozilla.components.support.ktx.kotlin.stripCommonSubdomains
import org.mozilla.focus.R
import org.mozilla.focus.shortcut.IconGenerator.generateAdaptiveLauncherIcon

/**
 * Utility for generating launcher icons for web shortcuts.
 */
object IconGenerator {
    private const val TEXT_SIZE_DP = 36f
    private const val DEFAULT_ICON_CHAR = '?'

    /**
     * See [generateAdaptiveLauncherIcon] for more details.
     */
    @JvmStatic
    fun generateLauncherIcon(context: Context, url: String?): Bitmap {
        val startingChar = getRepresentativeCharacter(url)
        return generateCharacterIcon(context, startingChar)
    }

    /**
     * Generate an icon with the given character. The icon will be drawn
     * on top of a generic launcher icon shape that we provide.
     */
    private fun generateCharacterIcon(context: Context, character: Char) =
            generateAdaptiveLauncherIcon(context, character)

    /**
     * Generates a launcher icon for versions of Android that support Adaptive Icons (Oreo+):
     * https://developer.android.com/guide/practices/ui_guidelines/icon_design_adaptive.html
     */
    private fun generateAdaptiveLauncherIcon(context: Context, character: Char): Bitmap {
        val res = context.resources
        val adaptiveIconDimen = res.getDimensionPixelSize(R.dimen.adaptive_icon_drawable_dimen)

        val bitmap = createBitmap(adaptiveIconDimen, adaptiveIconDimen)
        val canvas = Canvas(bitmap)

        // Adaptive Icons have two layers: a background that fills the canvas and
        // a foreground that's centered. First, we draw the background...
        canvas.drawColor(ContextCompat.getColor(context, R.color.add_to_homescreen_icon_background))

        // Then draw the foreground
        return drawCharacterOnBitmap(context, character, bitmap)
    }

    private fun drawCharacterOnBitmap(context: Context, character: Char, bitmap: Bitmap): Bitmap {
        val canvas = Canvas(bitmap)

        val paint = Paint()

        val textSize = TypedValue.applyDimension(
            TypedValue.COMPLEX_UNIT_DIP,
            TEXT_SIZE_DP,
            context.resources.displayMetrics,
        )

        paint.color = Color.WHITE
        paint.textAlign = Paint.Align.CENTER
        paint.textSize = textSize
        paint.isAntiAlias = true

        canvas.drawText(
            character.toString(),
            canvas.width / 2.0f,
            canvas.height / 2.0f - (paint.descent() + paint.ascent()) / 2.0f,
            paint,
        )

        return bitmap
    }

    /**
     * Get a representative character for the given URL.
     *
     * For example this method will return "f" for "http://m.facebook.com/foobar".
     */
    @JvmStatic
    fun getRepresentativeCharacter(url: String?): Char {
        val firstChar = getRepresentativeSnippet(url)?.find { it.isLetterOrDigit() }?.uppercaseChar()
        return (firstChar ?: DEFAULT_ICON_CHAR)
    }

    /**
     * Get the representative part of the URL. Usually this is the host (without common prefixes).
     *
     * @return the representative snippet or null if one could not be found.
     */
    private fun getRepresentativeSnippet(url: String?): String? {
        if (url == null || url.isEmpty()) return null

        val uri = url.toUri()
        val snippet = if (uri.host.isNullOrEmpty()) {
            if (uri.path.isNullOrEmpty()) {
                return null
            } else { // The uri may not have a host for e.g. file:// uri
                uri.path // cached by Uri class.
            }
        } else {
            uri.host // cached by Uri class.
        }

        // Strip common prefixes that we do not want to use to determine the representative characters
        return snippet?.stripCommonSubdomains()
    }
}
