package app.gwo.safenhancer.lite.util;

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.widget.ImageView;

import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.collection.LruCache;
import app.gwo.safenhancer.lite.R;
import io.reactivex.Scheduler;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;

public final class AppIconCacheUtils {

    private AppIconCacheUtils() {}

    private static final LruCache<String, Bitmap> sLruCache;

    /**
     * A replacement of Schedulers.computation();
     */
    private static final Scheduler sLoadIconScheduler;

    static {
        // Initialize app icon lru cache
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        final int availableCacheSize = maxMemory / 4;

        sLruCache = new AppIconLruCache(availableCacheSize);

        // Initialize load icon scheduler
        int availableProcessorsCount = 1;
        try {
            availableProcessorsCount = Runtime.getRuntime().availableProcessors();
        } catch (Exception ignored) {

        }
        final int threadCount = Math.max(1, availableProcessorsCount / 2);
        final Executor loadIconExecutor = Executors.newFixedThreadPool(threadCount);
        sLoadIconScheduler = Schedulers.from(loadIconExecutor);
    }

    public static Scheduler scheduler() {
        return sLoadIconScheduler;
    }

    @Nullable
    public static Bitmap getBitmapFromCache(@NonNull String packageName) {
        return sLruCache.get(packageName);
    }

    public static void putBitmapToCache(@NonNull String packageName, @NonNull Bitmap bitmap) {
        if (getBitmapFromCache(packageName) == null) {
            sLruCache.put(packageName, bitmap);
        }
    }

    public static void removeCache(@NonNull String packageName) {
        sLruCache.remove(packageName);
    }

    @Nullable
    public static Bitmap loadIconBitmap(@NonNull Context context, @NonNull ApplicationInfo info) {
        final Bitmap cachedBitmap = getBitmapFromCache(info.packageName);
        if (cachedBitmap != null) {
            return cachedBitmap;
        }
        final PackageManager pm = context.getPackageManager();
        Bitmap loadedResult = null;

        try {
            final int maxSize = context.getResources()
                    .getDimensionPixelSize(R.dimen.expected_app_icon_max_size);
            final Drawable icon;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
                icon = info.loadUnbadgedIcon(pm);
            } else {
                icon = info.loadIcon(pm);
            }
            loadedResult = DrawableUtils.toBitmap(icon, maxSize, maxSize, Bitmap.Config.ARGB_8888);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (loadedResult != null) {
            putBitmapToCache(info.packageName, loadedResult);
        }

        return loadedResult;
    }

    @Nullable
    public static Bitmap loadIconBitmap(@NonNull Context context,
                                        @NonNull String packageName) {
        final PackageManager pm = context.getPackageManager();
        try {
            return loadIconBitmap(context, pm.getApplicationInfo(packageName, 0));
        } catch (PackageManager.NameNotFoundException ignored) {
            return null;
        }
    }

    @NonNull
    public static Single<Bitmap> loadIconBitmapAsync(@NonNull Context context,
                                                     @NonNull ApplicationInfo info) {
        return Single.fromCallable(() -> loadIconBitmap(context, info))
                .subscribeOn(AppIconCacheUtils.scheduler())
                .observeOn(AndroidSchedulers.mainThread());
    }

    @NonNull
    public static Single<Bitmap> loadIconBitmapAsync(@NonNull Context context,
                                                     @NonNull String packageName) {
        try {
            final PackageManager pm = context.getPackageManager();
            final ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
            return loadIconBitmapAsync(context, info);
        } catch (PackageManager.NameNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    @NonNull
    public static Disposable loadIconBitmapToAsync(@NonNull Context context,
                                                   @NonNull ApplicationInfo info,
                                                   @NonNull ImageView view) {
        return loadIconBitmapAsync(context, info).subscribe(
                view::setImageBitmap,
                e -> view.setImageResource(R.mipmap.ic_default_app_icon)
        );
    }

    @NonNull
    public static Disposable loadIconBitmapToAsync(@NonNull Context context,
                                                   @NonNull String packageName,
                                                   @NonNull ImageView view) {
        return loadIconBitmapAsync(context, packageName).subscribe(
                view::setImageBitmap,
                e -> view.setImageResource(R.mipmap.ic_default_app_icon)
        );
    }

    private static class AppIconLruCache extends LruCache<String, Bitmap> {

        AppIconLruCache(int maxSize) {
            super(maxSize);
        }

        @Override
        protected int sizeOf(@NonNull String key, @NonNull Bitmap bitmap) {
            return bitmap.getByteCount() / 1024;
        }

    }

}