package com.gh.gamecenter.common.utils

import android.annotation.SuppressLint
import android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.drawable.Animatable
import android.graphics.drawable.ColorDrawable
import android.net.Uri
import android.os.Environment
import android.text.TextUtils
import androidx.annotation.DrawableRes
import androidx.core.content.ContextCompat
import com.alibaba.android.arouter.launcher.ARouter
import com.facebook.common.executors.CallerThreadExecutor
import com.facebook.common.references.CloseableReference
import com.facebook.datasource.DataSource
import com.facebook.drawee.backends.pipeline.Fresco
import com.facebook.drawee.controller.BaseControllerListener
import com.facebook.drawee.controller.ControllerListener
import com.facebook.drawee.drawable.ScalingUtils
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder
import com.facebook.drawee.view.SimpleDraweeView
import com.facebook.imagepipeline.common.ResizeOptions
import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber
import com.facebook.imagepipeline.image.CloseableImage
import com.facebook.imagepipeline.image.ImageInfo
import com.facebook.imagepipeline.request.ImageRequest
import com.facebook.imagepipeline.request.ImageRequestBuilder
import com.facebook.imagepipeline.request.Postprocessor
import com.gh.gamecenter.common.HaloApp
import com.gh.gamecenter.common.R
import com.gh.gamecenter.common.callback.BiCallback
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.core.GHThreadFactory
import com.gh.gamecenter.core.provider.IAppProvider
import com.gh.gamecenter.core.provider.IConfigProvider
import com.gh.gamecenter.core.runOnUiThread
import com.gh.gamecenter.core.utils.MD5Utils
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.core.utils.TopCutProcess
import com.lightgame.utils.Utils
import com.squareup.picasso.LruCache
import com.squareup.picasso.Picasso
import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import java.io.*
import java.util.concurrent.Executors

object ImageUtils {

    private const val PIC_MAX_FILE_SIZE: Long = 10 * 1024 * 1024

    private val TINY_ANIMATED_IMAGE_SIZE by lazy { 30F.dip2px() }
    private val LARGE_ANIMATED_IMAGE_SIZE by lazy { 80F.dip2px() }
    private val STANDARD_ANIMATED_IMAGE_SIZE by lazy { 60F.dip2px() }

    val TAG_TARGET_WIDTH by lazy { R.dimen.width_placeholder }
    private val TAG_NO_MEMORY_CACHE by lazy { R.string.no_memory_cache }

    // 是否仅显示静态图片 (动图会转码为静态图片)
    private var mDisplayStaticImageOnly = false

    private val mImageUrlCacheSet by lazy { FixedSizeLinkedHashSet<String>(maxSize = 200) }
    private val mImageDecorationThread by lazy {
        Executors.newSingleThreadExecutor(
            GHThreadFactory("GH_IMAGE_DECORATOR_")
        )
    }

    @JvmStatic
    val picasso: Picasso by lazy {
        // Picasso 的 Lru 缓存改为 5M，默认设定是 1/7 的 heap 大小，太大了
        // https://stackoverflow.com/a/20105828/4812571
        Picasso.Builder(HaloApp.getInstance()).memoryCache(picassoLruCache).build()
    }

    @JvmStatic
    val picassoLruCache: LruCache by lazy {
        LruCache(5 * 1024 * 1024)
    }

    /**
     * 禁用动图，全局设置
     */
    @JvmStatic
    fun disableAnimatedImage() {
        mDisplayStaticImageOnly = true
    }

    @JvmStatic
    fun getUploadFileMaxSize(): Long {
        val configProvider = ARouter.getInstance().build(RouteConsts.provider.config).navigation() as? IConfigProvider
        val uploadLimitSize = configProvider?.getUploadLimitSize()
        if (uploadLimitSize != null) {
            return uploadLimitSize
        }
        return PIC_MAX_FILE_SIZE
    }

    @JvmStatic
    fun getDefaultGifRule(): String {
        val configProvider = ARouter.getInstance().build(RouteConsts.provider.config).navigation() as? IConfigProvider
        val gifConfig = configProvider?.getGif()
        if (gifConfig != null) {
            return gifConfig
        }
        return ""
    }

