/*
 * Copyright (C) 2012 Google Inc.
 * Licensed to The Android Open Source Project.
 *
 * 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.android.mail.ui;

import android.content.ContentResolver;
import android.content.res.AssetFileDescriptor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.net.Uri;
import android.os.AsyncTask;
import android.util.DisplayMetrics;

import com.android.ex.photo.util.Exif;
import com.android.ex.photo.util.ImageUtils;

import com.android.mail.providers.Attachment;
import com.android.mail.utils.LogTag;
import com.android.mail.utils.LogUtils;

import java.io.IOException;
import java.io.InputStream;

/**
 * Performs the load of a thumbnail bitmap in a background
 * {@link AsyncTask}. Available for use with any view that implements
 * the {@link AttachmentBitmapHolder} interface.
 */
public class ThumbnailLoadTask extends AsyncTask<Uri, Void, Bitmap> {
    private static final String LOG_TAG = LogTag.getLogTag();

    private final AttachmentBitmapHolder mHolder;
    private final int mWidth;
    private final int mHeight;

    public static void setupThumbnailPreview(AttachmentTile.AttachmentPreviewCache cache,
            AttachmentBitmapHolder holder, Attachment attachment, Attachment prevAttachment) {
        // Check cache first
        if (cache != null) {
            final Bitmap cached = cache.get(attachment);
            if (cached != null) {
                holder.setThumbnail(cached);
                return;
            }
        }

        final int width = holder.getThumbnailWidth();
        final int height = holder.getThumbnailHeight();
        if (attachment == null || width == 0 || height == 0
                || !ImageUtils.isImageMimeType(attachment.getContentType())) {
            holder.setThumbnailToDefault();
            return;
        }

        final Uri thumbnailUri = attachment.thumbnailUri;
        final Uri contentUri = attachment.contentUri;
        final Uri uri = attachment.getIdentifierUri();
        final Uri prevUri = (prevAttachment == null) ? null : prevAttachment.getIdentifierUri();
        // begin loading a thumbnail if this is an image and either the thumbnail or the original
        // content is ready (and different from any existing image)
        if ((thumbnailUri != null || contentUri != null)
                && (holder.bitmapSetToDefault() ||
                prevUri == null || !uri.equals(prevUri))) {
            final ThumbnailLoadTask task = new ThumbnailLoadTask(
                    holder, width, height);
            task.execute(thumbnailUri, contentUri);
        } else if (thumbnailUri == null && contentUri == null) {
            // not an image, or no thumbnail exists. fall back to default.
            // async image load must separately ensure the default appears upon load failure.
            holder.setThumbnailToDefault();
        }
    }

    public ThumbnailLoadTask(AttachmentBitmapHolder holder, int width, int height) {
        mHolder = holder;
        mWidth = width;
        mHeight = height;
    }

    @Override
    protected Bitmap doInBackground(Uri... params) {
        Bitmap result = loadBitmap(params[0]);
        if (result == null) {
            result = loadBitmap(params[1]);
        }

        return result;
    }

    private Bitmap loadBitmap(final Uri thumbnailUri) {
        if (thumbnailUri == null) {
            LogUtils.e(LOG_TAG, "Attempting to load bitmap for null uri");
            return null;
        }

        final int orientation = getOrientation(thumbnailUri);

        AssetFileDescriptor fd = null;
        try {
            fd = mHolder.getResolver().openAssetFileDescriptor(thumbnailUri, "r");
            if (isCancelled() || fd == null) {
                return null;
            }

            final BitmapFactory.Options opts = new BitmapFactory.Options();
            opts.inJustDecodeBounds = true;
            opts.inDensity = DisplayMetrics.DENSITY_LOW;

            BitmapFactory.decodeFileDescriptor(fd.getFileDescriptor(), null, opts);
            if (isCancelled() || opts.outWidth == -1 || opts.outHeight == -1) {
                return null;
            }

            opts.inJustDecodeBounds = false;
            // Shrink both X and Y (but do not over-shrink)
            // and pick the least affected dimension to ensure the thumbnail is fillable
            // (i.e. ScaleType.CENTER_CROP)
            final int wDivider = Math.max(opts.outWidth / mWidth, 1);
            final int hDivider = Math.max(opts.outHeight / mHeight, 1);
            opts.inSampleSize = Math.min(wDivider, hDivider);

            LogUtils.d(LOG_TAG, "in background, src w/h=%d/%d dst w/h=%d/%d, divider=%d",
                    opts.outWidth, opts.outHeight, mWidth, mHeight, opts.inSampleSize);

            final Bitmap originalBitmap = BitmapFactory.decodeFileDescriptor(
                    fd.getFileDescriptor(), null, opts);
            if (originalBitmap != null && orientation != 0) {
                final Matrix matrix = new Matrix();
                matrix.postRotate(orientation);
                return Bitmap.createBitmap(originalBitmap, 0, 0, originalBitmap.getWidth(),
                        originalBitmap.getHeight(), matrix, true);
            }
            return originalBitmap;
        } catch (Throwable t) {
            LogUtils.i(LOG_TAG, "Unable to decode thumbnail %s: %s %s", thumbnailUri,
                    t.getClass(), t.getMessage());
        } finally {
            if (fd != null) {
                try {
                    fd.close();
                } catch (IOException e) {
                    LogUtils.e(LOG_TAG, e, "");
                }
            }
        }

        return null;
    }

    private int getOrientation(final Uri thumbnailUri) {
        if (thumbnailUri == null) {
            return 0;
        }

        InputStream in = null;
        try {
            final ContentResolver resolver = mHolder.getResolver();
            in = resolver.openInputStream(thumbnailUri);
            return Exif.getOrientation(in, -1);
        } catch (Throwable t) {
            LogUtils.i(LOG_TAG, "Unable to get orientation of thumbnail %s: %s %s", thumbnailUri,
                    t.getClass(), t.getMessage());
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    LogUtils.e(LOG_TAG, e, "error attemtping to close input stream");
                }
            }
        }

        return 0;
    }

    @Override
    protected void onPostExecute(Bitmap result) {
        if (result == null) {
            LogUtils.d(LOG_TAG, "back in UI thread, decode failed or file does not exist");
            mHolder.thumbnailLoadFailed();
            return;
        }

        LogUtils.d(LOG_TAG, "back in UI thread, decode success, w/h=%d/%d", result.getWidth(),
                result.getHeight());
        mHolder.setThumbnail(result);
    }

}
