package com.droid.cache.loader;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.TransitionDrawable;
import android.os.AsyncTask;
import android.text.TextUtils;
import android.util.SparseArray;
import android.widget.ImageView;
import com.droid.cache.ImageCache;
import com.droid.cache.util.LogUtil;

import java.lang.ref.WeakReference;

/**
 * 图片加载器.<P>
 * 图片的加载过程：1.从自身的图片缓存中取;2.如果取不到,则从网络中获取.<P>
 * 同时,我们可以停止后台的图片获取线程.
 *@Title:
 *@Description:
 */
public abstract class ImageWorker
{
    /** 日志TAG. */
    private static final String TAG = "ImageWorker";
    /** 图片加载效果时间<淡入时间>. */
    private static final int FADE_IN_TIME = 200;
    /** 图片缓存. */
    private ImageCache mImageCache;
    /** 是否将要推出加载<如:应用程序结束的时候,加载线程就应该停止>. */
    private boolean mExitTasksEarly = false;
    /** 上下文Context. */
    protected Context mContext;
    /** 正在加载时候的图片. */
    private SparseArray<Bitmap> loadingImageMap;

    /**
     * 构造方法.
     * @param context context
     */
    protected ImageWorker(Context context)
    {
        mContext = context;
        loadingImageMap = new SparseArray<Bitmap>();
    }

    /**
     * 根据url加载一个图片到ImageView上,未加载成功的时候显示加载图片<loadingImageId>
     *
     * @param url 图片网络地址.
     * @param imageView 图片View
     * @param loadingImageId 加载图片资源ID
     * @Description:
     */
    public void loadImage(String url, ImageView imageView, int loadingImageId)
    {
        //如果图片网络地址为空,直接返回.
        if(TextUtils.isEmpty(url))
        {
            return;
        }
        Bitmap bitmap = null;
        if (mImageCache != null)
        {
            //先从内存缓存中取.
            bitmap = mImageCache.getBitmapFromMemCache(url);
        }
        if (bitmap != null)
        {
            //如果取到了,直接显示.
            imageView.setImageBitmap(bitmap);
        }
        else if (cancelPotentialWork(url, imageView))
        {
            //如果没有取到,取消之前相同的加载线程,取消成功,就启动一个新的加载线程.
            final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
            final AsyncDrawable asyncDrawable =
                    new AsyncDrawable(mContext.getResources(), getLoadingImage(loadingImageId), task);
            imageView.setImageDrawable(asyncDrawable);
            task.execute(url);
        }
    }

    /**
     * 根据资源ID获取加载中的图片.
     * @param resId 资源ID.
     * @Description:
     */
    private Bitmap getLoadingImage(int resId)
    {
        //先从所有加载中的图片列表中取,如果取到,直接返回
        Bitmap loadingBitmap = null;
        loadingBitmap = loadingImageMap.get(resId);
        //如果没有取到
        if (loadingBitmap == null)
        {
            //从资源文件中获取,并且放到列表中,供后期提取.
            loadingBitmap = BitmapFactory.decodeResource(mContext.getResources(), resId);
            loadingImageMap.put(resId, loadingBitmap);
        }
        return loadingBitmap;
    }


    /**
     * 设置图片缓存.
     * @param imageCache 图片缓存.
     */
    public void setImageCache(ImageCache imageCache)
    {
        mImageCache = imageCache;
    }

    /**
     * 获得图片缓存.
     * @return imageCache
     * @Description:
     */
    public ImageCache getImageCache()
    {
        return mImageCache;
    }

    /**
     * 设置是否停止加载.
     */
    public void setExitTasksEarly(boolean exitTasksEarly)
    {
        mExitTasksEarly = exitTasksEarly;
    }

    /**
     * 根据url得到bitmap图片.
     */
    protected abstract Bitmap processBitmap(String url);

    /**
     * 取消一个ImageView的加载线程.
     * @param imageView 被取消加载线程的ImageView.
     * @Description:
     */
    public static void cancelWork(ImageView imageView)
    {
        final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
        if (bitmapWorkerTask != null)
        {
            bitmapWorkerTask.cancel(true);
            LogUtil.d(TAG, "cancel load : " + bitmapWorkerTask.url);
        }
    }

    /**
     * 取消指定url的图片加载线程.
     * @param url 图片网络地址
     * @param imageView 图片View
     * @return 如果有相同的线程在进行,返回false,否则返回 true.
     */
    public static boolean cancelPotentialWork(String url, ImageView imageView)
    {
        //从ImageView中获取加载线程.
        final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
        if (bitmapWorkerTask != null)
        {
            final String taskUrl = bitmapWorkerTask.url;
            if (TextUtils.isEmpty(taskUrl) || !taskUrl.equals(url))
            {
                //如果加载的url为空,获得加载的url不等指定的url,则取消当前的加载线程.
                bitmapWorkerTask.cancel(true);
                LogUtil.d(TAG, "cancel load : " + taskUrl);
            }
            else
            {
                // 如果加载的url等于指定的url,则说明不需要重新加载,返回false.
                return false;
            }
        }
        return true;
    }