    @JvmStatic
    fun getWatermarkWidthGifRule(width: Int?): String {
        val configProvider = ARouter.getInstance().build(RouteConsts.provider.config).navigation() as? IConfigProvider
        val gifConfig = configProvider?.getGitThumb()
        val gifWaterMark = configProvider?.getGifWaterMark()
        if (gifConfig != null && gifWaterMark != null) {
            return "$gifConfig,w_$width$gifWaterMark"
        }
        return ""
    }


    @JvmStatic
    fun getLimitWidthRule(width: Int?): String {
        val configProvider = ARouter.getInstance().build(RouteConsts.provider.config).navigation() as? IConfigProvider
        val jpegConfig = configProvider?.getJpeg()
        if (jpegConfig != null) {
            return "$jpegConfig,w_$width"
        }
        return ""
    }

    @JvmStatic
    fun addLimitWidth(imageUrl: String?, width: Int?): String? {
        val configProvider = ARouter.getInstance().build(RouteConsts.provider.config).navigation() as? IConfigProvider
        val jpegConfig = configProvider?.getJpeg()
        var webpConfig = configProvider?.getWebp() ?: configProvider?.getGif() ?: ""

        // 全部图片转 png
        if (mDisplayStaticImageOnly) {
            webpConfig = "?x-oss-process=image/format,png/quality,q_90"
        }

        if (imageUrl?.contains("?x-oss-process") == false && jpegConfig != null) {
            // jpegConfig 为空的时候尝试用 webp 加载
            return if (width == 0 || width == null || TextUtils.isEmpty(jpegConfig)) {
                "$imageUrl$webpConfig"
            } else {
                "$imageUrl$jpegConfig,w_$width"
            }
        }
        return imageUrl
    }

    @JvmStatic
    fun addLimitHeight(imageUrl: String, height: Int): String {
        val configProvider = ARouter.getInstance().build(RouteConsts.provider.config).navigation() as? IConfigProvider
        val jpegConfig = configProvider?.getJpeg()
        if (jpegConfig != null) {
            return "$imageUrl$jpegConfig,h_$height"
        }
        return imageUrl
    }

    @JvmStatic
    fun addLimitWidthAndLoad(
        draweeView: SimpleDraweeView?,
        imageUrl: String?,
        width: Int?,
        onLoadListener: OnImageloadListener?
    ) {
        val newUrl = getTransformedUrl(imageUrl, width)
        val listener = object : BaseControllerListener<ImageInfo>() {
            override fun onFinalImageSet(
                id: String?,
                imageInfo: ImageInfo?,
                animatable: Animatable?
            ) {
                onLoadListener?.onLoadFinal(imageInfo)
            }
        }

        draweeView?.controller = Fresco.newDraweeControllerBuilder()
            .setUri(newUrl)
            .setControllerListener(listener)
            .build()
    }


    fun display(
        simpleDraweeView: SimpleDraweeView?,
        url: String?,
        width: Int?,
        listener: BaseControllerListener<ImageInfo>
    ) {
        simpleDraweeView?.controller = Fresco.newDraweeControllerBuilder()
            .setUri(getTransformedUrl(url, width))
            .setControllerListener(listener)
            .build()
    }

    // 自适应图片宽高
    @JvmStatic
    fun display(simpleDraweeView: SimpleDraweeView?, url: String?, width: Int) {
        val listener = object : BaseControllerListener<ImageInfo>() {
            override fun onFinalImageSet(
                id: String?,
                imageInfo: ImageInfo?,
                animatable: Animatable?
            ) {
                if (imageInfo == null) {
                    return
                }
                val layoutParams = simpleDraweeView?.layoutParams
                val scale = imageInfo.height.toFloat() / imageInfo.width.toFloat()
                layoutParams?.height = (width * scale).toInt()
                simpleDraweeView?.layoutParams = layoutParams
            }
        }
        simpleDraweeView?.controller = Fresco.newDraweeControllerBuilder()
            .setControllerListener(listener)
            .setUri(getTransformedUrl(url, width))
            .build()
    }

