package protect.card_locker

import android.content.Context
import android.content.res.TypedArray
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.Typeface
import android.text.TextPaint
import android.util.Log
import androidx.core.graphics.PaintCompat
import java.util.Locale
import kotlin.math.abs

/**
 * Original from https://github.com/andOTP/andOTP/blob/master/app/src/main/java/org/shadowice/flocke/andotp/Utilities/LetterBitmap.java
 * which was originally from http://stackoverflow.com/questions/23122088/colored-boxed-with-letters-a-la-gmail
 * Used to create a {@link Bitmap} that contains a letter used in the English
 * alphabet or digit, if there is no letter or digit available, a default image
 * is shown instead.
 *
 * @constructor Constructor for <code>LetterTileProvider</code>
 * @param context            The {@link Context} to use
 * @param displayName        The name used to create the letter for the tile
 * @param key                The key used to generate the background color for the tile
 * @param tileLetterFontSize The font size used to display the letter
 * @param width              The desired width of the tile
 * @param height             The desired height of the tile
 * @param backgroundColor    (optional) color to use for background.
 * @param textColor          (optional) color to use for text.
 */
class LetterBitmap(
    context: Context, displayName: String, key: String, tileLetterFontSize: Int,
    width: Int, height: Int, backgroundColor: Int?, textColor: Int?
) {
    /**
     * A {@link Bitmap} that contains a letter used in the English
     * alphabet or digit, if there is no letter or digit available, a
     * default image is shown instead
     */
    private val letterTile: Bitmap

    /**
     * The background color of the letter bitmap
     */
    private val mColor: Int

    init {
        val paint = TextPaint().apply {
            color = textColor ?: Color.WHITE
            textAlign = Paint.Align.CENTER
            isAntiAlias = true
            textSize = tileLetterFontSize.toFloat()
            typeface = Typeface.defaultFromStyle(Typeface.BOLD)
        }

        mColor = backgroundColor ?: getDefaultColor(context, key)

        this.letterTile = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)

        var firstChar = displayName.substring(0, 1).uppercase(Locale.getDefault())
        var firstCharEnd = 2
        while (firstCharEnd <= displayName.length) {
            // Test for the longest render-able string
            // But ignore containing only a-Z0-9 to not render things like ffi as a single character
            val test = displayName.substring(0, firstCharEnd)
            if (!isAlphabetical(test) && PaintCompat.hasGlyph(paint, test)) {
                firstChar = test
            }
            firstCharEnd++
        }

        Log.d(
            "LetterBitmap",
            "using sequence $firstChar to render first char which has length ${firstChar.length}"
        )

        Canvas().apply {
            setBitmap(this@LetterBitmap.letterTile)
            drawColor(mColor)

            val bounds = Rect()
            paint.getTextBounds(firstChar, 0, firstChar.length, bounds)
            drawText(
                firstChar,
                0, firstChar.length,
                width / 2.0f, (height - (bounds.bottom + bounds.top)) / 2.0f,
                paint
            )
        }
    }

    val backgroundColor: Int
        /**
         * @return background color used for letter title.
         */
        get() = mColor
    
    fun getLetterTile(): Bitmap {
        return letterTile
    }

    companion object {
        /**
         * @param key The key used to generate the tile color
         * @return A new or previously chosen color for `key` used as the
         * tile background color
         */
        private fun pickColor(key: String, colors: TypedArray): Int {
            // String.hashCode() is not supposed to change across java versions, so
            // this should guarantee the same key always maps to the same color
            val color = abs(key.hashCode()) % colors.length()
            return colors.getColor(color, Color.BLACK)
        }

        private fun isAlphabetical(string: String): Boolean {
            return string.matches("[a-zA-Z0-9]*".toRegex())
        }

        /**
         * Determine the color which the letter tile will use if no default
         * color is provided.
         */
        fun getDefaultColor(context: Context, key: String): Int {
            val res = context.resources

            val colors = res.obtainTypedArray(R.array.letter_tile_colors)
            val color: Int = pickColor(key, colors)
            colors.recycle()

            return color
        }
    }
}