package com.crossbow.volley;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.support.v4.graphics.BitmapCompat;

import com.android.volley.VolleyLog;

import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Set;

/**

 */
public class BitmapScrapHeap {

    private static BitmapScrapHeap bitmapScrapHeap;

    private static final int UNUSED_BITMAP_COUNT = 50;

    private Set<WeakReference<Bitmap>> unusedBitmaps;

    public static BitmapScrapHeap get() {
        if(bitmapScrapHeap == null) {
            bitmapScrapHeap = new BitmapScrapHeap();
        }
        return bitmapScrapHeap;
    }

    private BitmapScrapHeap() {
        unusedBitmaps = Collections.newSetFromMap(new LinkedHashMap<WeakReference<Bitmap>, Boolean>(UNUSED_BITMAP_COUNT) {
            protected boolean removeEldestEntry(Entry<WeakReference<Bitmap>, Boolean> eldest) {
                boolean shouldRemove = size() > UNUSED_BITMAP_COUNT;
                if (shouldRemove) {
                    VolleyLog.d("Pruning unused size = " + size());

                    Bitmap toBeScrapped = eldest.getKey().get();
                    if(toBeScrapped != null) {
                        toBeScrapped.recycle();
                    }
                }
                return shouldRemove;
            }
        });
    }

    public Bitmap getBitmapToFill(BitmapFactory.Options options) {
        synchronized (unusedBitmaps) {
            Iterator<WeakReference<Bitmap>> iterator = unusedBitmaps.iterator();

            while (iterator.hasNext()) {
                Bitmap bitmap = iterator.next().get();
                if(bitmap != null) {
                    if(bitmap.isMutable() && canUseForInBitmap(bitmap, options)) {
                        iterator.remove();
                        return bitmap;
                    }
                }
                else {
                    iterator.remove();
                }
            }
        }

        return null;
    }

    public  void storeForReUse(Bitmap bitmap) {
        synchronized(unusedBitmaps) {
            if(bitmapCanBeReused(bitmap)) {
                unusedBitmaps.add(new WeakReference<Bitmap>(bitmap));
            }
        }
    }

    private static boolean bitmapCanBeReused(Bitmap bitmap) {
        return bitmap != null && !bitmap.isRecycled() && bitmap.isMutable();
    }

    private static boolean canUseForInBitmap(Bitmap candidate, BitmapFactory.Options targetOptions) {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {

            if(targetOptions.inSampleSize == 0) {
                final int width = targetOptions.outWidth;
                final int height = targetOptions.outHeight;
                final int byteCount = width * height * getBytesPerPixel(candidate.getConfig());
                return byteCount <= BitmapCompat.getAllocationByteCount(candidate);
            }
            final int  width = targetOptions.outWidth / targetOptions.inSampleSize;
            final int height = targetOptions.outHeight / targetOptions.inSampleSize;
            final int byteCount = width * height * getBytesPerPixel(candidate.getConfig());
            return byteCount <= BitmapCompat.getAllocationByteCount(candidate);
        }
        else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            return candidate.getWidth() == targetOptions.outWidth && candidate.getHeight() == targetOptions.outHeight && targetOptions.inSampleSize == 1;
        }
        else {
            return false;
        }
    }

    private static int getBytesPerPixel(Bitmap.Config config) {
        if (config == Bitmap.Config.ARGB_8888) {
            return 4;
        }
        else if (config == Bitmap.Config.RGB_565) {
            return 2;
        }
        else if (config == Bitmap.Config.ARGB_4444) {
            return 2;
        }
        else if (config == Bitmap.Config.ALPHA_8) {
            return 1;
        }
        return 1;
    }
}