    // 设置缩放类型，设置按压状态下的叠加图
    @JvmStatic
    fun display(
        resources: Resources?, simpleDraweeView: SimpleDraweeView?, width: Int,
        scaleType: ScalingUtils.ScaleType?, url: String?
    ) {
        if (simpleDraweeView == null) return
        val context = simpleDraweeView.context ?: return
        simpleDraweeView.hierarchy = GenericDraweeHierarchyBuilder(resources)
            .setFadeDuration(500)
            .setPressedStateOverlay(
                ColorDrawable(
                    ContextCompat.getColor(
                        context,
                        R.color.pressed_bg
                    )
                )
            )
            .setPlaceholderImage(R.drawable.occupy2, ScalingUtils.ScaleType.CENTER)
            .setBackground(ColorDrawable(ContextCompat.getColor(context, R.color.placeholder_bg)))
            .setActualImageScaleType(scaleType)
            .build()
        simpleDraweeView.setImageURI(getTransformedUrl(url, width))
    }

    // 设置占位符
    @JvmStatic
    fun display(
        resources: Resources?,
        simpleDraweeView: SimpleDraweeView?,
        url: String?,
        placeholderImage: Int
    ) {
        if (simpleDraweeView == null) return
        val context = simpleDraweeView.context ?: return
        simpleDraweeView.hierarchy = GenericDraweeHierarchyBuilder(resources)
            .setFadeDuration(500)
            .setPressedStateOverlay(
                ColorDrawable(
                    ContextCompat.getColor(
                        context,
                        R.color.pressed_bg
                    )
                )
            )
            .setBackground(ColorDrawable(ContextCompat.getColor(context, R.color.placeholder_bg)))
            .setPlaceholderImage(placeholderImage)
            .build()
        display(simpleDraweeView, url)
    }

    // 图片下载监听和设置低高分辨率图片
    fun display(
        simpleDraweeView: SimpleDraweeView?, url: String?, lowUrl: String?,
        listener: ControllerListener<in ImageInfo>
    ) {
        simpleDraweeView?.controller = Fresco.newDraweeControllerBuilder()
            .setImageRequest(ImageRequest.fromUri(url))
            .setControllerListener(listener)
            .setLowResImageRequest(ImageRequest.fromUri(lowUrl)) // 低分辨率图片
            .build()
    }

    /**
     * 获取bitmap (使用 fresco 获取 gif 的 bitmap 会为空，https://github.com/facebook/fresco/issues/241)
     * 所以当 url 以 .gif 结尾时换用 picasso 获取
     * 优先使用 fresco 是因为可能已经有了缓存
     */
    @SuppressLint("CheckResult")
    @JvmStatic
    fun getBitmap(url: String, callback: BiCallback<Bitmap, Boolean>) {
        if (url.endsWith(".gif")) {
            getBitmapWithPicasso(url, callback)
        } else {
            getBitmapWithFresco(url, callback)
        }
    }

    private fun getBitmapWithFresco(url: String, callback: BiCallback<Bitmap, Boolean>) {
        val imageRequest = ImageRequestBuilder
            .newBuilderWithSource(Uri.parse(url))
            .build()
        Fresco.getImagePipeline()
            .fetchDecodedImage(imageRequest, HaloApp.getInstance())
            .subscribe(object : BaseBitmapDataSubscriber() {
                override fun onFailureImpl(dataSource: DataSource<CloseableReference<CloseableImage>>) {
                    callback.onSecond(true)
                }

                override fun onNewResultImpl(bitmap: Bitmap?) {
                    if (bitmap != null) {
                        callback.onFirst(bitmap)
                    } else {
                        callback.onSecond(true)
                    }
                }
            }, CallerThreadExecutor.getInstance())
    }

    @SuppressLint("CheckResult")
    private fun getBitmapWithPicasso(url: String, callback: BiCallback<Bitmap, Boolean>) {
        Single.just(url)
            .map {
                Picasso
                    .with(HaloApp.getInstance())
                    .load(url)
                    .priority(Picasso.Priority.HIGH)
                    .get()
            }
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe({
                callback.onFirst(it)
            }, {
                callback.onSecond(true)
                it.printStackTrace()
            })
    }

    @JvmStatic
    fun display(view: SimpleDraweeView?, url: String?) {
        display(view, url, true)
    }

