package jp.juggler.subwaytooter.itemviewholder

import android.text.Spannable
import android.text.SpannableStringBuilder
import android.view.View
import android.widget.ImageView
import jp.juggler.subwaytooter.ActMain
import jp.juggler.subwaytooter.R
import jp.juggler.subwaytooter.actmain.checkAutoCW
import jp.juggler.subwaytooter.api.entity.TootAccount
import jp.juggler.subwaytooter.api.entity.TootAttachment
import jp.juggler.subwaytooter.api.entity.TootAttachmentLike
import jp.juggler.subwaytooter.api.entity.TootAttachmentType
import jp.juggler.subwaytooter.api.entity.TootMessageHolder
import jp.juggler.subwaytooter.api.entity.TootNotification
import jp.juggler.subwaytooter.api.entity.TootPolls
import jp.juggler.subwaytooter.api.entity.TootPollsType
import jp.juggler.subwaytooter.api.entity.TootStatus
import jp.juggler.subwaytooter.api.entity.TootVisibility
import jp.juggler.subwaytooter.calcIconRound
import jp.juggler.subwaytooter.column.Column
import jp.juggler.subwaytooter.column.ColumnType
import jp.juggler.subwaytooter.column.isConversation
import jp.juggler.subwaytooter.defaultColorIcon
import jp.juggler.subwaytooter.pref.PrefB
import jp.juggler.subwaytooter.pref.PrefI
import jp.juggler.subwaytooter.stylerBoostAlpha
import jp.juggler.subwaytooter.table.daoContentWarning
import jp.juggler.subwaytooter.table.daoMediaShown
import jp.juggler.subwaytooter.util.OpenSticker
import jp.juggler.util.data.cast
import jp.juggler.util.data.ellipsizeDot3
import jp.juggler.util.data.notBlank
import jp.juggler.util.data.notEmpty
import jp.juggler.util.data.notZero
import jp.juggler.util.log.LogCategory
import jp.juggler.util.ui.attrColor
import jp.juggler.util.ui.setIconDrawableId
import jp.juggler.util.ui.textOrGone
import jp.juggler.util.ui.vg
import org.jetbrains.anko.backgroundColor
import org.jetbrains.anko.imageResource
import org.jetbrains.anko.textColor

private val log = LogCategory("ItemViewHolderShowStatus")

fun ItemViewHolder.showStatusOrReply(
    item: TootStatus,
    colorBgArg: Int = 0,
    fadeText: Boolean = false,
) {
    var colorBg = colorBgArg
    val reply = item.reply
    val inReplyToId = item.in_reply_to_id
    val inReplyToAccountId = item.in_reply_to_account_id
    when {
        reply != null -> {
            showReply(item.account, reply, R.drawable.ic_reply, R.string.reply_to)
            if (colorBgArg == 0) colorBg = PrefI.ipEventBgColorMention.value
        }

        inReplyToId != null && inReplyToAccountId != null -> {
            showReply(null, item, inReplyToAccountId)
            if (colorBgArg == 0) colorBg = PrefI.ipEventBgColorMention.value
        }
    }
    showStatus(item, colorBg, fadeText = fadeText)
}

