package com.lemenzo.gallery.loader;

import android.content.ContentResolver;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.Process;
import android.util.Log;
import android.util.Size;
import android.widget.ImageView;

import com.lemenzo.gallery.GalleryApplication;

import java.io.IOException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public final class ThumbnailLoader {
    private static final String TAG = "LemenzoGalleryThumb";
    private static final int THREADS = 3;
    private static final int MAX_QUEUED_REQUESTS = 72;

    private final ContentResolver resolver;
    private final BitmapMemoryCache cache;
    private final ThreadPoolExecutor pool;
    private final Handler main = new Handler(Looper.getMainLooper());
    private volatile boolean closed;

    public ThumbnailLoader(Context context) {
        resolver = context.getContentResolver();
        cache = ((GalleryApplication) context.getApplicationContext()).cache();
        AtomicInteger ids = new AtomicInteger();
        ThreadFactory factory = runnable -> {
            Thread thread = new Thread(() -> {
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                runnable.run();
            }, "LemenzoGalleryThumb-" + ids.incrementAndGet());
            thread.setDaemon(true);
            return thread;
        };
        pool = new ThreadPoolExecutor(
                THREADS,
                THREADS,
                20L,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(MAX_QUEUED_REQUESTS),
                factory,
                new ThreadPoolExecutor.DiscardOldestPolicy());
        pool.allowCoreThreadTimeOut(true);
    }

    public void load(ImageView view, Uri uri, int px) {
        if (closed || view == null || uri == null) return;
        int targetPx = Math.max(160, px);
        String key = uri + "@" + targetPx;
        view.setTag(key);

        Bitmap hit = cache.get(key);
        if (hit != null && !hit.isRecycled()) {
            view.setImageBitmap(hit);
            return;
        }

        view.setImageDrawable(null);
        try {
            pool.execute(() -> {
                if (closed) return;
                try {
                    Bitmap bitmap = resolver.loadThumbnail(uri, new Size(targetPx, targetPx), null);
                    if (bitmap == null || closed) return;
                    cache.put(key, bitmap);
                    main.post(() -> {
                        if (!closed && key.equals(view.getTag())) view.setImageBitmap(bitmap);
                    });
                } catch (SecurityException securityException) {
                    Log.w(TAG, "Thumbnail permission denied for " + uri, securityException);
                } catch (IOException ioException) {
                    Log.w(TAG, "Unable to read thumbnail for " + uri, ioException);
                } catch (RuntimeException runtimeException) {
                    Log.w(TAG, "Unable to load thumbnail for " + uri, runtimeException);
                }
            });
        } catch (RejectedExecutionException ignored) {
            // RecyclerView is moving faster than decode can keep up. The next bind will retry.
        }
    }

    public void cancel(ImageView view) {
        if (view == null) return;
        view.setTag(null);
        view.setImageDrawable(null);
    }

    public void close() {
        closed = true;
        pool.getQueue().clear();
        pool.shutdownNow();
        main.removeCallbacksAndMessages(null);
    }
}
