/*
 * Copyright (C) 2012 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.gallery3d.ui;

import android.graphics.Bitmap;
import android.graphics.RectF;

import com.android.gallery3d.common.Utils;
import com.android.gallery3d.data.BitmapPool;
import com.android.gallery3d.data.MediaItem;

// This is a ScreenNail wraps a Bitmap. There are some extra functions:
//
// - If we need to draw before the bitmap is available, we draw a rectange of
// placeholder color (gray).
//
// - When the the bitmap is available, and we have drawn the placeholder color
// before, we will do a fade-in animation.
public class TiledScreenNail implements ScreenNail {
    @SuppressWarnings("unused")
    private static final String TAG = "TiledScreenNail";

    // The duration of the fading animation in milliseconds
    private static final int DURATION = 180;

    private static int sMaxSide = 640;

    // These are special values for mAnimationStartTime
    private static final long ANIMATION_NOT_NEEDED = -1;
    private static final long ANIMATION_NEEDED = -2;
    private static final long ANIMATION_DONE = -3;

    private int mWidth;
    private int mHeight;
    private long mAnimationStartTime = ANIMATION_NOT_NEEDED;

    private Bitmap mBitmap;
    private TiledTexture mTexture;

    public TiledScreenNail(Bitmap bitmap) {
        mWidth = bitmap.getWidth();
        mHeight = bitmap.getHeight();
        mBitmap = bitmap;
        mTexture = new TiledTexture(bitmap);
    }

    public TiledScreenNail(int width, int height) {
        setSize(width, height);
    }

    // This gets overridden by bitmap_screennail_placeholder
    // in GalleryUtils.initialize
    private static int mPlaceholderColor = 0xFF222222;
    private static boolean mDrawPlaceholder = true;

    public static void setPlaceholderColor(int color) {
        mPlaceholderColor = color;
    }

    private void setSize(int width, int height) {
        if (width == 0 || height == 0) {
            width = sMaxSide;
            height = sMaxSide * 3 / 4;
        }
        float scale = Math.min(1, (float) sMaxSide / Math.max(width, height));
        mWidth = Math.round(scale * width);
        mHeight = Math.round(scale * height);
    }

    private static void recycleBitmap(BitmapPool pool, Bitmap bitmap) {
        if (pool == null || bitmap == null) return;
        pool.recycle(bitmap);
    }

    // Combines the two ScreenNails.
    // Returns the used one and recycle the unused one.
    public ScreenNail combine(ScreenNail other) {
        if (other == null) {
            return this;
        }

        if (!(other instanceof TiledScreenNail)) {
            recycle();
            return other;
        }

        // Now both are TiledScreenNail. Move over the information about width,
        // height, and Bitmap, then recycle the other.
        TiledScreenNail newer = (TiledScreenNail) other;
        mWidth = newer.mWidth;
        mHeight = newer.mHeight;
        if (newer.mTexture != null) {
            recycleBitmap(MediaItem.getThumbPool(), mBitmap);
            if (mTexture != null) mTexture.recycle();
            mBitmap = newer.mBitmap;
            mTexture = newer.mTexture;
            newer.mBitmap = null;
            newer.mTexture = null;
        }
        newer.recycle();
        return this;
    }

    public void updatePlaceholderSize(int width, int height) {
        if (mBitmap != null) return;
        if (width == 0 || height == 0) return;
        setSize(width, height);
    }

    @Override
    public int getWidth() {
        return mWidth;
    }

    @Override
    public int getHeight() {
        return mHeight;
    }

    @Override
    public void noDraw() {
    }

    @Override
    public void recycle() {
        if (mTexture != null) {
            mTexture.recycle();
            mTexture = null;
        }
        recycleBitmap(MediaItem.getThumbPool(), mBitmap);
        mBitmap = null;
    }

    public static void disableDrawPlaceholder() {
        mDrawPlaceholder = false;
    }

    public static void enableDrawPlaceholder() {
        mDrawPlaceholder = true;
    }

    @Override
    public void draw(GLCanvas canvas, int x, int y, int width, int height) {
        if (mTexture == null || !mTexture.isReady()) {
            if (mAnimationStartTime == ANIMATION_NOT_NEEDED) {
                mAnimationStartTime = ANIMATION_NEEDED;
            }
            if(mDrawPlaceholder) {
                canvas.fillRect(x, y, width, height, mPlaceholderColor);
            }
            return;
        }

        if (mAnimationStartTime == ANIMATION_NEEDED) {
            mAnimationStartTime = AnimationTime.get();
        }

        if (isAnimating()) {
            mTexture.drawMixed(canvas, mPlaceholderColor, getRatio(), x, y,
                    width, height);
        } else {
            mTexture.draw(canvas, x, y, width, height);
        }
    }

    @Override
    public void draw(GLCanvas canvas, RectF source, RectF dest) {
        if (mTexture == null || !mTexture.isReady()) {
            canvas.fillRect(dest.left, dest.top, dest.width(), dest.height(),
                    mPlaceholderColor);
            return;
        }

        mTexture.draw(canvas, source, dest);
    }

    public boolean isAnimating() {
        // The TiledTexture may not be uploaded completely yet.
        // In that case, we count it as animating state and we will draw
        // the placeholder in TileImageView.
        if (mTexture == null || !mTexture.isReady()) return true;
        if (mAnimationStartTime < 0) return false;
        if (AnimationTime.get() - mAnimationStartTime >= DURATION) {
            mAnimationStartTime = ANIMATION_DONE;
            return false;
        }
        return true;
    }

    private float getRatio() {
        float r = (float) (AnimationTime.get() - mAnimationStartTime) / DURATION;
        return Utils.clamp(1.0f - r, 0.0f, 1.0f);
    }

    public boolean isShowingPlaceholder() {
        return (mBitmap == null) || isAnimating();
    }

    public TiledTexture getTexture() {
        return mTexture;
    }

    public static void setMaxSide(int size) {
        sMaxSide = size;
    }
}
