/*
 * Copyright 2015 Yan Zhenjie
 *
 * 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.yolanda.nohttp.tools;

import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.view.ViewGroup.LayoutParams;
import android.widget.ImageView;

import com.yolanda.nohttp.Logger;
import com.yolanda.nohttp.NoHttp;
import com.yolanda.nohttp.PosterHandler;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created in Nov 4, 2015 3:07:29 PM.
 *
 * @author Yan Zhenjie.
 */
public class ImageLocalLoader {
    /**
     * Single module.
     */
    private static ImageLocalLoader mInstance;
    /**
     * Default gray image.
     */
    private Drawable mDefaultDrawable;
    /**
     * Image cache.
     */
    private LruCache<String, Bitmap> mLruCache;
    /**
     * Thread pool.
     */
    private ExecutorService mExecutorService;

    /**
     * Get single object.
     *
     * @return {@link ImageLocalLoader}.
     */
    public static ImageLocalLoader getInstance() {
        synchronized (ImageLocalLoader.class) {
            if (mInstance == null) {
                mInstance = new ImageLocalLoader();
            }
        }
        return mInstance;
    }

    private ImageLocalLoader() {
        mDefaultDrawable = new ColorDrawable(Color.GRAY);
        mExecutorService = Executors.newSingleThreadExecutor();

        int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 8);
        mLruCache = new LruCache<String, Bitmap>(maxMemory) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getRowBytes() * value.getHeight();
            }
        };
    }

    /**
     * Deposit in the province read images, width is high, the greater the picture clearer, but also the memory.
     *
     * @param imagePath pictures in the path of the memory card.
     * @param maxWidth  the highest limit value target width.
     * @param maxHeight the highest limit value target height.
     * @return Bitmap
     */
    public Bitmap readImage(String imagePath, int maxWidth, int maxHeight) {
        File imageFile = new File(imagePath);
        if (imageFile.exists()) {
            BufferedInputStream inputStream = null;
            try {
                inputStream = new BufferedInputStream(new FileInputStream(imageFile));
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inJustDecodeBounds = true;
                BitmapFactory.decodeStream(inputStream, null, options);
                inputStream.close();
                int i = 0;
                while (true) {
                    if ((options.outWidth >> i <= maxWidth) && (options.outHeight >> i <= maxHeight)) {
                        inputStream = new BufferedInputStream(new FileInputStream(new File(imagePath)));
                        options.inSampleSize = (int) Math.pow(2.0, i);
                        options.inJustDecodeBounds = false;
                        Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, options);
                        inputStream.close();
                        return bitmap;
                    }
                    i += 1;
                }
            } catch (IOException e) {
                Logger.e(e, "This path does not exist" + imagePath + ".");
            } finally {
                IOUtils.closeQuietly(inputStream);
            }
        }
        return null;
    }

    /**
     * According to the ImageView obtains appropriate width and height of compression.
     *
     * @param imageView {@link ImageView}.
     * @param viewSizes ViewSize.
     */
    public void measureSize(ImageView imageView, int[] viewSizes) {
        final DisplayMetrics displayMetrics = NoHttp.getContext().getResources().getDisplayMetrics();
        final LayoutParams params = imageView.getLayoutParams();
        if (params == null) {
            viewSizes[0] = displayMetrics.widthPixels;
            viewSizes[1] = displayMetrics.heightPixels;
        } else {
            viewSizes[0] = params.width == LayoutParams.WRAP_CONTENT ? 0 : imageView.getWidth(); // Get actual image width
            viewSizes[1] = params.height == LayoutParams.WRAP_CONTENT ? 0 : imageView.getHeight(); // Get actual image height

            if (viewSizes[0] <= 0)
                viewSizes[0] = displayMetrics.widthPixels; // Get layout width parameter
            if (viewSizes[1] <= 0)
                viewSizes[1] = displayMetrics.heightPixels; // Get layout height parameter
        }
    }

    /**
     * Set the default image, resId from drawable. Is displayed when loading or loading failure.
     *
     * @param resId res id.
     */
    @SuppressLint("NewApi")
    public void setDefaultImage(int resId) {
        mDefaultDrawable = ResCompat.getDrawable(resId);
    }

    /**
     * Set the default image, resId from drawable. Is displayed when loading or loading failure.
     *
     * @param color color.
     */
    public void setDefaultImageColor(int color) {
        mDefaultDrawable = new ColorDrawable(color);
    }

    /**
     * Load image from local SDCard.
     *
     * @param imageView {@link ImageView}.
     * @param imagePath path.
     */
    public void loadImage(ImageView imageView, String imagePath) {
        loadImage(imageView, imagePath, 0, 0, null);
    }

    /**
     * Load image from local SDCard.
     *
     * @param imageView         {@link ImageView}.
     * @param imagePath         path.
     * @param imageLoadListener {@link ImageLoadListener}.
     */
    public void loadImage(ImageView imageView, String imagePath, ImageLoadListener imageLoadListener) {
        loadImage(imageView, imagePath, 0, 0, imageLoadListener);
    }

    /**
     * Load image from local SDCard.
     *
     * @param imageView {@link ImageView}.
     * @param imagePath path.
     * @param width     width.
     * @param height    height.
     */
    public void loadImage(ImageView imageView, String imagePath, int width, int height) {
        loadImage(imageView, imagePath, width, height, null);
    }

    /**
     * According to the specified width high loading pictures, wide high, the greater the picture clearer, more memory.
     *
     * @param imageView         {@link ImageView}.
     * @param imagePath         path from local SDCard.
     * @param width             target width.
     * @param height            target height.
     * @param imageLoadListener {@link ImageLoadListener}.
     */
    public void loadImage(ImageView imageView, String imagePath, int width, int height, ImageLoadListener imageLoadListener) {
        if (imageLoadListener == null)
            imageView.setTag(imagePath);
        Bitmap bitmap = getImageFromCache(imagePath + width + height);
        if (bitmap == null) {
            imageView.setImageDrawable(mDefaultDrawable);
            mExecutorService.execute(new TaskThread(imageView, imagePath, width, height, imageLoadListener));
        } else {
            ImgBeanHolder holder = new ImgBeanHolder();
            holder.imageView = imageView;
            holder.imagePath = imagePath;
            holder.bitmap = bitmap;
            holder.imageLoadListener = imageLoadListener;
            PosterHandler.getInstance().post(holder);
        }
    }

    private Bitmap getImageFromCache(String key) {
        return mLruCache.get(key);
    }

    private void addImageToCache(String key, Bitmap bitmap) {
        if (getImageFromCache(key) == null && bitmap != null)
            mLruCache.put(key, bitmap);
    }

    private class TaskThread implements Runnable {
        private ImageView mImageView;
        private String mImagePath;
        private int width;
        private int height;
        private ImageLoadListener imageLoadListener;

        TaskThread(ImageView imageView, String imagePath, int width, int height, ImageLoadListener imageLoadListener) {
            this.mImagePath = imagePath;
            this.mImageView = imageView;
            this.width = width;
            this.height = height;
            this.imageLoadListener = imageLoadListener;
        }

        @Override
        public void run() {
            if (TextUtils.isEmpty(mImagePath))
                Logger.e("The image path is null");
            else {
                Bitmap bitmap;
                if (width != 0 && height != 0)
                    bitmap = readImage(mImagePath, width, height);
                else {
                    int[] viewSizes = new int[2];
                    measureSize(mImageView, viewSizes);
                    bitmap = readImage(mImagePath, viewSizes[0], viewSizes[1]);
                }
                addImageToCache(mImagePath + width + height, bitmap);
                ImgBeanHolder holder = new ImgBeanHolder();
                holder.bitmap = getImageFromCache(mImagePath + width + height);
                holder.imageView = mImageView;
                holder.imagePath = mImagePath;
                holder.imageLoadListener = imageLoadListener;
                PosterHandler.getInstance().post(holder);
            }
        }
    }

    private class ImgBeanHolder implements Runnable {
        Bitmap bitmap;
        ImageView imageView;
        String imagePath;
        ImageLoadListener imageLoadListener;

        @Override
        public void run() {
            if (imagePath.equals(imageView.getTag())) {
                if (bitmap == null)
                    imageView.setImageDrawable(mDefaultDrawable);
                else
                    imageView.setImageBitmap(bitmap);
            }
            if (imageLoadListener != null) {
                if (bitmap != null)
                    imageLoadListener.onLoadSucceed(imageView, bitmap, imagePath);
                else
                    imageLoadListener.onLoadFailed(imageView, imagePath);
            }
        }
    }

    public interface ImageLoadListener {
        void onLoadSucceed(ImageView imageView, Bitmap bitmap, String imagePath);

        void onLoadFailed(ImageView imageView, String imagePath);
    }
}