    /**
     * 从ImageView获得加载线程.
     * @param imageView 图片View.
     */
    private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView)
    {
        //如果IamgeView不为空.
        if (imageView != null)
        {
            final Drawable drawable = imageView.getDrawable();
            if (drawable instanceof AsyncDrawable)
            {
                //获得ImageView的drawable,如果是AsyncDrawable的实例,返回加载线程.
                final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;

                return asyncDrawable.getBitmapWorkerTask();
            }
        }
        return null;
    }

    /**
     * 图片加载线程.
     */
    private class BitmapWorkerTask extends AsyncTask<String, String, Bitmap>
    {
        /** 图片网络地址. */
        private String url;
        /** 弱引用显示图片的ImageView. */
        private final WeakReference<ImageView> imageViewReference;

        /**
         * 构造方法
         * @param imageView 显示图片的ImageView
         */
        public BitmapWorkerTask(ImageView imageView)
        {
            //进行弱引用
            imageViewReference = new WeakReference<ImageView>(imageView);
        }

        @Override
        protected Bitmap doInBackground(String... params)
        {
            url = params[0];
            Bitmap bitmap = null;

            // 1.如果图片缓存不为null.
            // 2.如果当前线程没有被取消.
            // 3.如果被弱引用的ImageView没有被销毁.
            // 4.如果加载程序无须退出.
            if (mImageCache != null && !isCancelled() && getAttachedImageView() != null && !mExitTasksEarly)
            {
                //从硬盘缓存中去.
                bitmap = mImageCache.getBitmapFromDiskCache(url);
            }

            // 1.如果从硬盘缓存中获取的图片为Null.
            // 2.如果当前线程没有被取消.
            // 3.如果被弱引用的ImageView没有被销毁.
            // 4.如果加载程序无须退出.
            if (bitmap == null && !isCancelled() && getAttachedImageView() != null && !mExitTasksEarly)
            {
                //后台获取图片<网络获取>
                bitmap = processBitmap(params[0]);
            }

            // 1.如果加载过后的图片不为null.
            // 2.如果图片缓存不为null.
            if (bitmap != null && mImageCache != null)
            {
                //添加到缓存中.
                mImageCache.addBitmapToCache(url, bitmap);
            }
            return bitmap;
        }

        @Override
        protected void onPostExecute(Bitmap result)
        {
            // 1.如果当前线程<被取消>.
            // 2.如果加载程序<被命令退出>.
            if (isCancelled() || mExitTasksEarly)
            {
                result = null;
            }
            final ImageView imageView = getAttachedImageView();
            if (result != null && imageView != null)
            {
                //如果弱引用的ImageView还在,并且加载的图片不为空,则显示图片.
                setImageBitmap(imageView, result);
            }
        };

        /**
         * 获得弱引用的ImageView
         */
        private ImageView getAttachedImageView()
        {
            final ImageView imageView = imageViewReference.get();
            final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
            //获得的ImageView中的加载线程 为 当前线程
            if (this == bitmapWorkerTask)
            {
                //则返回这个IamgeView.
                return imageView;
            }
            return null;
        }
    }

    /**
     * 弱引用加载线程的BitmapDrawable
     *@Title:
     */
    private class AsyncDrawable extends BitmapDrawable
    {
        /** 弱引用加载线程. */
        private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;

        /**
         * 构造方法.
         * @param res 系统资源
         * @param bitmap 加载过程中的图片.
         * @param bitmapWorkerTask 图片加载线程.
         */
        public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask)
        {
            super(res, bitmap);
            //弱引用加载线程.
            bitmapWorkerTaskReference = new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
        }

        /**
         * 返回弱引用的图片加载线程.
         */
        public BitmapWorkerTask getBitmapWorkerTask()
        {
            return bitmapWorkerTaskReference.get();
        }
    }

    /**
     * 将图片设置到ImageView中.
     */
    private void setImageBitmap(ImageView imageView, Bitmap bitmap)
    {
        //当图片加载好的时候,设置一个跳转样式,从一个<透明图片>跳至<加载好的图片>,用时<FADE_IN_TIME><200ms>
        final Drawable[] layers = new Drawable[] {
                new ColorDrawable(android.R.color.transparent),
                new BitmapDrawable(mContext.getResources(), bitmap) };

        final TransitionDrawable transDrawable = new TransitionDrawable(layers);

        imageView.setImageDrawable(transDrawable);
        //开始展示.
        transDrawable.startTransition(FADE_IN_TIME);
    }
}
