/*
 * 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.Context;
import android.graphics.Bitmap;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.android.ex.photo.util.ImageUtils;
import com.android.mail.R;
import com.android.mail.providers.Attachment;
import com.android.mail.utils.AttachmentUtils;
import com.android.mail.utils.LogTag;
import com.android.mail.utils.LogUtils;

/**
 * Base class for attachment tiles that handles the work of fetching and displaying the bitmaps for
 * the tiles.
 */
public abstract class AttachmentTile extends RelativeLayout implements AttachmentBitmapHolder {
    protected Attachment mAttachment;
    private ImageView mIcon;
    private ImageView mDefaultIcon;
    private TextView mTitle;
    private TextView mSubtitle;
    private String mAttachmentSizeText;
    private String mDisplayType;
    private boolean mDefaultThumbnailSet;
    private AttachmentPreviewCache mAttachmentPreviewCache;

    private static final String LOG_TAG = LogTag.getLogTag();
    // previews with width/height or height/width less than this value will be
    // considered skinny
    private static final float skinnyThresholdRatio = 0.5f;

    private boolean mAlwaysShowInfoText;


    /**
     * Returns true if the attachment should be rendered as a tile. with a large image preview.
     * @param attachment the attachment to render
     * @return true if the attachment should be rendered as a tile
     */
    public static boolean isTiledAttachment(final Attachment attachment) {
        return ImageUtils.isImageMimeType(attachment.getContentType());
    }

    public AttachmentTile(Context context) {
        this(context, null);
    }

    public AttachmentTile(Context context, AttributeSet attrs) {
        super(context, attrs);
        mDefaultThumbnailSet = true;
        mAlwaysShowInfoText = false;
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();

        mTitle = (TextView) findViewById(R.id.attachment_tile_title);
        mSubtitle = (TextView) findViewById(R.id.attachment_tile_subtitle);
        mIcon = (ImageView) findViewById(R.id.attachment_tile_image);
        mDefaultIcon = (ImageView) findViewById(R.id.attachment_default_image);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);

