package com.adealink.weparty.commonui.text.span

import android.graphics.Color
import android.text.Spannable
import android.text.SpannableString
import android.text.Spanned
import android.text.TextPaint
import android.text.style.ClickableSpan
import android.text.style.ForegroundColorSpan
import android.view.View
import com.adealink.frame.data.json.froJsonErrorNull
import com.adealink.frame.data.json.toJsonErrorNull
import com.adealink.frame.ext.safeSetSpan
import com.adealink.frame.util.fitLocateDirectionForNickname
import com.adealink.frame.util.getJSONObject
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.JsonPrimitive
import com.google.gson.JsonSerializationContext
import com.google.gson.JsonSerializer
import com.google.gson.annotations.JsonAdapter
import com.google.gson.annotations.SerializedName
import java.lang.reflect.Type

private const val SPAN_TYPE = "spanType"

@JsonAdapter(DataBindingSpanParser::class)
sealed class DataBindingSpan(@SerializedName(SPAN_TYPE) val spanType: DataBindingSpanType) : IDataBindingSpan {
    //序列化的时候需要传入span起止位置
    @SerializedName("start")
    var start: Int = 0

    @SerializedName("end")
    var end: Int = 0

    @SerializedName("placeholder")
    val placeholder: String? = null

    @SerializedName("replaceContent")
    val replaceContent: String? = null

    override fun getSpannableString(): SpannableString {
        val ss = SpannableString(text())
        setSpan(ss, 0, ss.length)
        return ss
    }
}

@JsonAdapter(DataBindingSpanTypeParser::class)
enum class DataBindingSpanType(val type: String, val clazz: Class<*>?) {
    AT_USER("at_user", AtUserDataBindingSpan::class.java),
    COLOR_STRING("color_string", ColorStringDataBindingSpan::class.java),
    IMAGE("image", ImageDataBindingSpan::class.java),
    LINK("link", LinkDataBindingSpan::class.java);

    companion object {

        fun getSpanType(type: String): DataBindingSpanType? {
            for (entry in values()) {
                if (entry.type.equals(type, ignoreCase = true)) {
                    return entry
                }
            }
            return null
        }

        fun getSpanTypeClass(type: String): Class<*>? {
            return getSpanType(type)?.clazz
        }
    }

}

/**
 * "@somebody"
 */
data class AtUserDataBindingSpan(
    @SerializedName("id") val uid: String,
    @SerializedName("name") val name: String
) : DataBindingSpan(DataBindingSpanType.AT_USER), DirtySpan {

    var clickAction: (() -> Unit)? = null

    var highLightColor: Int? = null

    override fun text(): CharSequence {
        return "${fitLocateDirectionForNickname("@$name")} "
    }

    override fun setSpan(spannable: Spannable, start: Int, end: Int) {
        spannable.safeSetSpan(this, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
        spannable.safeSetSpan(ForegroundColorSpan(
            highLightColor ?: let {
                Color.parseColor("#00DFD6")
            }
        ), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
        spannable.safeSetSpan(
            object : ClickableSpan() {
                override fun onClick(widget: View) {
                    clickAction?.invoke()
                }

                override fun updateDrawState(ds: TextPaint) {}
            },
            start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
        )
    }

    override fun isDirty(text: Spannable): Boolean {
        val spanStart = text.getSpanStart(this)
        val spanEnd = text.getSpanEnd(this)
        return spanStart >= 0 && spanEnd >= 0 && text.substring(spanStart, spanEnd) != text()
    }
}

/**
 * 带颜色的文字
 */
data class ColorStringDataBindingSpan(
    @SerializedName("color") val color: String?, //eg: "#000000"
) : DataBindingSpan(DataBindingSpanType.COLOR_STRING) {

    override fun text(): CharSequence {
        return ""
    }

    override fun setSpan(spannable: Spannable, start: Int, end: Int) {
        spannable.safeSetSpan(
            ForegroundColorSpan(
                try {
                    Color.parseColor(color)
                } catch (e: Exception) {
                    Color.BLACK
                }
            ), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
        )
    }
}

/**
 * 图片, 需要配合 DraweeTextView 使用
 */
data class ImageDataBindingSpan(
    @SerializedName("uri") val uri: String?
) : DataBindingSpan(DataBindingSpanType.IMAGE) {

    //其他属性详见DraweeSpan, 在setSpan之前设置好即可
    var width: Int = 0
    var height: Int = 0

    override fun text(): CharSequence {
        return "[image]"
    }

    override fun setSpan(spannable: Spannable, start: Int, end: Int) {
        if (uri.isNullOrEmpty()) {
            return
        }
        spannable.safeSetSpan(
            DraweeSpan
                .Builder(uri)
                .setLayout(width, height)
                .build(),
            start,
            end,
            Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
        )
    }
}

/**
 * 带跳转链接的文字, 需要配合 LinkMovementMethod 使用
 */
data class LinkDataBindingSpan(
    @SerializedName("link") val link: String?
) : DataBindingSpan(DataBindingSpanType.LINK) {

    var clickAction: (() -> Unit)? = null

    override fun text(): CharSequence {
        return ""
    }

    override fun setSpan(spannable: Spannable, start: Int, end: Int) {
        spannable.safeSetSpan(
            object : ClickableSpan() {
                override fun onClick(widget: View) {
                    clickAction?.invoke()
                }
            },
            start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
        )
    }
}

object DataBindingSpanParser : JsonDeserializer<DataBindingSpan>, JsonSerializer<DataBindingSpan> {

    override fun deserialize(
        json: JsonElement?,
        typeOfT: Type?,
        context: JsonDeserializationContext?,
    ): DataBindingSpan? {
        val obj = json?.asString ?: return null
        val jsonObject = obj.getJSONObject()
        val type = jsonObject?.optString(SPAN_TYPE) ?: return null
        val clazz = DataBindingSpanType.getSpanTypeClass(type)
        return if (clazz != null) {
            froJsonErrorNull(obj, clazz as Type)
        } else {
            null
        }
    }

    override fun serialize(
        src: DataBindingSpan?,
        typeOfSrc: Type?,
        context: JsonSerializationContext?,
    ): JsonElement? {
        return if (src == null) null else JsonPrimitive(toJsonErrorNull(src))
    }
}

object DataBindingSpanTypeParser : JsonSerializer<DataBindingSpanType>, JsonDeserializer<DataBindingSpanType> {
    override fun serialize(
        src: DataBindingSpanType?,
        typeOfSrc: Type?,
        context: JsonSerializationContext?,
    ): JsonElement? {
        if (src is DataBindingSpanType) {
            return JsonPrimitive(src.type)
        }
        return null
    }

    override fun deserialize(
        json: JsonElement?,
        typeOfT: Type?,
        context: JsonDeserializationContext?,
    ): DataBindingSpanType? {
        if (null != json) {
            return DataBindingSpanType.getSpanType(json.asString)
        }
        return null
    }
}