package com.bumptech.glide.load.resource.bitmap;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.support.annotation.Nullable;
import android.util.Log;
import com.bumptech.glide.load.engine.Resource;
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPoolAdapter;
import com.bumptech.glide.request.target.Target;
import java.util.concurrent.locks.Lock;

final class DrawableToBitmapConverter {
  private static final String TAG = "DrawableToBitmap";
  private static final BitmapPool NO_RECYCLE_BITMAP_POOL = new BitmapPoolAdapter() {
    @Override
    public void put(Bitmap bitmap) {
      // Avoid calling super to avoid recycling the given Bitmap.
    }
  };
  private DrawableToBitmapConverter() {
    // Utility class.
  }

  @Nullable
  static Resource<Bitmap> convert(BitmapPool bitmapPool, Drawable drawable, int width, int height) {
    // Handle DrawableContainer or StateListDrawables that may contain one or more BitmapDrawables.
    drawable = drawable.getCurrent();
    Bitmap result = null;
    boolean isRecycleable = false;
    if (drawable instanceof BitmapDrawable) {
      result = ((BitmapDrawable) drawable).getBitmap();
    } else if (!(drawable instanceof Animatable)) {
      result = drawToBitmap(bitmapPool, drawable, width, height);
      // We created and drew to the Bitmap, so it's safe for us to recycle or re-use.
      isRecycleable = true;
    }

    BitmapPool toUse = isRecycleable ? bitmapPool : NO_RECYCLE_BITMAP_POOL;
    return BitmapResource.obtain(result, toUse);
  }

  @Nullable
  private static Bitmap drawToBitmap(
      BitmapPool bitmapPool, Drawable drawable, int width, int height) {
    if (width == Target.SIZE_ORIGINAL && drawable.getIntrinsicWidth() <= 0) {
      if (Log.isLoggable(TAG, Log.WARN)) {
        Log.w(TAG, "Unable to draw " + drawable + " to Bitmap with Target.SIZE_ORIGINAL because the"
            + " Drawable has no intrinsic width");
      }
      return null;
    }
    if (height == Target.SIZE_ORIGINAL && drawable.getIntrinsicHeight() <= 0) {
      if (Log.isLoggable(TAG, Log.WARN)) {
        Log.w(TAG, "Unable to draw " + drawable + " to Bitmap with Target.SIZE_ORIGINAL because the"
            + " Drawable has no intrinsic height");
      }
      return null;
    }
    int targetWidth = drawable.getIntrinsicWidth() > 0 ? drawable.getIntrinsicWidth() : width;
    int targetHeight = drawable.getIntrinsicHeight() > 0 ? drawable.getIntrinsicHeight() : height;

    Lock lock = TransformationUtils.getBitmapDrawableLock();
    lock.lock();
    Bitmap result = bitmapPool.get(targetWidth, targetHeight, Bitmap.Config.ARGB_8888);
    try {
      Canvas canvas = new Canvas(result);
      drawable.setBounds(0, 0, targetWidth, targetHeight);
      drawable.draw(canvas);
      canvas.setBitmap(null);
    } finally {
      lock.unlock();
    }
    return result;
  }
}
