/*
 * Copyright (C) 2014 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.camera.data;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.Point;
import android.media.MediaMetadataRetriever;

import com.android.camera.debug.Log;

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

import javax.microedition.khronos.opengles.GL11;

/**
 * An utility class for data in content provider.
 */
public class FilmstripItemUtils {

    private static final Log.Tag TAG = new Log.Tag("LocalDataUtil");

    /**
     * @param mimeType The MIME type to check.
     * @return Whether the MIME is a video type.
     */
    public static boolean isMimeTypeVideo(String mimeType) {
        return mimeType != null && mimeType.startsWith("video/");
    }

    /**
     * Checks whether the MIME type represents an image media item.
     *
     * @param mimeType The MIME type to check.
     * @return Whether the MIME is a image type.
     */
    public static boolean isMimeTypeImage(String mimeType) {
        return mimeType != null && mimeType.startsWith("image/");
    }


    /**
     * Decodes the dimension of a bitmap.
     *
     * @param is An input stream with the data of the bitmap.
     * @return The decoded width/height is stored in Point.x/Point.y
     *         respectively.
     */
    public static Point decodeBitmapDimension(InputStream is) {
        Point size = null;
        BitmapFactory.Options justBoundsOpts = new BitmapFactory.Options();
        justBoundsOpts.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(is, null, justBoundsOpts);
        if (justBoundsOpts.outWidth > 0 && justBoundsOpts.outHeight > 0) {
            size = new Point(justBoundsOpts.outWidth, justBoundsOpts.outHeight);
        } else {
            Log.e(TAG, "Bitmap dimension decoding failed");
        }
        return size;
    }

    /**
     * Load the thumbnail of an image from an {@link java.io.InputStream}.
     *
     * @param stream The input stream of the image.
     * @param imageWidth Image width.
     * @param imageHeight Image height.
     * @param widthBound The bound of the width of the decoded image.
     * @param heightBound The bound of the height of the decoded image.
     * @param orientation The orientation of the image. The image will be rotated
     *                    clockwise in degrees.
     * @param maximumPixels The bound for the number of pixels of the decoded image.
     * @return {@code null} if the decoding failed.
     */
    public static Bitmap loadImageThumbnailFromStream(InputStream stream, int imageWidth,
            int imageHeight, int widthBound, int heightBound, int orientation,
            int maximumPixels) {

        /** 32K buffer. */
        byte[] decodeBuffer = new byte[32 * 1024];

        if (orientation % 180 != 0) {
            int temp = imageHeight;
            imageHeight = imageWidth;
            imageWidth = temp;
        }

        // Generate Bitmap of maximum size that fits into widthBound x heightBound.
        // Algorithm: start with full size and step down in powers of 2.
        int targetWidth = imageWidth;
        int targetHeight = imageHeight;
        int sampleSize = 1;
        while (targetHeight > heightBound || targetWidth > widthBound ||
                targetHeight > GL11.GL_MAX_TEXTURE_SIZE || targetWidth > GL11.GL_MAX_TEXTURE_SIZE ||
                targetHeight * targetWidth > maximumPixels) {
            sampleSize <<= 1;
            targetWidth = imageWidth / sampleSize;
            targetHeight = imageWidth / sampleSize;
        }

        // For large (> MAXIMUM_TEXTURE_SIZE) high aspect ratio (panorama)
        // Bitmap requests:
        //   Step 1: ask for double size.
        //   Step 2: scale maximum edge down to MAXIMUM_TEXTURE_SIZE.
        //
        // Here's the step 1: double size.
        if ((heightBound > GL11.GL_MAX_TEXTURE_SIZE || widthBound > GL11.GL_MAX_TEXTURE_SIZE) &&
                targetWidth * targetHeight < maximumPixels / 4 && sampleSize > 1) {
            sampleSize >>= 2;
        }

        BitmapFactory.Options opts = new BitmapFactory.Options();
        opts.inSampleSize = sampleSize;
        opts.inTempStorage = decodeBuffer;
        Bitmap b = BitmapFactory.decodeStream(stream, null, opts);

        if (b == null) {
            return null;
        }

        // Step 2: scale maximum edge down to maximum texture size.
        // If Bitmap maximum edge > MAXIMUM_TEXTURE_SIZE, which can happen for panoramas,
        // scale to fit in MAXIMUM_TEXTURE_SIZE.
        if (b.getWidth() > GL11.GL_MAX_TEXTURE_SIZE || b.getHeight() >
                GL11.GL_MAX_TEXTURE_SIZE) {
            int maxEdge = Math.max(b.getWidth(), b.getHeight());
            b = Bitmap.createScaledBitmap(b, b.getWidth() * GL11.GL_MAX_TEXTURE_SIZE / maxEdge,
                    b.getHeight() * GL11.GL_MAX_TEXTURE_SIZE / maxEdge, false);
        }

        // Not called often because most modes save image data non-rotated.
        if (orientation != 0 && b != null) {
            Matrix m = new Matrix();
            m.setRotate(orientation);
            b = Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), m, false);
        }

        return b;
    }

    /**
     * Loads the thumbnail of a video.
     *
     * @param path The path to the video file.
     * @return {@code null} if the loading failed.
     */
    public static Bitmap loadVideoThumbnail(String path) {
        Bitmap bitmap = null;
        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
        try {
            retriever.setDataSource(path);
            byte[] data = retriever.getEmbeddedPicture();
            if (data != null) {
                bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
            }
            if (bitmap == null) {
                bitmap = retriever.getFrameAtTime();
            }
        } catch (IllegalArgumentException e) {
            Log.e(TAG, "MediaMetadataRetriever.setDataSource() fail:" + e.getMessage());
        }
        try {
            retriever.release();
        } catch (IOException e) {
            // We ignore errors occurred while releasing the MediaMetadataRetriever.
        }
        return bitmap;
    }
}