fun ItemViewHolder.showStatus(
    status: TootStatus,
    colorBg: Int = 0,
    fadeText: Boolean = false,
) {

    val filteredWord = status.filteredWord
    if (filteredWord != null) {
        PrefI.ipEventBgColorFiltered.value.notZero()?.let {
            viewRoot.backgroundColor = it
        }
        val text = StringBuilder().apply {
            append(activity.getString(R.string.filtered))
            if (PrefB.bpShowFilteredWord.value) {
                append(" / $filteredWord")
            }
            if (PrefB.bpShowUsernameFilteredPost.value) {
                val s = status.reblog ?: status
                append(" / ${s.account.display_name} @${s.account.acct}")
            }
        }.toString()
        showMessageHolder(TootMessageHolder(text))
        return
    }

    this.statusShowing = status
    llStatus.visibility = View.VISIBLE

    if (status.conversation_main) {
        PrefI.ipConversationMainTootBgColor.value.notZero()
            ?: activity.attrColor(R.attr.colorConversationMainTootBg)
    } else {
        colorBg.notZero() ?: when (status.bookmarked) {
            true -> PrefI.ipEventBgColorBookmark.value
            false -> 0
        }.notZero() ?: when (status.getBackgroundColorType(accessInfo)) {
            TootVisibility.UnlistedHome -> ItemViewHolder.toot_color_unlisted
            TootVisibility.PrivateFollowers -> ItemViewHolder.toot_color_follower
            TootVisibility.DirectSpecified -> ItemViewHolder.toot_color_direct_user
            TootVisibility.DirectPrivate -> ItemViewHolder.toot_color_direct_me
            // TODO add color setting for limited?
            TootVisibility.Limited -> ItemViewHolder.toot_color_follower
            else -> 0
        }.notZero()
    }?.let { viewRoot.backgroundColor = it }

    showStatusTime(activity, tvTime, who = status.account, status = status)

    val whoRef = status.accountRef
    val who = whoRef.get()
    this.statusAccount = whoRef

    setAcct(tvAcct, accessInfo, who)

    nameInvalidator.text = whoRef.decoded_display_name

    ivAvatar.setImageUrl(
        calcIconRound(ivAvatar.layoutParams),
        accessInfo.supplyBaseUrl(who.avatar_static),
        accessInfo.supplyBaseUrl(who.avatar)
    )

    showOpenSticker(who)

    val modifiedContent = if (status.time_deleted_at > 0L) {
        SpannableStringBuilder()
            .append('(')
            .append(
                activity.getString(
                    R.string.deleted_at,
                    TootStatus.formatTime(activity, status.time_deleted_at, true)
                )
            )
            .append(')')
    } else {
        showPoll(status) ?: status.decoded_content
    }

    //			if( status.decoded_tags == null ){
    //				tvTags.setVisibility( View.GONE );
    //			}else{
    //				tvTags.setVisibility( View.VISIBLE );
    //				tvTags.setText( status.decoded_tags );
    //			}

    val fadeAlpha = ActMain.eventFadeAlpha
    if (fadeAlpha < 1f) {
        val a = if (fadeText) fadeAlpha else 1f
        tvMentions.alpha = a
        tvContentWarning.alpha = a
        tvContent.alpha = a
        tvApplication.alpha = a
        tvCardText.alpha = a
    }

    tvMentions.textOrGone = status.decoded_mentions

    contentInvalidator.text = modifiedContent

    activity.checkAutoCW(status, modifiedContent)
    val r = status.auto_cw
    tvContent.minLines = r?.originalLineCount ?: -1

    showPreviewCard(status)
    showSpoilerTextAndContent(status)
    showAttachments(status)
    makeReactionsView(status)
    buttonsForStatus?.bind(status, (item as? TootNotification))
    showApplicationAndLanguage(status)
}

// 投票の表示
// returns modified decoded_content or null
private fun ItemViewHolder.showPoll(status: TootStatus): Spannable? {
    val enquete = status.enquete
    return when {
        enquete == null -> null

        // フレニコの投票の結果表示は普通にテキストを表示するだけでよい
        enquete.pollType == TootPollsType.FriendsNico && enquete.type != TootPolls.TYPE_ENQUETE -> null

        else -> {
            showEnqueteItems(status, enquete)
            // アンケートの本文を使ってcontentを上書きする
            enquete.decoded_question.notBlank()
        }
    }
}

private fun ItemViewHolder.showSpoilerTextAndContent(status: TootStatus) {
    val r = status.auto_cw
    val decodedSpoilerText = status.decoded_spoiler_text
    val autoCwText = r?.decodedSpoilerText
    when {
        decodedSpoilerText.isNotEmpty() -> {
            // 元データに含まれるContent Warning を使う
            llContentWarning.visibility = View.VISIBLE
            spoilerInvalidator.text = status.decoded_spoiler_text
            val cwShown = daoContentWarning.isShown(status, accessInfo.expandCw)
            setContentVisibility(cwShown)
        }

        autoCwText != null -> {
            // 自動CW
            llContentWarning.visibility = View.VISIBLE
            spoilerInvalidator.text = autoCwText
            val cwShown = daoContentWarning.isShown(status, accessInfo.expandCw)
            setContentVisibility(cwShown)
        }

        else -> {
            // CWしない
            llContentWarning.visibility = View.GONE
            llContents.visibility = View.VISIBLE
        }
    }
}

