package com.amazon.ivs.chatdemo.ui.chat

import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Rect
import android.util.AttributeSet
import android.view.View
import android.view.animation.LinearInterpolator
import androidx.core.animation.doOnEnd
import com.amazon.ivs.chatdemo.R
import com.amazon.ivs.chatdemo.common.extensions.dp
import com.amazon.ivs.chatdemo.common.extensions.loadSticker
import com.amazon.ivs.chatdemo.common.extensions.sp
import com.amazon.ivs.chatdemo.repository.networking.models.ChatMessageResponse
import com.amazon.ivs.chatdemo.repository.networking.models.MessageViewType
import timber.log.Timber
import kotlin.random.Random

private const val PX_SPEED = 70f
private const val RANDOM_DURATION_VARIANCE_PERCENT = 0.2f

class BulletChatRowView(context: Context, attrs: AttributeSet) : View(context, attrs) {
    private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        color = Color.WHITE
        setShadowLayer(1f, 4f, 4f, Color.BLACK)
    }
    private val textBounds = Rect()
    private var messages = mutableMapOf<ChatMessageResponse, Float>()
    private var stickerBitmaps = mutableMapOf<String, Bitmap>()
    private var spMessageTextSize = 16.sp
    private var dpStickerSize = 96.dp

    init {
        context.theme
            .obtainStyledAttributes(attrs, R.styleable.BulletChatRowView, 0, 0)
            .apply {
                try {
                    spMessageTextSize = getDimension(R.styleable.BulletChatRowView_messageTextSize, spMessageTextSize)
                    dpStickerSize = getDimensionPixelSize(R.styleable.BulletChatRowView_stickerSize, dpStickerSize)
                } finally {
                    recycle()
                }
            }
    }

    fun sendMessage(message: ChatMessageResponse) {
        if (message.viewType != MessageViewType.STICKER  || stickerBitmaps.containsKey(message.imageResource)) {
            startMessageAnimation(message)
            return
        }

        fetchStickerImageBeforeAnimatingMessage(message)
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)

        paint.textSize = spMessageTextSize

        for ((message, positionX) in messages) {
            when (message.viewType) {
                MessageViewType.STICKER -> {
                    val stickerBitmap = stickerBitmaps[message.imageResource]!!
                    Timber.d("DRAW: ${stickerBitmap.height}, $height")
                    canvas.drawBitmap(
                        stickerBitmap,
                        positionX,
                        (height.toFloat() - stickerBitmap.height) / 2f,
                        paint
                    )
                }
                MessageViewType.MESSAGE -> {
                    val text = message.content
                    paint.getTextBounds(text, 0, text.length, textBounds)
                    canvas.drawText(
                        message.content,
                        positionX,
                        (height / 2f) + (textBounds.height() / 2f) - textBounds.bottom,
                        paint
                    )
                }
                else -> { /* Ignored */ }
            }
        }
    }

    private fun fetchStickerImageBeforeAnimatingMessage(message: ChatMessageResponse) {
        try {
            message.loadSticker(context, dpStickerSize) { bitmap ->
                stickerBitmaps[message.imageResource] = bitmap
                startMessageAnimation(message)
            }
        } catch (e: IllegalArgumentException) {
            /* Ignored - this happens when the screen is rotated and an image load is cancelled */
        }
    }

    private fun startMessageAnimation(message: ChatMessageResponse) {
        if (width == 0) return

        messages[message] = 0f
        val messageWidth = when (message.viewType) {
            MessageViewType.STICKER -> stickerBitmaps[message.imageResource]!!.width.toFloat()
            else -> paint.measureText(message.content)
        }

        ValueAnimator.ofFloat(width.toFloat(), -messageWidth).apply {
            val durationBeforeRandomVariance = ((width / PX_SPEED) * 1000).toLong()
            val randomDurationVariance = Random.nextLong(
                from = 0,
                until = (durationBeforeRandomVariance * RANDOM_DURATION_VARIANCE_PERCENT).toLong()
            )
            duration = durationBeforeRandomVariance + randomDurationVariance

            interpolator = LinearInterpolator()
            addUpdateListener {
                val newPositionX = it.animatedValue as Float
                messages[message] = newPositionX
                invalidate()
            }
            doOnEnd { onMessageAnimationEnd(message) }
            start()
        }
    }

    private fun onMessageAnimationEnd(message: ChatMessageResponse) {
        messages.remove(message)
        invalidate()
    }
}