/*
 * Copyright (C) 2006 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.internal.view.menu;

import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.text.Layout;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.SoundEffectConstants;
import android.view.View;
import android.view.ViewDebug;
import android.widget.TextView;

import com.android.internal.view.menu.MenuBuilder.ItemInvoker;

/**
 * The item view for each item in the {@link IconMenuView}.
 */
public final class IconMenuItemView extends TextView implements MenuView.ItemView {
    
    private static final int NO_ALPHA = 0xFF;
    
    private IconMenuView mIconMenuView;
    
    private ItemInvoker mItemInvoker;
    private MenuItemImpl mItemData; 
    
    private Drawable mIcon;
    
    private int mTextAppearance;
    private Context mTextAppearanceContext;
    
    private float mDisabledAlpha;

    private Rect mPositionIconAvailable = new Rect();
    private Rect mPositionIconOutput = new Rect();
    
    private boolean mShortcutCaptionMode;
    private String mShortcutCaption;
    
    private static String sPrependShortcutLabel;

    public IconMenuItemView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);

        if (sPrependShortcutLabel == null) {
            /*
             * Views should only be constructed from the UI thread, so no
             * synchronization needed
             */
            sPrependShortcutLabel = getResources().getString(
                    com.android.internal.R.string.prepend_shortcut_label);
        }

        final TypedArray a = context.obtainStyledAttributes(
                attrs, com.android.internal.R.styleable.MenuView, defStyleAttr, defStyleRes);

        mDisabledAlpha = a.getFloat(
                com.android.internal.R.styleable.MenuView_itemIconDisabledAlpha, 0.8f);
        mTextAppearance = a.getResourceId(com.android.internal.R.styleable.
                                          MenuView_itemTextAppearance, -1);
        mTextAppearanceContext = context;
        
        a.recycle();
    }

    public IconMenuItemView(Context context, AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    public IconMenuItemView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    /**
     * Initializes with the provided title and icon
     * @param title The title of this item
     * @param icon The icon of this item
     */
    void initialize(CharSequence title, Drawable icon) {
        setClickable(true);
        setFocusable(true);

        if (mTextAppearance != -1) {
            setTextAppearance(mTextAppearanceContext, mTextAppearance);
        }

        setTitle(title);
        setIcon(icon);

        if (mItemData != null) {
            final CharSequence contentDescription = mItemData.getContentDescription();
            if (TextUtils.isEmpty(contentDescription)) {
                setContentDescription(title);
            } else {
                setContentDescription(contentDescription);
            }
            setTooltipText(mItemData.getTooltipText());
        }
    }

    public void initialize(MenuItemImpl itemData, int menuType) {
        mItemData = itemData;

        initialize(itemData.getTitleForItemView(this), itemData.getIcon());

        setVisibility(itemData.isVisible() ? View.VISIBLE : View.GONE);
        setEnabled(itemData.isEnabled());
    }

    public void setItemData(MenuItemImpl data) {
        mItemData = data;
    }

    @Override
    public boolean performClick() {
        // Let the view's click listener have top priority (the More button relies on this)
        if (super.performClick()) {
            return true;
        }
        
        if ((mItemInvoker != null) && (mItemInvoker.invokeItem(mItemData))) {
            playSoundEffect(SoundEffectConstants.CLICK);
            return true;
        } else {
            return false;
        }
    }
    
    public void setTitle(CharSequence title) {
        
        if (mShortcutCaptionMode) {
            /*
             * Don't set the title directly since it will replace the
             * shortcut+title being shown. Instead, re-set the shortcut caption
             * mode so the new title is shown.
             */
            setCaptionMode(true);
            
        } else if (title != null) {
            setText(title);
        }
    }
    
    void setCaptionMode(boolean shortcut) {
        /*
         * If there is no item model, don't do any of the below (for example,
         * the 'More' item doesn't have a model)
         */
        if (mItemData == null) {
            return;
        }
        
        mShortcutCaptionMode = shortcut && (mItemData.shouldShowShortcut());
        
        CharSequence text = mItemData.getTitleForItemView(this);
        
        if (mShortcutCaptionMode) {
            
            if (mShortcutCaption == null) {
                mShortcutCaption = mItemData.getShortcutLabel();
            }

            text = mShortcutCaption;
        }
        
        setText(text);
    }
    
    public void setIcon(Drawable icon) {
        mIcon = icon;
        
        if (icon != null) {
            
            /* Set the bounds of the icon since setCompoundDrawables needs it. */
            icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
            
            // Set the compound drawables
            setCompoundDrawables(null, icon, null, null);
            
            // When there is an icon, make sure the text is at the bottom
            setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL);

            /*
             * Request a layout to reposition the icon. The positioning of icon
             * depends on this TextView's line bounds, which is only available
             * after a layout.
             */  
            requestLayout();
        } else {
            setCompoundDrawables(null, null, null, null);
            
            // When there is no icon, make sure the text is centered vertically
            setGravity(Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL);
        }
    }

    @UnsupportedAppUsage
    public void setItemInvoker(ItemInvoker itemInvoker) {
        mItemInvoker = itemInvoker;
    }
    
    @ViewDebug.CapturedViewProperty(retrieveReturn = true)
    public MenuItemImpl getItemData() {
        return mItemData;
    }

    @Override
    public void setVisibility(int v) {
        super.setVisibility(v);
        
        if (mIconMenuView != null) {
            // On visibility change, mark the IconMenuView to refresh itself eventually
            mIconMenuView.markStaleChildren();
        }
    }
    
    @UnsupportedAppUsage
    void setIconMenuView(IconMenuView iconMenuView) {
        mIconMenuView = iconMenuView;
    }

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

        if (mItemData != null && mIcon != null) {
            // When disabled, the not-focused state and the pressed state should
            // drop alpha on the icon
            final boolean isInAlphaState = !mItemData.isEnabled() && (isPressed() || !isFocused());
            mIcon.setAlpha(isInAlphaState ? (int) (mDisabledAlpha * NO_ALPHA) : NO_ALPHA);
        }
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        
        positionIcon();
    }

    @Override
    protected void onTextChanged(CharSequence text, int start, int before, int after) {
        super.onTextChanged(text, start, before, after);

        // our layout params depend on the length of the text
        setLayoutParams(getTextAppropriateLayoutParams());
    }

    /**
     * @return layout params appropriate for this view.  If layout params already exist, it will
     *         augment them to be appropriate to the current text size.
     */
    @UnsupportedAppUsage
    IconMenuView.LayoutParams getTextAppropriateLayoutParams() {
        IconMenuView.LayoutParams lp = (IconMenuView.LayoutParams) getLayoutParams();
        if (lp == null) {
            // Default layout parameters
            lp = new IconMenuView.LayoutParams(
                    IconMenuView.LayoutParams.MATCH_PARENT, IconMenuView.LayoutParams.MATCH_PARENT);
        }

        // Set the desired width of item
        lp.desiredWidth = (int) Layout.getDesiredWidth(getText(), 0, getText().length(),
                getPaint(), getTextDirectionHeuristic());

        return lp;
    }

    /**
     * Positions the icon vertically (horizontal centering is taken care of by
     * the TextView's gravity).
     */
    private void positionIcon() {
        
        if (mIcon == null) {
            return;
        }
        
        // We reuse the output rectangle as a temp rect
        Rect tmpRect = mPositionIconOutput;
        getLineBounds(0, tmpRect);
        mPositionIconAvailable.set(0, 0, getWidth(), tmpRect.top);
        final int layoutDirection = getLayoutDirection();
        Gravity.apply(Gravity.CENTER_VERTICAL | Gravity.START, mIcon.getIntrinsicWidth(), mIcon
                .getIntrinsicHeight(), mPositionIconAvailable, mPositionIconOutput,
                layoutDirection);
        mIcon.setBounds(mPositionIconOutput);
    }

    public void setCheckable(boolean checkable) {
    }

    public void setChecked(boolean checked) {
    }

    public void setShortcut(boolean showShortcut, char shortcutKey) {
        
        if (mShortcutCaptionMode) {
            /*
             * Shortcut has changed and we're showing it right now, need to
             * update (clear the old one first).
             */
            mShortcutCaption = null;
            setCaptionMode(true);
        }
    }

    public boolean prefersCondensedTitle() {
        return true;
    }

    public boolean showsIcon() {
        return true;
    }

}
