package com.gh.base

import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.app.Activity
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.os.Bundle
import android.text.TextUtils
import android.view.View
import android.webkit.JavascriptInterface
import android.widget.CheckBox
import android.widget.FrameLayout
import android.widget.TextView
import androidx.lifecycle.Observer
import com.gh.common.util.DialogUtils
import com.gh.common.util.NewLogUtils
import com.gh.common.view.RichEditor
import com.gh.gamecenter.CropImageActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.activity.ToolBarActivity
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.runOnIoThread
import com.gh.gamecenter.core.utils.*
import com.gh.gamecenter.entity.*
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.qa.editor.*
import com.gh.gamecenter.feature.entity.AnswerEntity
import com.gh.gamecenter.feature.entity.ArticleEntity
import com.gh.gamecenter.qa.entity.EditorInsertEntity
import com.gh.gamecenter.video.poster.PosterEditActivity
import com.gh.gamecenter.video.upload.UploadManager
import com.google.gson.JsonObject
import com.halo.assistant.HaloApp
import com.lightgame.utils.Util_System_Keyboard
import com.lightgame.utils.Utils
import com.lightgame.view.CheckableImageView
import io.reactivex.disposables.Disposable
import org.json.JSONArray
import org.json.JSONObject
import java.io.File

// TODO: 移动到module_bbs模块
abstract class BaseRichEditorActivity<VM : BaseRichEditorViewModel> : ToolBarActivity(),
    KeyboardHeightObserver, UploadVideoListener {

    lateinit var mRichEditor: RichEditor

    private lateinit var mEditorTextNumTv: TextView
    private lateinit var mEditorFont: CheckableImageView
    private lateinit var mEditorLink: CheckableImageView

    private lateinit var mEditorFontBold: CheckableImageView
    private lateinit var mEditorFontItalic: CheckableImageView
    private lateinit var mEditorFontStrikeThrough: CheckableImageView
    private lateinit var mEditorFontUnderline: CheckableImageView
    private lateinit var mEditorParagraphH1: CheckableImageView
    private lateinit var mEditorParagraphH2: CheckableImageView
    private lateinit var mEditorParagraphH3: CheckableImageView
    private lateinit var mEditorParagraphH4: CheckableImageView
    private lateinit var mEditorParagraphQuote: CheckableImageView
    private lateinit var mEditorFontContainer: View
    private lateinit var mEditorParagraphContainer: View
    private lateinit var mEditorLinkContainer: View
    private lateinit var mEditorInsertDetailContainer: View
    private lateinit var mTagsContainer: FrameLayout
    private lateinit var mUploadVideoGuideContainer: View
    protected lateinit var mOriginalCb: CheckBox
    private lateinit var mOriginalTipsContainer: View
    private lateinit var mOriginalTipsClose: TextView

    private var mCurrentParagraphStyle = ""
    private var mIsExtendedKeyboardShow = false
    private var mAgreePostPic: Boolean = false
    private var mGuideDisposable: Disposable? = null
    protected lateinit var mViewModel: VM
    protected var mIsKeyBoardShow = false
    private var mKeyboardHeightProvider: KeyboardHeightProvider? = null
    private var mMaxUploadVideoGuideCount = 2
    val FILE_HOST = "file:///"

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        DialogUtils.fixWebViewKeyboardNotWorking(this)
        if (resultCode != Activity.RESULT_OK) return
        val insertData: EditorInsertEntity?
        when (requestCode) {
            INSERT_ANSWER_CODE -> {
                val answer =
                    data?.getParcelableExtra<AnswerEntity>(AnswerEntity::class.java.simpleName)
                if (answer != null) {
                    mRichEditor.focusEditor()
                    insertData = EditorInsertEntity.transform(answer)
                    mRichEditor.insertCustomStyleLink(insertData)
                }
            }
            INSERT_ARTICLE_CODE -> {
                val article =
                    data?.getParcelableExtra<ArticleEntity>(ArticleEntity::class.java.simpleName)
                if (article != null) {
                    mRichEditor.focusEditor()
                    insertData = EditorInsertEntity.transform(article)
                    mRichEditor.insertCustomStyleLink(insertData)
                }
            }
            INSERT_GAME_CODE -> {
                val game = data?.getParcelableExtra<GameEntity>(GameEntity::class.java.simpleName)
                if (game != null) {
                    mRichEditor.focusEditor()
                    insertData = EditorInsertEntity.transform(game)
                    mRichEditor.insertCustomStyleLink(insertData)
                }
            }
            INSERT_GAME_COLLECTION_CODE -> {
                val gameCollectionEntity =
                    data?.getParcelableExtra<GamesCollectionEntity>(GamesCollectionEntity::class.java.simpleName)
                if (gameCollectionEntity != null) {
                    mRichEditor.focusEditor()
                    insertData = EditorInsertEntity.transform(gameCollectionEntity)
                    mRichEditor.insertCustomStyleLink(insertData)
                }
            }
            REQUEST_CODE_IMAGE -> {
                if (data != null) mViewModel.uploadPic(data)
            }
            INSERT_MEDIA_VIDEO_CODE -> {
                val localVideoList =
                    data?.getParcelableArrayListExtra<LocalVideoEntity>(LocalVideoEntity::class.java.name)
                        ?: arrayListOf()
                if (localVideoList.isNotEmpty()) {
                    mRichEditor.focusEditor()
                    uploadVideo(localVideoList)
                }
            }
            REQUEST_CODE_IMAGE_CROP -> {
                val imagePath = data?.getStringExtra(CropImageActivity.RESULT_CLIP_PATH)
                if (!imagePath.isNullOrEmpty()) {
                    mViewModel.uploadPoster(imagePath)
                }
            }
            INSERT_VIDEO_CODE -> {
                val videoEntity = data?.getParcelableExtra<MyVideoEntity>(MyVideoEntity::class.java.simpleName)
                if (videoEntity != null) {
                    mRichEditor.focusEditor()
                    insertData = EditorInsertEntity.transform(videoEntity)
                    mRichEditor.insertCustomStyleLink(insertData)
                }
            }
        }
        closeExtendedKeyboard()
        AppExecutor.uiExecutor.executeWithDelay(Runnable {
            Util_System_Keyboard.showSoftKeyboard(this)
        }, 100)
    }

    private fun uploadVideo(localVideoList: ArrayList<LocalVideoEntity>) {
        mViewModel.localVideoList.addAll(localVideoList)
        runOnIoThread {
            localVideoList.forEach {
                if (it.poster.startsWith("http")) {
                    runOnUiThread {
                        mRichEditor.focusEditor()
                        mRichEditor.insertPlaceholderVideo(it.id, it.poster)
                    }
                } else {
                    val videoThumbnail = BitmapUtils.getVideoThumbnail(it.filePath)
                    val filePath = "${cacheDir.absolutePath}${File.separator}${it.id}.webp"
                    BitmapUtils.saveBitmap(videoThumbnail, filePath)
                    it.poster = filePath
                    runOnUiThread {
                        mRichEditor.focusEditor()
                        mRichEditor.insertPlaceholderVideo(it.id, "$FILE_HOST${it.poster}")
                    }
                }
            }
            mViewModel.uploadVideo()
        }

    }

    private fun findView() {
        mRichEditor = findViewById(R.id.rich_editor)
        mEditorTextNumTv = findViewById(R.id.editorTextNumTv)
        mEditorFont = findViewById(R.id.editor_font)
        mEditorLink = findViewById(R.id.editor_link)
        mEditorFontBold = findViewById(R.id.editor_font_bold)
        mEditorFontItalic = findViewById(R.id.editor_font_italic)
        mEditorFontStrikeThrough = findViewById(R.id.editor_font_strikethrough)
        mEditorFontUnderline = findViewById(R.id.editor_font_underline)
        mEditorParagraphH1 = findViewById(R.id.editor_paragraph_h1)
        mEditorParagraphH2 = findViewById(R.id.editor_paragraph_h2)
        mEditorParagraphH3 = findViewById(R.id.editor_paragraph_h3)
        mEditorParagraphH4 = findViewById(R.id.editor_paragraph_h4)
        mEditorParagraphQuote = findViewById(R.id.editor_paragraph_quote)
        mEditorFontContainer = findViewById(R.id.editor_font_container)
        mEditorParagraphContainer = findViewById(R.id.editor_paragraph_container)
        mEditorLinkContainer = findViewById(R.id.editor_link_container)
        mEditorInsertDetailContainer = findViewById(R.id.editor_insert_detail_container)
        mTagsContainer = findViewById(R.id.tagsContainer)
        mUploadVideoGuideContainer = findViewById(R.id.uploadVideoGuideContainer)
        mOriginalCb = findViewById(R.id.originalCb)
        mOriginalTipsContainer = findViewById(R.id.originalTipsContainer)
        mOriginalTipsClose = findViewById(R.id.originalTipsClose)
    }

    @SuppressLint("AddJavascriptInterface", "ClickableViewAccessibility")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        findView()
        onRichClick()
        mViewModel = provideViewModel()
        mViewModel.setUploadVideoListener(this)
        mKeyboardHeightProvider = KeyboardHeightProvider(this)
        mRichEditor.post { mKeyboardHeightProvider?.start() }
        mRichEditor.enableForceDark(DarkModeUtils.isDarkModeOn(this))
        mRichEditor.setEditorBackgroundColor(R.color.background_white.toColor(this))
        mRichEditor.setEditorFontColor(if (mIsDarkModeOn) Color.parseColor("#C2C2C2") else Color.parseColor("#4A4A4A"))
        // 防止个别手机在Js里无法获取粘贴内容
        mRichEditor.addJavascriptInterface(OnPasteListener(), "onPasteListener")
        mRichEditor.addJavascriptInterface(OnCursorChangeListener(), "OnCursorChangeListener")
        mRichEditor.addJavascriptInterface(
            OnEditorTextChangeListener(),
            "OnEditorTextChangeListener"
        )
        mRichEditor.addJavascriptInterface(OnVideoListener(), "onVideoListener")
        mRichEditor.addJavascriptInterface(
            OnQuoteCountChangeListener(),
            "OnQuoteCountChangeListener"
        )
        mRichEditor.setInputEnabled(true)
        mRichEditor.setPadding(16, 12, 16, 12)

        mRichEditor.setOnTouchListener { _, _ ->
            if (mIsExtendedKeyboardShow) {
                closeExtendedKeyboard()
                Util_System_Keyboard.showSoftKeyboard(this)
                //是否消费事件根据mRichEditor是否含有焦点决定，mRichEditor没有焦点则不消费事件
                mRichEditor.hasFocus()
            } else false
        }
        mOriginalCb.setOnCheckedChangeListener { _, isChecked ->
            if (isChecked) {
                mOriginalTipsContainer.alpha = 0f
                mOriginalTipsContainer.visibility = View.VISIBLE
                ObjectAnimator.ofFloat(mOriginalTipsContainer, "alpha", 0f, 1f).setDuration(200).start()
            }
        }
        observeData()
    }

    private fun observeData() {
        mViewModel.chooseImagesUpload.observe(this, Observer {
            mRichEditor.focusEditor()
            for (key in it.keys) {
                mRichEditor.insertPlaceholderImage(key)
            }
        })
        mViewModel.chooseImagesUploadSuccess.observe(this, Observer {
            val jsonArray = JSONArray()
            for (key in it.keys) {
                val jsonObject = JSONObject()
                jsonObject.put("id", key)
                jsonObject.put("url", it[key])
                jsonArray.put(jsonObject)
            }
            mRichEditor.replacePlaceholderImage(jsonArray.toString())
        })
    }

    override fun onKeyboardHeightChanged(height: Int, orientation: Int) {
        mIsKeyBoardShow = height > 0
        if (height > 0) {
            closeExtendedKeyboard()
        }
    }

    fun closeExtendedKeyboard() {
        mEditorInsertDetailContainer.visibility = View.GONE
        mEditorFont.isChecked = false
        mEditorLink.isChecked = false
        mIsExtendedKeyboardShow = false
    }

    protected fun controlEditorInsertContainerEnabled(isEnabled: Boolean) {
        mEditorFont.isEnabled = isEnabled
    }

    private fun onRichClick() {
        mEditorFont.setOnClickListener {
            controlEditorFontContainer()
        }
        mEditorLink.setOnClickListener {
            controlEditorLinkContainer()
        }
        mEditorFontBold.setOnClickListener {
            mEditorFontBold.isChecked = !mEditorFontBold.isChecked
            mRichEditor.setBold()
            if (mEditorFontBold.isChecked) {
                MtaHelper.onEvent(mtaEventName(), "文字样式", "文字样式-加粗")
            }
        }
        mEditorFontItalic.setOnClickListener {
            mEditorFontItalic.isChecked = !mEditorFontItalic.isChecked
            mRichEditor.setItalic()
            if (mEditorFontItalic.isChecked) {
                MtaHelper.onEvent(mtaEventName(), "文字样式", "文字样式-斜体")
            }
        }
        mEditorFontStrikeThrough.setOnClickListener {
            mEditorFontStrikeThrough.isChecked = !mEditorFontStrikeThrough.isChecked
            mRichEditor.setStrikeThrough()

            if (mEditorFontStrikeThrough.isChecked) {
                MtaHelper.onEvent(mtaEventName(), "文字样式", "文字样式-删除线")
            }
        }
        mEditorFontUnderline.setOnClickListener {
            mEditorFontUnderline.isChecked = !mEditorFontUnderline.isChecked
            mRichEditor.setUnderline()

            if (mEditorFontUnderline.isChecked) {
                MtaHelper.onEvent(mtaEventName(), "文字样式", "文字样式-下滑线")
            }
        }
        mEditorParagraphH1.setOnClickListener {
            if (mEditorParagraphH1.isChecked) {
                mRichEditor.formatBlock()
            } else {
                MtaHelper.onEvent(mtaEventName(), "段落样式", "段落样式-1级标题")
                mRichEditor.setHeading(1)
            }
            mEditorParagraphH1.isChecked = !mEditorParagraphH1.isChecked
        }
        mEditorParagraphH2.setOnClickListener {
            if (mEditorParagraphH2.isChecked) {
                mRichEditor.formatBlock()
            } else {
                MtaHelper.onEvent(mtaEventName(), "段落样式", "段落样式-2级标题")
                mRichEditor.setHeading(2)
            }
            mEditorParagraphH2.isChecked = !mEditorParagraphH2.isChecked
        }
        mEditorParagraphH3.setOnClickListener {
            if (mEditorParagraphH3.isChecked) {
                mRichEditor.formatBlock()
            } else {
                MtaHelper.onEvent(mtaEventName(), "段落样式", "段落样式-3级标题")
                mRichEditor.setHeading(3)
            }
            mEditorParagraphH3.isChecked = !mEditorParagraphH3.isChecked
        }
        mEditorParagraphH4.setOnClickListener {
            if (mEditorParagraphH4.isChecked) {
                mRichEditor.formatBlock()
            } else {
                MtaHelper.onEvent(mtaEventName(), "段落样式", "段落样式-4级标题")
                mRichEditor.setHeading(4)
            }
            mEditorParagraphH4.isChecked = !mEditorParagraphH4.isChecked
        }
        mEditorParagraphQuote.setOnClickListener {
            if (mEditorParagraphQuote.isChecked) {
                mRichEditor.formatBlock()
            } else {
                MtaHelper.onEvent(mtaEventName(), "段落样式", "段落样式-引用")
                mRichEditor.setBlockquote()
            }
            mEditorParagraphQuote.isChecked = !mEditorParagraphQuote.isChecked
        }
        findViewById<View>(R.id.editor_link_answer).setOnClickListener {
            MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-回答")
            startActivityForResult(
                InsertAnswerWrapperActivity.getIntent(this),
                INSERT_ANSWER_CODE
            )
        }
        findViewById<View>(R.id.editor_link_article).setOnClickListener {
            MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-文章")
            startActivityForResult(
                InsertArticleWrapperActivity.getIntent(this),
                INSERT_ARTICLE_CODE
            )
        }
        findViewById<View>(R.id.editor_link_game).setOnClickListener {
            MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-游戏")
            startActivityForResult(
                GameActivity.getIntent(this, GameActivity.INSERT_GAME_TITLE),
                INSERT_GAME_CODE
            )
        }
        findViewById<View>(R.id.editor_link_video).setOnClickListener {
            startActivityForResult(
                InsertVideoWrapperActivity.getIntent(this),
                INSERT_VIDEO_CODE
            )
        }
        findViewById<View>(R.id.editor_link_game_collection).setOnClickListener {
            startActivityForResult(
                InsertGameCollectionWrapperActivity.getIntent(this),
                INSERT_GAME_COLLECTION_CODE
            )
        }
        findViewById<View>(R.id.editor_video).setOnClickListener {
            chooseVideo()
        }
        findViewById<View>(R.id.editor_image).setOnClickListener {
            if (!mAgreePostPic && !NetworkUtils.isWifiOr4GOr3GConnected(this)) {
                mAgreePostPic = true
                DialogHelper.showDialog(
                    this,
                    "警告",
                    "当前使用移动网络，上传图片会消耗手机流量",
                    "我知道了", "", { chooseImage() },
                    extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)
                )
                return@setOnClickListener
            }
            chooseImage()
            NewLogUtils.logChooseMedia(
                "view_media",
                if (mtaEventName() == "提问帖") "提问帖" else "帖子",
                "图片"
            )
        }
        findViewById<View>(R.id.uploadVideoGuideClose).setOnClickListener {
            hideUploadVideoGuide()
            if (mGuideDisposable != null && !mGuideDisposable!!.isDisposed) {
                mGuideDisposable!!.dispose()
                mGuideDisposable = null
            }
        }
        findViewById<View>(R.id.originalTipsClose).setOnClickListener {
            val animator = ObjectAnimator.ofFloat(mOriginalTipsContainer, "alpha", 1f, 0f).setDuration(200)
            animator.doOnEnd {
                mOriginalTipsContainer.visibility = View.GONE
            }
            animator.start()
        }
    }

    private fun chooseVideo() {
        MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-视频")
        val videoCount = mViewModel.quoteCountEntity.videoCount
        if (videoCount >= MAX_MEDIA_COUNT) {
            toast(R.string.answer_edit_max_video_hint)
            return
        }
        try {
            PermissionHelper.checkStoragePermissionBeforeAction(this,
                object : EmptyCallback {
                    override fun onCallback() {
                        val maxChooseCount = if (videoCount + 3 <= MAX_MEDIA_COUNT) 3 else MAX_MEDIA_COUNT - videoCount
                        startActivityForResult(
                            LocalMediaActivity.getIntent(
                                this@BaseRichEditorActivity,
                                LocalMediaActivity.ChooseType.VIDEO,
                                maxChooseCount,
                                if (mtaEventName() == "提问帖") "发提问帖" else "发帖子"
                            ), INSERT_MEDIA_VIDEO_CODE
                        )
                        NewLogUtils.logChooseMedia(
                            "view_media",
                            if (mtaEventName() == "提问帖") "提问帖" else "帖子",
                            "视频"
                        )
                    }
                })
        } catch (e: Exception) {
            toast(R.string.media_image_hint)
            e.printStackTrace()
        }
    }

    private fun chooseImage() {
        MtaHelper.onEvent(mtaEventName(), "插入图片", "插入图片")
        val imageCount = mViewModel.quoteCountEntity.imageCount
        if (imageCount >= MAX_IMAGE_COUNT) {
            toast(R.string.answer_edit_max_img_hint)
            return
        }
        try {
            PermissionHelper.checkStoragePermissionBeforeAction(this, object : EmptyCallback {
                override fun onCallback() {
                    val maxChooseCount = if (imageCount + 10 <= MAX_IMAGE_COUNT) 10 else MAX_IMAGE_COUNT - imageCount
                    val intent = LocalMediaActivity.getIntent(
                        this@BaseRichEditorActivity,
                        LocalMediaActivity.ChooseType.IMAGE,
                        maxChooseCount,
                        if (mtaEventName() == "提问帖") "发提问帖" else "发帖子"
                    )
                    startActivityForResult(intent, REQUEST_CODE_IMAGE)
                }
            })
        } catch (e: Exception) {
            toast(R.string.media_image_hint)
            e.printStackTrace()
        }
    }

    private fun controlEditorFontContainer() {
        mEditorFont.isChecked = !mEditorFont.isChecked
        mEditorLink.isChecked = false
        val isShouldDelay = if (mEditorFont.isChecked) {
            Util_System_Keyboard.hideSoftKeyboard(this)
            true
        } else {
            Util_System_Keyboard.showSoftKeyboard(this)
            false
        }
        mEditorInsertDetailContainer.postDelayed({
            mEditorInsertDetailContainer.visibility =
                if (mEditorFont.isChecked) View.VISIBLE else View.GONE
            mEditorFontContainer.visibility = if (mEditorFont.isChecked) View.VISIBLE else View.GONE
            mEditorParagraphContainer.visibility =
                if (mEditorFont.isChecked) View.VISIBLE else View.GONE
            mEditorLinkContainer.visibility = View.GONE
            mTagsContainer.visibility = View.GONE
            mIsExtendedKeyboardShow = mEditorFont.isChecked
        }, if (isShouldDelay) 200 else 0L)
    }

    private fun controlEditorLinkContainer() {
        mEditorLink.isChecked = !mEditorLink.isChecked
        mEditorFont.isChecked = false
        val isShouldDelay = if (mEditorLink.isChecked) {
            Util_System_Keyboard.hideSoftKeyboard(this)
            true
        } else {
            Util_System_Keyboard.showSoftKeyboard(this)
            false
        }
        mEditorInsertDetailContainer.postDelayed({
            mEditorInsertDetailContainer.visibility =
                if (mEditorLink.isChecked) View.VISIBLE else View.GONE
            mEditorLinkContainer.visibility = if (mEditorLink.isChecked) View.VISIBLE else View.GONE
            mEditorFontContainer.visibility = View.GONE
            mEditorParagraphContainer.visibility = View.GONE
            mTagsContainer.visibility = View.GONE
            mIsExtendedKeyboardShow = mEditorLink.isChecked
        }, if (isShouldDelay) 200 else 0L)
    }


    override fun handleBackPressed(): Boolean {
        if (mIsExtendedKeyboardShow) {
            closeExtendedKeyboard()
            return true
        }
        return super.handleBackPressed()
    }

    override fun onResume() {
        super.onResume()
        mKeyboardHeightProvider?.setKeyboardHeightObserver(this)
    }

    override fun onPause() {
        super.onPause()
        mKeyboardHeightProvider?.setKeyboardHeightObserver(null)
    }

    //视频上传功能引导
    fun showUploadVideoGuide() {
        mUploadVideoGuideContainer.postDelayed({
            val count = SPUtils.getInt(getVideoGuideKey(), 0)
            if (count >= mMaxUploadVideoGuideCount) return@postDelayed
            mUploadVideoGuideContainer.alpha = 0f
            mUploadVideoGuideContainer.visibility = View.VISIBLE
            mUploadVideoGuideContainer.animate().alpha(1f).setDuration(200).start()
            mGuideDisposable = countDownTimer(3) { finish, _ ->
                if (finish) {
                    hideUploadVideoGuide()
                }
            }
            SPUtils.setInt(getVideoGuideKey(), count + 1)
        }, 1000)
    }

    fun hideUploadVideoGuide() {
        val animate = mUploadVideoGuideContainer.animate().alpha(0f).setDuration(200)
        animate.doOnEnd {
            mUploadVideoGuideContainer.visibility = View.GONE
        }
        animate.start()
    }

    override fun onDestroy() {
        super.onDestroy()
        mKeyboardHeightProvider?.close()
        val path = mViewModel.currentUploadingVideo?.filePath
        if (path != null && UploadManager.isUploading(path)) {
            UploadManager.cancelTask(path)
        }
        if (mGuideDisposable != null && !mGuideDisposable!!.isDisposed) {
            mGuideDisposable!!.dispose()
            mGuideDisposable = null
        }
    }

    private inner class OnCursorChangeListener {
        @JavascriptInterface
        fun onElements(elements: String) {
            Utils.log("-----------------------")
            Utils.log(elements)
            Utils.log(mRichEditor.html)
            Utils.log("-----------------------")

            mCurrentParagraphStyle = when {
                elements.contains(ELEMENT_PARAGRAPH_QUOTE) -> ELEMENT_PARAGRAPH_QUOTE
                elements.contains(ELEMENT_PARAGRAPH_P) -> ELEMENT_PARAGRAPH_P
                else -> ""
            }

            mBaseHandler.post {
                mEditorFontBold.isChecked = elements.contains(ELEMENT_NAME_BOLD)
                mEditorFontItalic.isChecked = elements.contains(ELEMENT_NAME_ITALIC)
                mEditorFontStrikeThrough.isChecked = elements.contains(ELEMENT_NAME_STRIKE)
                mEditorFontUnderline.isChecked = elements.contains(ELEMENT_NAME_UNDERLINE)
                mEditorParagraphH1.isChecked = elements.contains(ELEMENT_PARAGRAPH_H1)
                mEditorParagraphH2.isChecked = elements.contains(ELEMENT_PARAGRAPH_H2)
                mEditorParagraphH3.isChecked = elements.contains(ELEMENT_PARAGRAPH_H3)
                mEditorParagraphH4.isChecked = elements.contains(ELEMENT_PARAGRAPH_H4)
                mEditorParagraphQuote.isChecked = elements.contains(ELEMENT_PARAGRAPH_QUOTE)
            }
        }
    }

    private inner class OnPasteListener {
        @JavascriptInterface
        fun onPaste() {
            val clipboard =
                HaloApp.getInstance().application.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
            val clipText = clipboard.text.toString()
            if (!TextUtils.isEmpty(clipText)) {
                // 替换换行符号否则 插入失败
                val text = clipText.replace("[  ]".toRegex(), "&nbsp;")
                    .replace("[\r\n]".toRegex(), "<br/>")
                mBaseHandler.post { mRichEditor.insertHtml(text) }
            }
        }
    }

    private inner class OnEditorTextChangeListener {
        @JavascriptInterface
        fun onTextChange(count: Int) {
            val num = if (count > MAX_INPUT_TEXT_NUM) MAX_INPUT_TEXT_NUM - count else count
            mEditorTextNumTv.post {
                mEditorTextNumTv.text = num.toString()
                mViewModel.quoteCountEntity.textCount = num
            }
        }
    }

    private inner class OnQuoteCountChangeListener {
        @JavascriptInterface
        fun onQuoteCountChange(
            imageCount: Int,
            articleCount: Int,
            answerCount: Int,
            videoCount: Int,
            gameCount: Int
        ) {
            mEditorTextNumTv.post {
                mViewModel.quoteCountEntity.apply {
                    this.imageCount = imageCount
                    this.articleCount = articleCount
                    this.answerCount = answerCount
                    this.videoCount = videoCount
                    this.gameCount = gameCount
                }
            }
        }
    }

    private inner class OnVideoListener {
        @JavascriptInterface
        fun showDeleteDialog(id: String) {
            DialogHelper.showDialog(this@BaseRichEditorActivity, "提示", "确定删除吗？", "确定", "取消", {
                runOnUiThread {
                    mRichEditor.delPlaceholderVideo(id)
                    mViewModel.deleteVideo(id)
                }
            })
        }

        @JavascriptInterface
        fun updatePoster(id: String, videoId: String, url: String) {
            mViewModel.id = id
            mViewModel.videoId = videoId
            val videoEntity = VideoEntity(url = url)
            val intent =
                PosterEditActivity.getIntentByVideo(this@BaseRichEditorActivity, videoEntity)
            startActivityForResult(intent, REQUEST_CODE_IMAGE_CROP)
        }

        @JavascriptInterface
        fun deleteUploadingVideo(id: String) {
            mViewModel.deleteVideo(id)
        }

        @JavascriptInterface
        fun reUploadVideo(id: String) {
            val video = mViewModel.uploadVideoErrorList.find { it.id == id }
            if (video != null) {
                mViewModel.localVideoList.add(video)
                mViewModel.uploadVideoErrorList.remove(video)
                mViewModel.uploadVideo()
            }
        }
    }

    override fun insertPlaceholderVideo(id: String, poster: String) {
        mRichEditor.insertPlaceholderVideo(id, poster)
    }

    override fun updateVideoProgress(id: String, progress: String) {
        mRichEditor.updateVideoProgress(id, progress)
    }

    override fun videoUploadFinished(id: String, url: String, msg: JsonObject) {
        try {
            val obj = JSONObject()
            obj.put("poster", msg.get("poster").asString)
            obj.put("url", msg.get("url").asString)
            obj.put("duration", RichEditor.formatVideoDuration(msg.get("length").asLong))
            obj.put("id", msg.get("_id").asString)
            obj.put("status", "pending")
            mRichEditor.videoUploadFinished(id, url, obj.toString())
        } catch (e: java.lang.Exception) {
            e.printStackTrace()
        }
    }

    override fun changePoster(id: String, poster: String) {
        mRichEditor.changePoster(id, poster)
    }

    override fun videoUploadFailed(id: String) {
        mRichEditor.videoUploadFailed(id)
    }

    open fun getSelectedLabel(): Int = 0
    open fun onActivityDialogResult(requestCode: Int, resultCode: Int, data: Intent?) {}
    abstract fun mtaEventName(): String
    abstract fun provideViewModel(): VM
    abstract fun getVideoGuideKey(): String

    override fun isAutoResetViewBackgroundEnabled(): Boolean = true

    override fun onDarkModeChanged() {
        super.onDarkModeChanged()
        updateStatusBarColor(R.color.background_white, R.color.background_white)
        mRichEditor.enableForceDark(DarkModeUtils.isDarkModeOn(this))
        mRichEditor.setEditorBackgroundColor(R.color.background_white.toColor(this))
        mRichEditor.setEditorFontColor(if (mIsDarkModeOn) Color.parseColor("#C2C2C2") else Color.parseColor("#4A4A4A"))
    }

    companion object {
        const val ELEMENT_NAME_BOLD = " b "
        const val ELEMENT_NAME_ITALIC = " i "
        const val ELEMENT_NAME_STRIKE = " strike "
        const val ELEMENT_NAME_UNDERLINE = " u "
        const val ELEMENT_PARAGRAPH_H1 = " h1 "
        const val ELEMENT_PARAGRAPH_H2 = " h2 "
        const val ELEMENT_PARAGRAPH_H3 = " h3 "
        const val ELEMENT_PARAGRAPH_H4 = " h4 "
        const val ELEMENT_PARAGRAPH_P = " p "
        const val ELEMENT_PARAGRAPH_QUOTE = " blockquote "
        const val INSERT_ANSWER_CODE = 411
        const val INSERT_ARTICLE_CODE = 412
        const val INSERT_GAME_CODE = 413
        const val INSERT_GAME_COLLECTION_CODE = 414
        const val INSERT_VIDEO_CODE = 415
        const val MAX_INPUT_TEXT_NUM = 10000
        const val MAX_MEDIA_COUNT = 20
        const val MAX_IMAGE_COUNT = 35

        const val REQUEST_CODE_IMAGE = 120
        const val INSERT_MEDIA_VIDEO_CODE = 121
        const val REQUEST_CODE_IMAGE_CROP = 122
    }
}