    /**
     * 加载图片，但图片不放到内存缓存中
     * 适用于仅加载一次，应用启动生命周期内不再复用的场景
     */
    @JvmStatic
    fun displayWithoutMemoryCache(view: SimpleDraweeView?, url: String?) {
        view?.setTag(TAG_NO_MEMORY_CACHE, true)
        display(view, url, true)
    }

    /**
     *  ~~规则 width>0 Wifi/4G:x2 traffic:x1~~
     *  统一加载 webp 忽略掉第二种方案
     *  第一种方案:通过LayoutParams获取 可以快速(无延迟)获取宽高,但是无法获取wrap_content和match_parent的View
     *  ~~第二种方案(备用方案):有延迟,View的宽高需要在Measure过程后才能确定,能够在这里获取到正确的宽高~~
     *  @param shouldAutoPlayAnimatedImage 是否禁止播放动图
     */
    @JvmStatic
    fun display(
        view: SimpleDraweeView?,
        url: String?,
        shouldAutoPlayAnimatedImage: Boolean = true,
        processor: Postprocessor? = null,
        isIgnoreWidth: Boolean = false
    ) {
        displayInternal(view, url, shouldAutoPlayAnimatedImage, processor, isIgnoreWidth)
    }

    @JvmStatic
    private fun displayInternal(
        view: SimpleDraweeView?,
        url: String?,
        shouldAutoPlayAnimatedImage: Boolean = true,
        processor: Postprocessor? = null,
        isIgnoreWidth: Boolean = false
    ) {
        mImageDecorationThread.execute {
            if (url.isNullOrEmpty()) return@execute

            val targetWidth = view?.getTag(TAG_TARGET_WIDTH) as? Int
            val ignoreDefaultAnimatedImageResize = targetWidth != null && targetWidth != 0

            // 部分自适应宽高图片需要一个 TARGET_WIDTH 来避免加载过小图片
            val width = targetWidth ?: view?.width?.coerceAtLeast(view.layoutParams?.width ?: 0)
            val height = view?.layoutParams?.height
            val shouldNotSaveMemoryCache = view?.getTag(TAG_NO_MEMORY_CACHE) == true

            if (view != null && width == 0 && !isIgnoreWidth) {
                AsyncImageLoader.getInstance().addAsyncImage(view, url, shouldAutoPlayAnimatedImage, processor)
                return@execute
            }
            var lowResUrl = ""
            var highResUrl = ""

            // 找同一图片地址已加载过的图片作为低质量预览图(字符串操作耗时也不短，所以这里放到了独立的子线程来处理)
            // TODO 根据实际请求大小(w_width)来避免小图用大图作为低质量图片
            for (cachedImageUrl in mImageUrlCacheSet) {
                if (url.isNotEmpty() && cachedImageUrl.startsWith(url)) {
                    lowResUrl = cachedImageUrl
                    break
                }
            }

            val loadImageClosure: (autoPlay: Boolean, highResUrl: String, lowResUrl: String) -> Unit =
                { autoPlay, hUrl, lUrl ->
                    runOnUiThread {
                        val imageRequest = ImageRequestBuilder.newBuilderWithSource(Uri.parse(hUrl))
                            .apply {
                                if (shouldNotSaveMemoryCache) {
                                    disableMemoryCache()
                                }
                                postprocessor = processor
                            }
                            .build()
                        val controller = Fresco.newDraweeControllerBuilder()
                            .setImageRequest(imageRequest)
                            .apply {
                                if (lUrl.isNotEmpty()
                                    && lUrl != hUrl
                                    && hUrl != view?.getTag(R.string.highResImageTag)
                                ) {
                                    lowResImageRequest =
                                        ImageRequestBuilder.newBuilderWithSource(Uri.parse(lUrl))
                                            .setPostprocessor(processor)
                                            .build()
                                }
                                autoPlayAnimations = autoPlay
                            }
                            .build()
                        view?.controller = controller

                        view?.setTag(R.string.highResImageTag, highResUrl)
                    }
                }
            val appProvider = ARouter.getInstance().build(RouteConsts.provider.app).navigation() as? IAppProvider
            // 低于 2G 运行内存的不加载动图
            val loadAsAnimatedImage = url.endsWith(".gif") || url.endsWith(".webp")
                    && shouldAutoPlayAnimatedImage
                    && (appProvider?.getDeviceRamSize() == 0L || (appProvider?.getDeviceRamSize() ?: 0) > 2500)
                    && view?.getTag(R.id.tag_show_animated_image) != false

            if (loadAsAnimatedImage && view?.tag == url) return@execute

            if (!loadAsAnimatedImage || mDisplayStaticImageOnly) {
                highResUrl = getTransformedUrl(url, width ?: 0) ?: ""
                highResUrl = getCutUrl(highResUrl, width ?: 0, processor)
                loadImageClosure(loadAsAnimatedImage, highResUrl, lowResUrl)
            } else {
                if (width != null && width > 0) {
                    highResUrl = resizeAnimatedImage(url, width, height ?: 0, ignoreDefaultAnimatedImageResize)
                    highResUrl = getCutUrl(highResUrl, width, processor)
                    loadImageClosure(loadAsAnimatedImage, highResUrl, lowResUrl)
                } else {
                    runOnUiThread {
                        view ?: return@runOnUiThread
                        highResUrl = resizeAnimatedImage(url, width ?: view.width, height ?: 0, ignoreDefaultAnimatedImageResize)
                        highResUrl = getCutUrl(highResUrl, width ?: 0, processor)
                        loadImageClosure(loadAsAnimatedImage, highResUrl, lowResUrl)
                    }
                }
            }
            view?.tag = url
        }
    }
    