        ThumbnailLoadTask.setupThumbnailPreview(mAttachmentPreviewCache, this, mAttachment, null);
    }

    public Attachment getAttachment() {
        return mAttachment;
    }

    /**
     * Render or update an attachment's view. This happens immediately upon instantiation, and
     * repeatedly as status updates stream in, so only properties with new or changed values will
     * cause sub-views to update.
     */
    protected void render(Attachment attachment, AttachmentPreviewCache attachmentPreviewCache) {
        if (attachment == null) {
            setVisibility(View.INVISIBLE);
            return;
        }

        final Attachment prevAttachment = mAttachment;
        mAttachment = attachment;
        mAttachmentPreviewCache = attachmentPreviewCache;

        LogUtils.d(LOG_TAG, "got attachment list row: name=%s state/dest=%d/%d dled=%d" +
                " contentUri=%s MIME=%s flags=%d", attachment.getName(), attachment.state,
                attachment.destination, attachment.downloadedSize, attachment.contentUri,
                attachment.getContentType(), attachment.flags);

        if ((attachment.flags & Attachment.FLAG_DUMMY_ATTACHMENT) != 0) {
            // TODO: This is not an ideal string, but it's too late in KLP to add new strings.
            mTitle.setText(R.string.load_more);
        } else if (prevAttachment == null
                || !TextUtils.equals(attachment.getName(), prevAttachment.getName())) {
            mTitle.setText(attachment.getName());
        }

        if (prevAttachment == null || attachment.size != prevAttachment.size) {
            mAttachmentSizeText = AttachmentUtils.convertToHumanReadableSize(getContext(),
                    attachment.size);
            mDisplayType = AttachmentUtils.getDisplayType(getContext(), attachment);
            updateSubtitleText();
        }

        ThumbnailLoadTask.setupThumbnailPreview(mAttachmentPreviewCache, this, attachment,
                prevAttachment);
    }

    private void updateSubtitleText() {
        // TODO: make this a formatted resource when we have a UX design.
        // not worth translation right now.
        StringBuilder sb = new StringBuilder();
        sb.append(mAttachmentSizeText);
        if (mDisplayType != null) {
            sb.append(' ');
            sb.append(mDisplayType);
        }
        mSubtitle.setText(sb.toString());
    }

    @Override
    public void setThumbnailToDefault() {
        final Bitmap cachedPreview = mAttachmentPreviewCache.get(mAttachment);
        if (cachedPreview != null) {
            setThumbnail(cachedPreview);
            return;
        }
        mDefaultIcon.setVisibility(View.VISIBLE);
        mTitle.setVisibility(View.VISIBLE);
        mSubtitle.setVisibility(View.VISIBLE);
        mDefaultThumbnailSet = true;
    }

    @Override
    public void setThumbnail(Bitmap result) {
        if (result == null) {
            return;
        }

        // We got a real thumbnail; hide the default thumbnail.
        mDefaultIcon.setVisibility(View.GONE);
        if (!mAlwaysShowInfoText) {
            mTitle.setVisibility(View.GONE);
            mSubtitle.setVisibility(View.GONE);
        }

        final int maxSize = getResources().getInteger(R.integer.attachment_preview_max_size);
        final int width = result.getWidth();
        final int height = result.getHeight();
        final int scaledWidth = width * getResources().getDisplayMetrics().densityDpi
                / DisplayMetrics.DENSITY_DEFAULT;
        final int scaledHeight = height * getResources().getDisplayMetrics().densityDpi
                / DisplayMetrics.DENSITY_DEFAULT;
        // ratio of the image
        final float ratio = Math.min((float) width / height, (float) height / width);

        final boolean large = width >= maxSize || scaledWidth >= mIcon.getWidth()
                || height >= maxSize || scaledHeight >= mIcon.getHeight();
        final boolean skinny =
                // the image is loooong
                ratio < skinnyThresholdRatio &&
                // AND if the image was centered and cropped, the resulting
                // image would still be loooong
                !(scaledWidth >= mIcon.getHeight() * skinnyThresholdRatio
                        && scaledHeight >= mIcon.getWidth() * skinnyThresholdRatio);
        LogUtils.d(LOG_TAG, "scaledWidth: %d, scaledHeight: %d, large: %b, skinny: %b", scaledWidth,
                scaledHeight, large, skinny);

        if (large) {
            // preview fills up at least 1 dimension
            if (skinny) {
                // just center. The shorter dimension stays the same while the
                // longer dimension is cropped
                mIcon.setScaleType(ScaleType.CENTER);
            } else {
                // fill. Both dimensions are scaled to fill the box, the longer
                // dimension is cropped
                mIcon.setScaleType(ScaleType.CENTER_CROP);
            }
        } else {
            // preview is small. just center
            mIcon.setScaleType(ScaleType.CENTER);
        }

        mIcon.setImageBitmap(result);
        mAttachmentPreviewCache.set(mAttachment, result);
        mDefaultThumbnailSet = false;
    }

    @Override
    public int getThumbnailWidth() {
        return mIcon.getWidth();
    }

    @Override
    public int getThumbnailHeight() {
        return mIcon.getHeight();
    }

    @Override
    public ContentResolver getResolver() {
        return getContext().getContentResolver();
    }

    @Override
    public boolean bitmapSetToDefault() {
        return mDefaultThumbnailSet;
    }

    public static final class AttachmentPreview implements Parcelable {
        public String attachmentIdentifier;
        public Bitmap preview;

        @Override
        public int describeContents() {
            return 0;
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(attachmentIdentifier);
            dest.writeParcelable(preview, 0);
        }

        public static final Parcelable.Creator<AttachmentPreview> CREATOR
                = new Parcelable.Creator<AttachmentPreview>() {
                        @Override
                    public AttachmentPreview createFromParcel(Parcel in) {
                        return new AttachmentPreview(in);
                    }

                        @Override
                    public AttachmentPreview[] newArray(int size) {
                        return new AttachmentPreview[size];
                    }
                };

        private AttachmentPreview(Parcel in) {
            attachmentIdentifier = in.readString();
            preview = in.readParcelable(null);
        }

        public AttachmentPreview(Attachment attachment, Bitmap preview) {
            this.attachmentIdentifier = attachment.getIdentifierUri().toString();
            this.preview = preview;
        }
    }

    public interface AttachmentPreviewCache {
        void set(Attachment attachment, Bitmap preview);
        Bitmap get(Attachment attachment);
    }

    @Override
    public void thumbnailLoadFailed() {
        setThumbnailToDefault();
    }

    protected void setAlwaysShowInfoText(boolean alwaysShowInfoText) {
        mAlwaysShowInfoText = alwaysShowInfoText;
    }
}
