// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.browser.feedback;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Rect;

import org.chromium.base.ThreadUtils;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.ui.UiUtils;
import org.chromium.ui.base.WindowAndroid;

import javax.annotation.Nullable;

/**
 * A utility class to take a feedback-formatted screenshot of an {@link Activity}.
 */
@JNINamespace("chrome::android")
public final class ScreenshotTask {
    /**
     * Maximum dimension for the screenshot to be sent to the feedback handler.  This size
     * ensures the size of bitmap < 1MB, which is a requirement of the handler.
     */
    private static final int MAX_FEEDBACK_SCREENSHOT_DIMENSION = 600;

    /**
     * A callback passed to {@link #create} which will get a bitmap version of the screenshot.
     */
    public interface ScreenshotTaskCallback {
        /**
         * Called when collection of the bitmap has completed.
         * @param bitmap the bitmap or null.
         */
        void onGotBitmap(@Nullable Bitmap bitmap);
    }

    /**
     * Prepares screenshot (possibly asynchronously) and invokes the callback when the screenshot
     * is available, or collection has failed. The asynchronous path is only taken when the activity
     * that is passed in is a {@link ChromeActivity}.
     * The callback is always invoked asynchronously.
     */
    public static void create(Activity activity, final ScreenshotTaskCallback callback) {
        if (activity instanceof ChromeActivity) {
            Rect rect = new Rect();
            activity.getWindow().getDecorView().getRootView().getWindowVisibleDisplayFrame(rect);
            createCompositorScreenshot(((ChromeActivity) activity).getWindowAndroid(), rect,
                    callback);
            return;
        }

        final Bitmap bitmap = prepareScreenshot(activity, null);
        ThreadUtils.postOnUiThread(new Runnable() {
            @Override
            public void run() {
                callback.onGotBitmap(bitmap);
            }
        });
    }

    /**
     * A callback passed to the native snapshot API which returns the result in PNG format.
     */
    private interface SnapshotResultCallback {
        /**
         * Called when collection of the bitmap has completed.
         * @param pngBytes PNG-formatted bitmap in byte array if successful; otherwise null
         */
        void onCompleted(@Nullable byte[] pngBytes);
    }

    private static void createCompositorScreenshot(WindowAndroid windowAndroid,
            Rect windowRect, final ScreenshotTaskCallback callback) {
        SnapshotResultCallback resultCallback = new SnapshotResultCallback() {
            @Override
            public void onCompleted(byte[] pngBytes) {
                callback.onGotBitmap(pngBytes != null
                        ? BitmapFactory.decodeByteArray(pngBytes, 0, pngBytes.length) : null);
            }
        };
        nativeGrabWindowSnapshotAsync(resultCallback, windowAndroid.getNativePointer(),
                windowRect.width(), windowRect.height());
    }

    /**
     * Prepares a given screenshot for sending with a feedback.
     * If no screenshot is given it creates one from the activity View if an activity is provided.
     * @param activity An activity or null
     * @param bitmap A screenshot or null
     * @return A feedback-ready screenshot or null
     */
    private static Bitmap prepareScreenshot(@Nullable Activity activity, @Nullable Bitmap bitmap) {
        if (bitmap == null) {
            if (activity == null) return null;
            return UiUtils.generateScaledScreenshot(
                    activity.getWindow().getDecorView().getRootView(),
                    MAX_FEEDBACK_SCREENSHOT_DIMENSION, Bitmap.Config.ARGB_8888);
        }

        int screenshotMaxDimension = Math.max(bitmap.getWidth(), bitmap.getHeight());
        if (screenshotMaxDimension <= MAX_FEEDBACK_SCREENSHOT_DIMENSION) return bitmap;

        float screenshotScale = (float) MAX_FEEDBACK_SCREENSHOT_DIMENSION / screenshotMaxDimension;
        int destWidth = (int) (bitmap.getWidth() * screenshotScale);
        int destHeight = (int) (bitmap.getHeight() * screenshotScale);
        return Bitmap.createScaledBitmap(bitmap, destWidth, destHeight, true);
    }

    @CalledByNative
    private static void notifySnapshotFinished(Object callback, byte[] pngBytes) {
        ((SnapshotResultCallback) callback).onCompleted(pngBytes);
    }

    // This is a utility class, so it should never be created.
    private ScreenshotTask() {}

    private static native void nativeGrabWindowSnapshotAsync(SnapshotResultCallback callback,
            long nativeWindowAndroid, int width, int height);
}