    private fun getCutUrl(url: String, width: Int, processor: Postprocessor?): String {
        if (width > 0 && processor != null) {
            var height = 0
            if (processor is TopCutProcess) {
                height = (width / processor.cutRatio).toInt()
            }
            if (height > 0) {
                return if (url.contains("?x-oss-process")) {
                    "${url}/crop,h_${height}"
                } else {
                    "${url}?x-oss-process=image/crop,h_${height}"
                }
            }
        }
        return url
    }

    @JvmStatic
    fun getTransformedUrl(url: String?, width: Int?): String? {
        if (url?.startsWith("res:") == true) return url

        val transformUrl: String? = if (width != null && width > 0) {
            addLimitWidth(url, width)
        } else {
            addLimitWidth(url, null)
        }
        addCachedUrl(transformUrl ?: "")
        return transformUrl
    }

    @JvmStatic
    fun displayIcon(view: SimpleDraweeView?, url: String?) {
        val width = view?.layoutParams?.width
        if (width != null && width > 0) {
            view.setImageURI(addLimitWidth(url, width))
        } else {
            view?.setImageURI(addLimitWidth(url, view.width))
        }
    }

    private fun addCachedUrl(url: String) {
        mImageDecorationThread.execute {
            if (url.startsWith("http")) {
                mImageUrlCacheSet.add(url)
            }
        }
    }

    /**
     * 获取已缓存的图片地址，不区分质量，暂时只供查看大图页面使用
     */
    fun getCachedUrl(url: String): String {
        // 创建一个 Snapshot Set 供主线程遍历用
        tryCatchInRelease {
            val snapshotUrlSet = HashSet<String>(mImageUrlCacheSet)
            for (decoratedUrl in snapshotUrlSet) {
                if (decoratedUrl.contains(url)) {
                    return decoratedUrl.also { snapshotUrlSet.clear() }
                }
            }
        }
        return url
    }

    @JvmStatic
    fun bmpToByteArray(bmp: Bitmap, needRecycle: Boolean): ByteArray {
        val output = ByteArrayOutputStream()
        bmp.compress(Bitmap.CompressFormat.PNG, 100, output)
        if (needRecycle) {
            bmp.recycle()
        }

        val result = output.toByteArray()
        try {
            output.close()
        } catch (e: Exception) {
            e.printStackTrace()
        }

        return result
    }


    @JvmStatic
    fun display(draweeView: SimpleDraweeView, @DrawableRes res: Int?) {
        draweeView.setImageURI("res:///" + res)
    }

