content: /*
   * Copyright 2013 Chris Banes
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package com.zhaojian.jolly.views;
  
  import java.lang.ref.WeakReference;
  import java.util.concurrent.Future;
  
  import uk.co.senab.bitmapcache.BitmapLruCache;
  import uk.co.senab.bitmapcache.CacheableBitmapWrapper;
  import uk.co.senab.bitmapcache.CacheableImageView;
  import android.content.Context;
  import android.graphics.Bitmap;
  import android.graphics.Color;
  import android.graphics.drawable.BitmapDrawable;
  import android.graphics.drawable.ColorDrawable;
  import android.graphics.drawable.Drawable;
  import android.graphics.drawable.TransitionDrawable;
  import android.util.AttributeSet;
  
  import com.zhaojian.jolly.model.PhotoUpload;
  import com.zhaojian.jolly.selectphotos.SelectPhotoApplication;
  import com.zhaojian.jolly.tasks.PhotupThreadRunnable;
  
  public class PhotupImageView extends CacheableImageView {
  
      public static interface OnPhotoLoadListener {
  
          void onPhotoLoadFinished(Bitmap bitmap);
      }
  
      static final class PhotoFilterRunnable extends PhotupThreadRunnable {
  
          private final WeakReference<PhotupImageView> mImageView;
          private final PhotoUpload mUpload;
          private final boolean mFullSize;
          private final BitmapLruCache mCache;
          private final OnPhotoLoadListener mListener;
  
          public PhotoFilterRunnable(PhotupImageView imageView, PhotoUpload upload,
                  BitmapLruCache cache,
                  final boolean fullSize, final OnPhotoLoadListener listener) {
              mImageView = new WeakReference<PhotupImageView>(imageView);
              mUpload = upload;
              mFullSize = fullSize;
              mCache = cache;
              mListener = listener;
          }
  
          public void runImpl() {
              final PhotupImageView imageView = mImageView.get();
              if (null == imageView) {
                  return;
              }
  
              final Context context = imageView.getContext();
              final Bitmap filteredBitmap;
  
              final String key = mFullSize ? mUpload.getDisplayImageKey()
                      : mUpload.getThumbnailImageKey();
              CacheableBitmapWrapper wrapper = mCache.get(key);
  
              if (null == wrapper || !wrapper.hasValidBitmap()) {
                  Bitmap bitmap = mFullSize ? mUpload.getDisplayImage(context)
                          : mUpload.getThumbnailImage(context);
                  wrapper = new CacheableBitmapWrapper(key, bitmap);
                  wrapper.setBeingUsed(true);
                  mCache.put(wrapper);
              } else {
                  wrapper.setBeingUsed(true);
              }
  
              // Don't process if we've been interrupted
              if (!isInterrupted()) {
                  filteredBitmap = mUpload.processBitmap(wrapper.getBitmap(), mFullSize, false);
              } else {
                  filteredBitmap = null;
              }
  
              // Make sure we release the original bitmap
              wrapper.setBeingUsed(false);
  
              // If we haven't been interrupted, update the view
              if (!isInterrupted()) {
  
                  imageView.post(new Runnable() {
                      public void run() {
                          imageView.setImageBitmap(filteredBitmap);
  
                          if (null != mListener) {
                              mListener.onPhotoLoadFinished(filteredBitmap);
                          }
                      }
                  });
              }
          }
      }
  
      static final class PhotoLoadRunnable extends PhotupThreadRunnable {
  
          private final WeakReference<PhotupImageView> mImageView;
          private final PhotoUpload mUpload;
          private final boolean mFullSize;
          private final BitmapLruCache mCache;
          private final OnPhotoLoadListener mListener;
  
          public PhotoLoadRunnable(PhotupImageView imageView, PhotoUpload upload,
                  BitmapLruCache cache,
                  final boolean fullSize, final OnPhotoLoadListener listener) {
              mImageView = new WeakReference<PhotupImageView>(imageView);
              mUpload = upload;
              mFullSize = fullSize;
              mCache = cache;
              mListener = listener;
          }
  
          public void runImpl() {
              final PhotupImageView imageView = mImageView.get();
              if (null == imageView) {
                  return;
              }
  
              final Context context = imageView.getContext();
              final CacheableBitmapWrapper wrapper;
  
              final Bitmap bitmap = mFullSize ? mUpload.getDisplayImage(context)
                      : mUpload.getThumbnailImage(context);
  
              if (null != bitmap) {
                  final String key = mFullSize ? mUpload.getDisplayImageKey()
                          : mUpload.getThumbnailImageKey();
                  wrapper = new CacheableBitmapWrapper(key, bitmap);
              } else {
                  wrapper = null;
              }
  
              // If we're interrupted, just update the cache and return
              if (isInterrupted()) {
                  mCache.put(wrapper);
                  return;
              }
  
              // If we're still running, update the Views
              if (null != wrapper) {
                  imageView.post(new Runnable() {
                      public void run() {
                          imageView.setImageCachedBitmap(wrapper);
                          mCache.put(wrapper);
  
                          if (null != mListener) {
                              mListener.onPhotoLoadFinished(wrapper.getBitmap());
                          }
                      }
                  });
              }
          }
      }
  
      static final int FACE_DETECTION_DELAY = 800;
  
      private boolean mFadeInDrawables = false;
      private Drawable mFadeFromDrawable;
      private int mFadeDuration;
  
      private Runnable mRequestFaceDetectionRunnable;
  
      private Future<?> mCurrentRunnable;
  
      public PhotupImageView(Context context) {
          super(context);
      }
  
      public PhotupImageView(Context context, AttributeSet attrs) {
          super(context, attrs);
      }
  
      public void cancelRequest() {
          if (null != mCurrentRunnable) {
              mCurrentRunnable.cancel(true);
              mCurrentRunnable = null;
          }
      }
  
      public void clearFaceDetection() {
          if (null != mRequestFaceDetectionRunnable) {
              removeCallbacks(mRequestFaceDetectionRunnable);
              mRequestFaceDetectionRunnable = null;
          }
      }
  
      public Bitmap getCurrentBitmap() {
          Drawable d = getDrawable();
          if (d instanceof BitmapDrawable) {
              return ((BitmapDrawable) d).getBitmap();
          }
  
          return null;
      }
  
      public void recycleBitmap() {
          Bitmap currentBitmap = getCurrentBitmap();
          if (null != currentBitmap) {
              setImageDrawable(null);
              currentBitmap.recycle();
          }
      }
  
      public void requestFullSize(final PhotoUpload upload, final boolean honourFilter,
              final boolean clearDrawableOnLoad, final OnPhotoLoadListener listener) {
          resetForRequest(clearDrawableOnLoad);
  
          if (upload.requiresProcessing(true) && honourFilter) {
              requestFiltered(upload, true, listener);
          } else {
              // Show thumbnail if it's in the cache
              BitmapLruCache cache = SelectPhotoApplication.getInstanse().getImageCache();
              CacheableBitmapWrapper thumbWrapper = cache.get(upload.getThumbnailImageKey());
              if (null != thumbWrapper && thumbWrapper.hasValidBitmap()) {
                  setImageCachedBitmap(thumbWrapper);
              } else {
                  setImageDrawable(null);
              }
  
              requestImage(upload, true, listener);
          }
      }
      
      private void requestFiltered(final PhotoUpload upload, boolean fullSize,
              final OnPhotoLoadListener listener) {
          mCurrentRunnable = SelectPhotoApplication.getInstanse().getPhotoFilterThreadExecutorService().submit(
                  new PhotoFilterRunnable(this, upload, SelectPhotoApplication.getInstanse().getImageCache(), fullSize, listener));
      }
  
      public void requestFullSize(final PhotoUpload upload, final boolean honourFilter,
              final OnPhotoLoadListener listener) {
          requestFullSize(upload, honourFilter, true, listener);
      }
  
      public void requestThumbnail(final PhotoUpload upload, final boolean honourFilter) {
          resetForRequest(true);
  
          if (upload.requiresProcessing(false) && honourFilter) {
              requestFiltered(upload, false, null);
          } else {
              requestImage(upload, false, null);
          }
      }
  
      public void setFadeInDrawables(boolean fadeIn) {
          mFadeInDrawables = fadeIn;
  
          if (fadeIn && null == mFadeFromDrawable) {
              mFadeFromDrawable = new ColorDrawable(Color.TRANSPARENT);
              mFadeDuration = getResources().getInteger(android.R.integer.config_shortAnimTime);
          }
      }
  
      @Override
      public void setImageDrawable(Drawable drawable) {
          if (mFadeInDrawables && null != drawable) {
              TransitionDrawable newDrawable = new TransitionDrawable(
                      new Drawable[]{mFadeFromDrawable, drawable});
              super.setImageDrawable(newDrawable);
              newDrawable.startTransition(mFadeDuration);
          } else {
              super.setImageDrawable(drawable);
          }
      }
  
      private void requestImage(final PhotoUpload upload, final boolean fullSize,
              final OnPhotoLoadListener listener) {
          final String key = fullSize ? upload.getDisplayImageKey() : upload.getThumbnailImageKey();
  
          BitmapLruCache cache = SelectPhotoApplication.getInstanse().getImageCache();
          final CacheableBitmapWrapper cached = cache.get(key);
  
          if (null != cached && cached.hasValidBitmap()) {
              setImageCachedBitmap(cached);
              if (null != listener) {
                  listener.onPhotoLoadFinished(cached.getBitmap());
              }
          } else {
              // Means we have an object with an invalid bitmap so remove it
              if (null != cached) {
                  cache.remove(key);
              }
              mCurrentRunnable = SelectPhotoApplication.getInstanse().getMultiThreadExecutorService().submit(
                      new PhotoLoadRunnable(this, upload, cache, fullSize, listener));
          }
      }
  
      private void resetForRequest(final boolean clearDrawable) {
          cancelRequest();
  
          // Clear currently display bitmap
          if (clearDrawable) {
              setImageDrawable(null);
          }
      }
  
  }
  