// 予約投稿でも使う
fun ItemViewHolder.setContentVisibility(shown: Boolean) {
    llContents.visibility = if (shown) View.VISIBLE else View.GONE
    btnContentWarning.contentDescription =
        activity.getString(if (shown) R.string.hide else R.string.show)
    btnContentWarning.imageResource =
        if (shown) R.drawable.outline_compress_24 else R.drawable.outline_expand_24

    statusShowing?.let { status ->
        val r = status.auto_cw
        tvContent.minLines = r?.originalLineCount ?: -1
        val autoCwText = r?.decodedSpoilerText
        if (autoCwText != null) {
            // 自動CWの場合はContentWarningのテキストを切り替える
            spoilerInvalidator.text =
                if (shown) activity.getString(R.string.auto_cw_prefix) else autoCwText
        }
    }
}

private fun ItemViewHolder.showApplicationAndLanguage(status: TootStatus) {

    var sb: StringBuilder? = null
    fun prepareSb(): StringBuilder =
        sb?.append(", ") ?: StringBuilder().also { sb = it }

    val application = status.application
    if (application != null &&
        (column.isConversation || PrefB.bpShowAppName.value)
    ) {
        prepareSb().append(activity.getString(R.string.application_is, application.name ?: ""))
    }

    val language = status.language
    if (language != null &&
        (column.isConversation || PrefB.bpShowLanguage.value)
    ) {
        prepareSb().append(activity.getString(R.string.language_is, language))
    }

    tvApplication.vg(sb != null)?.text = sb
}

private fun ItemViewHolder.showOpenSticker(who: TootAccount) {
    try {
        if (!Column.showOpenSticker) return

        val host = who.apDomain

        // LTLでホスト名が同じならTickerを表示しない
        when (column.type) {
            ColumnType.LOCAL, ColumnType.LOCAL_AROUND -> {
                if (host == accessInfo.apDomain) return
            }

            else -> Unit
        }

        val item = OpenSticker.lastList[host.ascii] ?: return

        tvOpenSticker.text = item.name
        tvOpenSticker.textColor = item.fontColor

        val density = activity.density

        val lp = ivOpenSticker.layoutParams
        lp.height = (density * 16f + 0.5f).toInt()
        lp.width = (density * item.imageWidth + 0.5f).toInt()

        ivOpenSticker.layoutParams = lp
        ivOpenSticker.setImageUrl(0f, item.favicon)
        val colorBg = item.bgColor
        when (colorBg.size) {
            1 -> {
                val c = colorBg.first()
                tvOpenSticker.setBackgroundColor(c)
                ivOpenSticker.setBackgroundColor(c)
            }

            else -> {
                ivOpenSticker.setBackgroundColor(colorBg.last())
                tvOpenSticker.background = colorBg.getGradation()
            }
        }
        llOpenSticker.visibility = View.VISIBLE
        llOpenSticker.requestLayout()
    } catch (ex: Throwable) {
        log.e(ex, "showOpenSticker failed.")
    }
}

private fun ItemViewHolder.showAttachments(status: TootStatus) {
    val mediaAttachments = status.media_attachments
    if (mediaAttachments.isNullOrEmpty()) {
        flMedia.visibility = View.GONE
        llMedia.visibility = View.GONE
        btnShowMedia.visibility = View.GONE
    } else {
        flMedia.visibility = View.VISIBLE
        tvMediaCount.vg(mediaAttachments.size > 4)?.let {
            it.text = activity.getString(R.string.media_count, mediaAttachments.size)
        }

        // hide sensitive media
        val defaultShown = when {
            column.hideMediaDefault -> false
            accessInfo.dontHideNsfw -> true
            else -> !status.sensitive
        }
        val isShown = daoMediaShown.isShown(status, defaultShown)

        btnShowMedia.visibility = if (!isShown) View.VISIBLE else View.GONE
        llMedia.visibility = if (!isShown) View.GONE else View.VISIBLE
        repeat(ItemViewHolder.MEDIA_VIEW_COUNT) { idx ->
            setMedia(mediaAttachments, idx)
        }

        btnShowMedia.blurhash = mediaAttachments.firstOrNull()
            ?.cast<TootAttachment>()
            ?.blurhash

        setIconDrawableId(
            activity,
            btnHideMedia,
            R.drawable.ic_close,
            color = colorTextContent,
            alphaMultiplier = stylerBoostAlpha
        )
    }
}

