package com.adealink.weparty.commonui.widget.roulette

import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Point
import android.graphics.PointF
import android.graphics.PorterDuff
import android.graphics.PorterDuffXfermode
import android.graphics.RadialGradient
import android.graphics.RectF
import android.graphics.Shader
import android.net.Uri
import android.util.AttributeSet
import android.view.View
import com.adealink.frame.aab.util.getCompatColor
import com.adealink.frame.image.imageService
import com.adealink.frame.image.listener.ICloseableImageResultListener
import com.adealink.frame.mvvm.lifecycle.viewScope
import com.adealink.frame.util.DisplayUtil
import com.adealink.weparty.R
import com.adealink.weparty.commonui.ext.isGIFImage
import com.adealink.weparty.commonui.widget.roulette.data.RoulettePlayer
import com.adealink.weparty.commonui.widget.roulette.data.RoulettePlayerColor
import com.adealink.weparty.commonui.widget.roulette.data.RouletteViewPlayer
import com.facebook.common.executors.CallerThreadExecutor
import com.facebook.common.references.CloseableReference
import com.facebook.datasource.BaseDataSubscriber
import com.facebook.datasource.DataSource
import com.facebook.drawee.backends.pipeline.Fresco
import com.facebook.imagepipeline.animated.base.AnimatedImageFrame
import com.facebook.imagepipeline.core.ImagePipeline
import com.facebook.imagepipeline.image.CloseableAnimatedImage
import com.facebook.imagepipeline.image.CloseableBitmap
import com.facebook.imagepipeline.image.CloseableImage
import com.facebook.imagepipeline.request.ImageRequest
import com.facebook.imagepipeline.request.ImageRequestBuilder
import kotlinx.coroutines.launch
import kotlin.math.cos
import kotlin.math.sin