    @JvmStatic
    fun displayResizeMedia(
        draweeView: SimpleDraweeView,
        url: String,
        resizeWidthDp: Int,
        resizeHeightDp: Int
    ) {
        val request = ImageRequestBuilder.newBuilderWithSource(Uri.parse(url))
            .setResizeOptions(ResizeOptions(resizeWidthDp, resizeHeightDp))
            .build()
        val controller = Fresco.newDraweeControllerBuilder()
            .setImageRequest(request)
            .setOldController(draweeView.controller)
            .setControllerListener(BaseControllerListener())
            .build()
        draweeView.controller = controller
    }

    //预加载图片
    @JvmStatic
    fun prefetchToDiskCache(url: String) {
        val imagePipeline = Fresco.getImagePipeline()
        val imageRequest = ImageRequest.fromUri(url)
        imagePipeline.prefetchToDiskCache(imageRequest, HaloApp.getInstance())
    }

    private fun resizeAnimatedImage(url: String, width: Int, height: Int, ignoreDefaultResize: Boolean): String {
        val idealSize = if (ignoreDefaultResize) width else getIdealAnimatedImageSize(width, height)
        return "$url?x-oss-process=image/resize,h_$idealSize,w_$idealSize".also { addCachedUrl(it) }
    }

    private fun getIdealAnimatedImageSize(width: Int, height: Int): String {
        return if (width > LARGE_ANIMATED_IMAGE_SIZE || height > LARGE_ANIMATED_IMAGE_SIZE) {
            "256"
        } else if (width >= STANDARD_ANIMATED_IMAGE_SIZE || height >= STANDARD_ANIMATED_IMAGE_SIZE) {
            "192"
        } else if (width > TINY_ANIMATED_IMAGE_SIZE || height > TINY_ANIMATED_IMAGE_SIZE) {
            "128"
        } else {
            "64"
        }
    }

    interface OnImageloadListener {
        fun onLoadFinal(imageInfo: ImageInfo?)
    }

    fun getVideoSnapshot(videoUrl: String, progress: Long): String {
        return "$videoUrl?x-oss-process=video/snapshot,t_$progress,f_jpg,w_0,h_0,ar_auto"
    }

    /**
     * 虽然在 Application 里有使用子线程初始化但有可能出现初始化超时(卡住？)的情况，
     * 这里反射获取 sDraweecontrollerbuildersupplier 根据是否有值确定是否被初始化了
     */
    @JvmStatic
    fun isFrescoInitialized(): Boolean {
        val clazz = SimpleDraweeView::class.java
        return try {
            val field =
                clazz.getDeclaredField("sDraweecontrollerbuildersupplier")
            field.isAccessible = true
            val obj = field[SimpleDraweeView::class.java]
            obj != null
        } catch (ignore: java.lang.Exception) {
            false
        }
    }

    /**
     * 将图片保存到外部存储空间
     * @param imageFile 内部存储的文件
     * @param url 图片原地址，用来截取当图片名称用的
     * @param useRandomName 是否使用随机名称
     */
    fun saveImageToFile(imageFile: File, url: String?, useRandomName: Boolean = false) {
        var `in`: InputStream? = null
        var out: OutputStream? = null
        try {
            val fileName: String = if (useRandomName) {
                MD5Utils.getContentMD5(System.currentTimeMillis().toString()) + ".png"
            } else {
                Uri.parse(url).lastPathSegment.toString()
            }
            val savePath =
                Environment.getExternalStorageDirectory().absolutePath + "/Pictures/ghzhushou/"
            val file = File(savePath)
            if (!file.exists()) {
                file.mkdirs()
            }
            val dst = File(savePath, fileName)
            if (dst.exists()) {
                dst.delete()
            }
            `in` = FileInputStream(imageFile)
            out = FileOutputStream(dst)
            val buf = ByteArray(1024)
            var len: Int
            while (`in`.read(buf).also { len = it } > 0) {
                out.write(buf, 0, len)
            }

            runOnUiThread {
                ToastUtils.toast("图片已保存到/Pictures/ghzhushou/")
            }
            BitmapUtils.refreshImage(HaloApp.getInstance(), dst)
        } catch (e: Exception) {
            Utils.log("图片保存失败：$e")
        } finally {
            tryWithDefaultCatch {
                out?.close()
                `in`?.close()
            }
        }
    }

    @JvmStatic
    fun clearUrlCaches() {
        mImageDecorationThread.execute {
            mImageUrlCacheSet.clear()
        }
    }
}