val reUrlGif by lazy {
    """\.gif(?:\z|\?|#)"""
        .toRegex(RegexOption.IGNORE_CASE)
}

fun ItemViewHolder.setMedia(
    mediaAttachments: ArrayList<TootAttachmentLike>,
    idx: Int,
) {
    val iv = ivMediaThumbnails[idx]
    val ta = if (idx < mediaAttachments.size) mediaAttachments[idx] else null
    if (ta == null) {
        iv.visibility = View.GONE
        return
    }

    iv.visibility = View.VISIBLE

    iv.setFocusPoint(ta.focusX, ta.focusY)

    if (PrefB.bpDontCropMediaThumb.value) {
        iv.scaleType = ImageView.ScaleType.FIT_CENTER
    } else {
        iv.setScaleTypeForMedia()
    }

    val showUrl: Boolean

    when (ta.type) {
        TootAttachmentType.Audio -> {
            iv.setMediaType(0)
            iv.setDefaultImage(defaultColorIcon(activity, R.drawable.wide_music))
            iv.setImageUrl(0f, ta.urlForThumbnail())
            showUrl = true
        }

        TootAttachmentType.Unknown -> {
            iv.setMediaType(0)
            iv.setDefaultImage(defaultColorIcon(activity, R.drawable.wide_question))
            if (ta is TootAttachment &&
                reUrlGif.containsMatchIn(ta.remote_url ?: "") &&
                PrefB.bpImageAnimationEnable.value
            ) {
                val url = ta.remote_url!!
                iv.setImageUrl(0f, url, url)
            } else {
                iv.setImageUrl(0f, null)
            }
            showUrl = true
        }

        else -> if (ta is TootAttachment &&
            reUrlGif.containsMatchIn(ta.remote_url ?: "") &&
            PrefB.bpImageAnimationEnable.value
        ) {
            iv.setMediaType(0)
            iv.setDefaultImage(null)
            val url = ta.remote_url!!
            iv.setImageUrl(0f, url, url)
            showUrl = false
        } else when (val urlThumbnail = ta.urlForThumbnail()) {
            null, "" -> {
                iv.setMediaType(0)
                iv.setDefaultImage(defaultColorIcon(activity, R.drawable.wide_question))
                iv.setImageUrl(0f, null)
                showUrl = true
            }

            else -> {
                iv.setMediaType(
                    when (ta.type) {
                        TootAttachmentType.Video -> R.drawable.media_type_video
                        TootAttachmentType.GIFV -> R.drawable.media_type_gifv
                        else -> 0
                    }
                )
                iv.setDefaultImage(null)
                iv.setImageUrl(
                    0f,
                    accessInfo.supplyBaseUrl(urlThumbnail),
                    accessInfo.supplyBaseUrl(urlThumbnail)
                )
                showUrl = false
            }
        }
    }

    val desc = ta.description.notEmpty()
        ?: if (!showUrl) null else ta.urlForDescription.notEmpty()

    tvMediaDescriptions[idx].vg(
        !desc.isNullOrBlank() && column.showMediaDescription
    )?.let {
        //			val lp = LinearLayout.LayoutParams(
        //				LinearLayout.LayoutParams.MATCH_PARENT,
        //				LinearLayout.LayoutParams.WRAP_CONTENT
        //			)
        //			lp.topMargin = (0.5f + activity.density * 3f).toInt()
        //
        //			val tv = MyTextView(activity)
        //			tv.layoutParams = lp
        //			//
        //			tv.movementMethod = MyLinkMovementMethod
        //			if(! activity.timeline_font_size_sp.isNaN()) {
        //				tv.textSize = activity.timeline_font_size_sp
        //			}
        //			tv.setTextColor(content_color)
        it.setTag(R.id.text, desc)
        it.text = activity.getString(
            R.string.media_description,
            idx + 1,
            desc?.ellipsizeDot3(140)
        )
    }
}