class RouletteView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0,
) : View(context, attrs, defStyleAttr), IRouletteView {

    private var paint: Paint = Paint()
    private var center = Point(0, 0) //圆心
    private var radius = 0f //半径
    private val drawRectF: RectF = RectF() //圆的绘制区域
    private var playerColorList = DEFAULT_COLOR_LIST
    private var players = mutableListOf<RouletteViewPlayer>()
    private val eachPlayerDegree: Float
        get() {
            if (playerCount <= 0) {
                return 360f
            }
            return 360f / playerCount
        }
    private val playerCount: Int
        get() {
            return players.size
        }
    private var playerBitmapMap = HashMap<String, Bitmap?>()
    private val defaultFrameBitmap by lazy { BitmapFactory.decodeResource(resources, R.drawable.common_default_avatar_ic) }

    override fun setPlayers(
        players: List<RoulettePlayer>,
        playerColorList: List<RoulettePlayerColor>?,
    ) {
        this.playerColorList = playerColorList ?: DEFAULT_COLOR_LIST
        this.players.clear()
        players.forEachIndexed { index, roulettePlayer ->
            val playerColor = this.playerColorList[index.rem(this.playerColorList.size)]
            this.players.add(
                RouletteViewPlayer(
                    roulettePlayer.playerId,
                    roulettePlayer.avatarUrl,
                    playerColor
                )
            )
        }
        fetchAvatars()
        invalidate()
    }

    override fun addPlayer(player: RoulettePlayer) {

        if (players.find { it.playerId == player.playerId } != null) {
            return
        }
        players.add(
            RouletteViewPlayer(
                player.playerId,
                imageService.getResizeUrl(player.avatarUrl, AVATAR_WIDTH.toInt(), AVATAR_WIDTH.toInt()),
                playerColorList[playerCount.rem(this.playerColorList.size)]
            )
        )
        fetchAvatars()
        invalidate()
    }

    override fun addPlayers(players: List<RoulettePlayer>) {
        val toAddPlayers = mutableListOf<RouletteViewPlayer>()
        var curPlayerCount = playerCount
        players.forEach { newPlayer ->
            if (this.players.find { it.playerId == newPlayer.playerId } != null) {
                return@forEach
            }
            toAddPlayers.add(
                RouletteViewPlayer(
                    newPlayer.playerId,
                    newPlayer.avatarUrl,
                    playerColorList[curPlayerCount.rem(this.playerColorList.size)]
                )
            )
            curPlayerCount++
        }
        this.players.addAll(toAddPlayers)
        fetchAvatars()
        invalidate()
    }

    override fun weepOutPlayer(playerId: Long) {

        players.find { it.playerId == playerId }?.let {
            players.remove(it)
            invalidate()
        }
    }

    override fun weepOutPlayers(playerIds: List<Long>) {
        playerIds.forEach {
            weepOutPlayer(it)
        }
    }

    override fun weepOutAllPlayer() {
        players.clear()
        invalidate()
    }

    override fun getPlayerDegree(playerId: Long): Float? {
        val index = players.indexOfFirst { it.playerId == playerId }
        if (index == -1) {
            return null
        }
        return eachPlayerDegree * (index + 0.5f)
    }

    override fun getPlayerIds(): List<Long> {
        return players.map { it.playerId }
    }

    override fun getPlayerAvatarCenter(playerId: Long): PointF? {
        return players.find { it.playerId == playerId }?.avatarCenter
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        center.x = w / 2
        center.y = h / 2
        radius = ((width - paddingLeft - paddingRight) / 2).toFloat() //半径
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        canvas.translate(center.x.toFloat(), center.y.toFloat()) //移到中心
        drawRectF.set(-radius, -radius, radius, radius) //绘制区域
        if (players.size == 0) {
            drawDefaultBg(canvas)
            return
        }
        var startAngle = -90f
        for (i in 0 until playerCount) {
            //依次画圆弧
            drawArc(canvas, i, startAngle)
            //画头像
            drawPlayerAvatar(canvas, i, startAngle)
            startAngle += eachPlayerDegree
        }
    }

    private fun drawDefaultBg(canvas: Canvas?) {
        val playerColor = DEFAULT_BG_COLOR
        val radiaShader = RadialGradient(
            0f,
            0f,
            radius,
            playerColor.colors.toIntArray(),
            playerColor.positions.toFloatArray(),
            Shader.TileMode.CLAMP
        )
        paint.color = Color.WHITE
        paint.style = Paint.Style.FILL
        paint.shader = radiaShader
        paint.isAntiAlias = true
        canvas?.drawArc(drawRectF, 0f, 360f, true, paint)
    }

    private fun drawArc(canvas: Canvas?, index: Int, startAngle: Float) {
        val playerColor = players[index].playerColor
        val radiaShader = RadialGradient(
            0f,
            0f,
            radius,
            playerColor.colors.toIntArray(),
            playerColor.positions.toFloatArray(),
            Shader.TileMode.CLAMP
        )
        paint.color = Color.WHITE
        paint.style = Paint.Style.FILL
        paint.shader = radiaShader
        paint.isAntiAlias = true
        canvas?.drawArc(drawRectF, startAngle, eachPlayerDegree, true, paint)
        if (playerCount > 1) {
            drawArcBorder(canvas, startAngle)
            drawArcBorder(canvas, startAngle + eachPlayerDegree)
        }
    }

    private fun drawArcBorder(canvas: Canvas?, angle: Float) {
        val radian: Double = getRadianByAngle(angle)
        val stopX = (radius * cos(radian)).toFloat()
        val stopY = (radius * sin(radian)).toFloat()
        paint.reset()
        paint.style
        paint.color = INNER_BORDER_COLOR
        paint.isAntiAlias = true
        paint.strokeWidth = 2 * OUTER_BORDER_WIDTH + 2 * INNER_BORDER_WIDTH
        //画内边框直线
        canvas?.drawLine(0f, 0f, stopX, stopY, paint)
        paint.reset()
        paint.color = OUTER_BORDER_COLOR
        paint.isAntiAlias = true
        paint.strokeWidth = 2 * OUTER_BORDER_WIDTH
        //画外边框直线
        canvas?.drawLine(0f, 0f, stopX, stopY, paint)
        paint.reset()
        paint.color = INNER_BORDER_COLOR
        paint.isAntiAlias = true
        paint.style = Paint.Style.STROKE
        paint.strokeWidth = INNER_BORDER_WIDTH
        //画内边框弧线
        canvas?.drawCircle(
            0f,
            0f,
            radius - INNER_BORDER_WIDTH,
            paint
        )
    }

    private fun getRadianByAngle(degree: Float): Double {
        return degree * Math.PI / 180
    }

    private fun drawPlayerAvatar(canvas: Canvas?, index: Int, startAngle: Float) {
        val imageSize = AVATAR_WIDTH
        val imageRadius = imageSize / 2
        val radian = getRadianByAngle(startAngle + eachPlayerDegree / 2)
        val x = (2 * radius / 3 * cos(radian)).toFloat()
        val y = (2 * radius / 3 * sin(radian)).toFloat()
        players.getOrNull(index)?.avatarCenter = PointF(x + radius, y + radius)
        val rect = RectF(x - imageRadius, y - imageRadius, x + imageRadius, y + imageRadius)

        var bitmap = playerBitmapMap[players.getOrNull(index)?.avatarUrl]

        if (null == bitmap || bitmap.isRecycled) {
            bitmap = defaultFrameBitmap
        }
        canvas?.save()
        paint.reset()
        paint.isAntiAlias = true
        paint.color = Color.WHITE
        paint.style = Paint.Style.STROKE
        paint.strokeWidth = DisplayUtil.dp2px(2f).toFloat()
        //绘制白色边框
        canvas?.drawCircle(
            rect.centerX(),
            rect.centerY(),
            imageRadius,
            paint
        )
        paint.reset()
        paint.isAntiAlias = true
        //新建图层
        val sc = canvas?.saveLayer(
            (x - imageRadius),
            (y - imageRadius),
            (x + imageRadius),
            (y + imageRadius),
            paint,
            Canvas.ALL_SAVE_FLAG
        )
        //画圆角矩形
        canvas?.drawRoundRect(
            RectF(rect),
            radius,
            radius,
            paint
        )
        //设置混合模式
        paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
        //绘制bitmap
        bitmap?.let {
            canvas?.drawBitmap(
                it,
                null,
                RectF(rect),
                paint
            )
        }
        paint.xfermode = null
        sc?.let {
            canvas.restoreToCount(it)
        }
        canvas?.restore()
    }

    private fun fetchAvatars() {
        players.forEach {
            if (playerBitmapMap.containsKey(it.avatarUrl)) {
                return@forEach
            }

            if (isGIFImage(it.avatarUrl)) {
                getGifFirstFrameBitmap(it.avatarUrl) { bitmap ->
                    playerBitmapMap[it.avatarUrl] = bitmap
                    postInvalidate()
                }
                return
            }

            imageService.fetchImage(it.avatarUrl, object : ICloseableImageResultListener {
                override fun onSuccess(reference: CloseableReference<CloseableImage>) {
                    super.onSuccess(reference)
                    val closeableBitmap = reference.get()
                    if (closeableBitmap is CloseableBitmap) {
                        playerBitmapMap[it.avatarUrl] = closeableBitmap.underlyingBitmap
                        postInvalidate()
                    }
                }
            })

        }
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        playerBitmapMap.clear()
    }

    companion object {
        const val TAG = "RouletteView"
        val DEFAULT_COLOR_LIST =
            listOf(
                RoulettePlayerColor(
                    colors = listOf(
                        Color.parseColor("#FFFFE6AD"),
                        Color.parseColor("#FFFFBE2E")
                    ), positions = listOf(0f, 1f)
                ),
                RoulettePlayerColor(
                    colors = listOf(
                        Color.parseColor("#FFFFD0AD"),
                        Color.parseColor("#FFFF872E")
                    ), positions = listOf(0f, 1f)
                ),
                RoulettePlayerColor(
                    colors = listOf(
                        Color.parseColor("#FFFFBDAD"),
                        Color.parseColor("#FFFF3E2E")
                    ), positions = listOf(0f, 1f)
                )
            )
        val DEFAULT_BG_COLOR = RoulettePlayerColor(
            colors = listOf(
                Color.parseColor("#FFFFE6AD"),
                Color.parseColor("#FFFFBE2E")
            ), positions = listOf(0f, 1f)
        )
        val INNER_BORDER_COLOR = getCompatColor(R.color.roulette_inner_border)
        val OUTER_BORDER_COLOR = getCompatColor(R.color.roulette_outer_border)
        val OUTER_BORDER_WIDTH = DisplayUtil.dp2px(1f).toFloat()
        val INNER_BORDER_WIDTH = DisplayUtil.dp2px(1f).toFloat()
        val AVATAR_WIDTH = DisplayUtil.dp2px(40f).toFloat()
    }

    private fun getGifFirstFrameBitmap(url: String, callback: (Bitmap?) -> Unit) {
        viewScope.launch {
            val imageRequest: ImageRequest = ImageRequestBuilder
                .newBuilderWithSource(Uri.parse(url))
                .setProgressiveRenderingEnabled(true)
                .build()
            val imagePipeline: ImagePipeline = Fresco.getImagePipeline()
            val dataSource = imagePipeline.fetchDecodedImage(imageRequest, context)

            dataSource.subscribe(object : BaseDataSubscriber<CloseableReference<CloseableImage>>() {
                override fun onNewResultImpl(dataSource: DataSource<CloseableReference<CloseableImage>>) {
                    if (!dataSource.isFinished) {
                        return
                    }
                    val imageReference: CloseableReference<CloseableImage>? = dataSource.result
                    imageReference?.use { imageReference ->
                        val image: CloseableImage = imageReference.get()
                        if (image is CloseableAnimatedImage) {
                            callback.invoke(image.image?.getFrame(0)?.getBitmap())
                        }
                    }
                }

                override fun onFailureImpl(dataSource: DataSource<CloseableReference<CloseableImage>>) {
                }
            }, CallerThreadExecutor.getInstance())
        }

    }

    fun AnimatedImageFrame.getBitmap(): Bitmap? {
        val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
        renderFrame(width, height, bitmap)
        return bitmap
    }
}