package cc.winboll.studio.powerbell.utils;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.text.TextUtils;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

import cc.winboll.studio.libappbase.LogUtils;

/**
 * @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
 * @Date 2025/12/11 01:57
 * @Describe 单例 Bitmap 缓存工具类（Java 7 兼容）
 * 功能：内存缓存 Bitmap，支持路径关联缓存、全局获取、缓存清空
 * 特点：1. 单例模式  2. 压缩加载避免OOM  3. 路径- Bitmap 映射  4. 线程安全
 */
public class BitmapCacheUtils {
    public static final String TAG = "BitmapCacheUtils";
    // 最大图片尺寸（适配1080P屏幕，可根据需求调整）
    private static final int MAX_WIDTH = 1080;
    private static final int MAX_HEIGHT = 1920;

    // 单例实例（volatile 保证多线程可见性）
    private static volatile BitmapCacheUtils sInstance;
    // 路径-Bitmap 缓存容器（内存缓存）
    private final Map<String, Bitmap> mBitmapCacheMap;

    // 私有构造器（单例模式）
    private BitmapCacheUtils() {
        mBitmapCacheMap = new HashMap<>();
    }

    /**
     * 获取单例实例（双重校验锁，线程安全）
     */
    public static BitmapCacheUtils getInstance() {
        if (sInstance == null) {
            synchronized (BitmapCacheUtils.class) {
                if (sInstance == null) {
                    sInstance = new BitmapCacheUtils();
                }
            }
        }
        return sInstance;
    }

    /**
     * 核心接口：根据图片路径缓存 Bitmap 到内存
     * @param imagePath 图片绝对路径
     * @return 缓存成功的 Bitmap / null（路径无效/文件不存在/解码失败）
     */
    public Bitmap cacheBitmap(String imagePath) {
        if (TextUtils.isEmpty(imagePath)) {
            LogUtils.e(TAG, "cacheBitmap: 图片路径为空");
            return null;
        }

        File imageFile = new File(imagePath);
        if (!imageFile.exists() || !imageFile.isFile() || imageFile.length() <= 0) {
            LogUtils.e(TAG, "cacheBitmap: 图片文件无效（不存在/非文件/空文件） - " + imagePath);
            return null;
        }

        // 已缓存则直接返回，避免重复加载
        if (mBitmapCacheMap.containsKey(imagePath)) {
            Bitmap cachedBitmap = mBitmapCacheMap.get(imagePath);
            // 额外校验缓存的Bitmap是否有效
            if (cachedBitmap != null && !cachedBitmap.isRecycled()) {
                LogUtils.d(TAG, "cacheBitmap: 图片已缓存，直接返回 - " + imagePath);
                return cachedBitmap;
            } else {
                // 缓存的Bitmap已失效，移除后重新加载
                mBitmapCacheMap.remove(imagePath);
                LogUtils.w(TAG, "cacheBitmap: 缓存Bitmap已失效，移除后重新加载 - " + imagePath);
            }
        }

        // 压缩加载 Bitmap（避免OOM）
        Bitmap bitmap = decodeCompressedBitmap(imagePath);
        if (bitmap != null) {
            // 存入缓存容器
            mBitmapCacheMap.put(imagePath, bitmap);
            LogUtils.d(TAG, "cacheBitmap: 图片缓存成功 - " + imagePath);
        } else {
            LogUtils.e(TAG, "cacheBitmap: 图片解码失败 - " + imagePath);
        }
        return bitmap;
    }

    /**
     * 核心接口：根据路径获取缓存的 Bitmap
     * @param imagePath 图片绝对路径
     * @return 缓存的有效 Bitmap / null（未缓存/已回收）
     */
    public Bitmap getCachedBitmap(String imagePath) {
        if (TextUtils.isEmpty(imagePath)) {
            return null;
        }
        Bitmap bitmap = mBitmapCacheMap.get(imagePath);
        // 校验Bitmap是否有效
        if (bitmap != null && bitmap.isRecycled()) {
            mBitmapCacheMap.remove(imagePath);
            return null;
        }
        return bitmap;
    }

    /**
     * 清空所有 Bitmap 缓存（释放内存）
     */
    public void clearAllCache() {
        synchronized (mBitmapCacheMap) {
            for (Bitmap bitmap : mBitmapCacheMap.values()) {
                if (bitmap != null && !bitmap.isRecycled()) {
                    bitmap.recycle(); // 主动回收 Bitmap
                }
            }
            mBitmapCacheMap.clear();
        }
        LogUtils.d(TAG, "clearAllCache: 所有 Bitmap 缓存已清空");
    }

    /**
     * 移除指定路径的 Bitmap 缓存
     * @param imagePath 图片绝对路径
     */
    public void removeCachedBitmap(String imagePath) {
        if (TextUtils.isEmpty(imagePath)) {
            return;
        }
        synchronized (mBitmapCacheMap) {
            Bitmap bitmap = mBitmapCacheMap.remove(imagePath);
            if (bitmap != null && !bitmap.isRecycled()) {
                bitmap.recycle();
                LogUtils.d(TAG, "removeCachedBitmap: 移除并回收缓存 - " + imagePath);
            }
        }
    }

    /**
     * 压缩解码 Bitmap（按最大尺寸缩放，避免OOM）
     * @param imagePath 图片绝对路径
     * @return 解码后的 Bitmap / null（文件无效/解码失败）
     */
    private Bitmap decodeCompressedBitmap(String imagePath) {
        // 前置校验：确保文件有效
        File imageFile = new File(imagePath);
        if (!imageFile.exists() || !imageFile.isFile() || imageFile.length() <= 0) {
            LogUtils.e(TAG, "decodeCompressedBitmap: 文件无效，跳过解码 - " + imagePath);
            return null;
        }

        BitmapFactory.Options options = new BitmapFactory.Options();
        // 第一步：只获取图片尺寸，不加载像素
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(imagePath, options);

        // 校验尺寸是否有效
        if (options.outWidth <= 0 || options.outHeight <= 0) {
            LogUtils.e(TAG, "decodeCompressedBitmap: 图片尺寸无效 - " + imagePath);
            return null;
        }

        // 计算缩放比例
        int sampleSize = calculateInSampleSize(options, MAX_WIDTH, MAX_HEIGHT);

        // 第二步：加载压缩后的 Bitmap
        options.inJustDecodeBounds = false;
        options.inSampleSize = sampleSize;
        options.inPreferredConfig = Bitmap.Config.RGB_565; // 节省内存（比ARGB_8888少一半内存）
        options.inPurgeable = true;
        options.inInputShareable = true;

        try {
            return BitmapFactory.decodeFile(imagePath, options);
        } catch (OutOfMemoryError e) {
            LogUtils.e(TAG, "decodeCompressedBitmap: OOM异常 - " + imagePath);
            return null;
        } catch (Exception e) {
            LogUtils.e(TAG, "decodeCompressedBitmap: 解码异常 - " + imagePath, e);
            return null;
        }
    }

    /**
     * 计算 Bitmap 缩放比例
     */
    private int calculateInSampleSize(BitmapFactory.Options options, int maxWidth, int maxHeight) {
        int rawWidth = options.outWidth;
        int rawHeight = options.outHeight;
        int inSampleSize = 1;

        if (rawWidth > maxWidth || rawHeight > maxHeight) {
            int halfWidth = rawWidth / 2;
            int halfHeight = rawHeight / 2;
            while ((halfWidth / inSampleSize) >= maxWidth && (halfHeight / inSampleSize) >= maxHeight) {
                inSampleSize *= 2;
            }
        }
        return inSampleSize;
    }
}

