/*
 * Copyright (C) 2017 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.documentsui;

import static android.content.ContentResolver.wrap;

import static com.android.documentsui.base.SharedMinimal.VERBOSE;

import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.CancellationSignal;
import android.os.FileUtils;
import android.os.OperationCanceledException;
import android.provider.DocumentsContract;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;

import com.android.documentsui.ProviderExecutor.Preemptable;
import com.android.documentsui.base.UserId;

import java.util.function.BiConsumer;
import java.util.function.Consumer;

/**
 *  Loads a Thumbnails asynchronously then animates from the mime icon to the thumbnail
 */
public final class ThumbnailLoader extends AsyncTask<Uri, Void, Bitmap> implements Preemptable {

    private static final String TAG = ThumbnailLoader.class.getCanonicalName();

    /**
     * Two animations applied to image views. The first is used to switch mime icon and thumbnail.
     * The second is used when we need to update thumbnail.
     */
    public static final BiConsumer<View, View> ANIM_FADE_IN = (mime, thumb) -> {
        float alpha = mime.getAlpha();
        mime.animate().alpha(0f).start();
        thumb.setAlpha(0f);
        thumb.animate().alpha(alpha).start();
    };
    public static final BiConsumer<View, View> ANIM_NO_OP = (mime, thumb) -> {};

    private final ImageView mIconThumb;
    private final Point mThumbSize;
    private final Uri mUri;
    private final UserId mUserId;
    private final long mLastModified;
    private final Consumer<Bitmap> mCallback;
    private final boolean mAddToCache;
    private final CancellationSignal mSignal;

    /**
     * @param uri - to a thumbnail.
     * @param userId - user of the uri.
     * @param iconThumb - ImageView to display the thumbnail.
     * @param thumbSize - size of the thumbnail.
     * @param lastModified - used for updating thumbnail caches.
     * @param addToCache - flag that determines if the loader saves the thumbnail to the cache.
     */
    public ThumbnailLoader(Uri uri, UserId userId, ImageView iconThumb, Point thumbSize,
            long lastModified, Consumer<Bitmap> callback, boolean addToCache) {

        mUri = uri;
        mUserId = userId;
        mIconThumb = iconThumb;
        mThumbSize = thumbSize;
        mLastModified = lastModified;
        mCallback = callback;
        mAddToCache = addToCache;
        mSignal = new CancellationSignal();
        mIconThumb.setTag(this);

        if (VERBOSE) Log.v(TAG, "Starting icon loader task for " + mUri);
    }

    @Override
    public void preempt() {
        if (VERBOSE) Log.v(TAG, "Icon loader task for " + mUri + " was cancelled.");
        cancel(false);
        mSignal.cancel();
    }

    @Override
    protected Bitmap doInBackground(Uri... params) {
        if (isCancelled()) {
            return null;
        }

        final Context context = mIconThumb.getContext();
        final ContentResolver resolver = mUserId.getContentResolver(context);

        ContentProviderClient client = null;
        Bitmap result = null;
        try {
            client = DocumentsApplication.acquireUnstableProviderOrThrow(
                resolver, mUri.getAuthority());
            result = DocumentsContract.getDocumentThumbnail(wrap(client),
                    mUri, mThumbSize, mSignal);
            if (result != null && mAddToCache) {
                final ThumbnailCache cache = DocumentsApplication.getThumbnailCache(context);
                cache.putThumbnail(mUri, mUserId, mThumbSize, result, mLastModified);
            }
        } catch (Exception e) {
            if (!(e instanceof OperationCanceledException)) {
                Log.w(TAG, "Failed to load thumbnail for " + mUri + ": " + e);
            }
        } finally {
            FileUtils.closeQuietly(client);
        }
        return result;
    }

    @Override
    protected void onPostExecute(Bitmap result) {
        if (VERBOSE) Log.v(TAG, "Loader task for " + mUri + " completed");

        if (mIconThumb.getTag() == this) {
            mIconThumb.setTag(null);
            mCallback.accept(result);
        }
    }
}