/*
 * Copyright (C) 2010 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.browser;

import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.PaintDrawable;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * tabbed title bar for xlarge screen browser
 */
public class TabBar extends LinearLayout implements OnClickListener {

    private static final int PROGRESS_MAX = 100;

    private Activity mActivity;
    private UiController mUiController;
    private TabControl mTabControl;
    private XLargeUi mUi;

    private int mTabWidth;

    private TabScrollView mTabs;

    private ImageButton mNewTab;
    private int mButtonWidth;

    private Map<Tab, TabView> mTabMap;

    private int mCurrentTextureWidth = 0;
    private int mCurrentTextureHeight = 0;

    private Drawable mActiveDrawable;
    private Drawable mInactiveDrawable;

    private final Paint mActiveShaderPaint = new Paint();
    private final Paint mInactiveShaderPaint = new Paint();
    private final Paint mFocusPaint = new Paint();
    private final Matrix mActiveMatrix = new Matrix();
    private final Matrix mInactiveMatrix = new Matrix();

    private BitmapShader mActiveShader;
    private BitmapShader mInactiveShader;

    private int mTabOverlap;
    private int mAddTabOverlap;
    private int mTabSliceWidth;
    private boolean mUseQuickControls;

    public TabBar(Activity activity, UiController controller, XLargeUi ui) {
        super(activity);
        mActivity = activity;
        mUiController = controller;
        mTabControl = mUiController.getTabControl();
        mUi = ui;
        Resources res = activity.getResources();
        mTabWidth = (int) res.getDimension(R.dimen.tab_width);
        mActiveDrawable = res.getDrawable(R.drawable.bg_urlbar);
        mInactiveDrawable = res.getDrawable(R.drawable.browsertab_inactive);

        mTabMap = new HashMap<Tab, TabView>();
        LayoutInflater factory = LayoutInflater.from(activity);
        factory.inflate(R.layout.tab_bar, this);
        setPadding(0, (int) res.getDimension(R.dimen.tab_padding_top), 0, 0);
        mTabs = (TabScrollView) findViewById(R.id.tabs);
        mNewTab = (ImageButton) findViewById(R.id.newtab);
        mNewTab.setOnClickListener(this);

        updateTabs(mUiController.getTabs());
        mButtonWidth = -1;
        // tab dimensions
        mTabOverlap = (int) res.getDimension(R.dimen.tab_overlap);
        mAddTabOverlap = (int) res.getDimension(R.dimen.tab_addoverlap);
        mTabSliceWidth = (int) res.getDimension(R.dimen.tab_slice);

        mActiveShaderPaint.setStyle(Paint.Style.FILL);
        mActiveShaderPaint.setAntiAlias(true);

        mInactiveShaderPaint.setStyle(Paint.Style.FILL);
        mInactiveShaderPaint.setAntiAlias(true);

        mFocusPaint.setStyle(Paint.Style.STROKE);
        mFocusPaint.setStrokeWidth(res.getDimension(R.dimen.tab_focus_stroke));
        mFocusPaint.setAntiAlias(true);
        mFocusPaint.setColor(res.getColor(R.color.tabFocusHighlight));
    }

    @Override
    public void onConfigurationChanged(Configuration config) {
        super.onConfigurationChanged(config);
        Resources res = mActivity.getResources();
        mTabWidth = (int) res.getDimension(R.dimen.tab_width);
        // force update of tab bar
        mTabs.updateLayout();
    }

    void setUseQuickControls(boolean useQuickControls) {
        mUseQuickControls = useQuickControls;
        mNewTab.setVisibility(mUseQuickControls ? View.GONE
                : View.VISIBLE);
    }

    int getTabCount() {
        return mTabMap.size();
    }

    void updateTabs(List<Tab> tabs) {
        mTabs.clearTabs();
        mTabMap.clear();
        for (Tab tab : tabs) {
            TabView tv = buildTabView(tab);
            mTabs.addTab(tv);
        }
        mTabs.setSelectedTab(mTabControl.getCurrentPosition());
    }

    @Override
    protected void onMeasure(int hspec, int vspec) {
        super.onMeasure(hspec, vspec);
        int w = getMeasuredWidth();
        // adjust for new tab overlap
        if (!mUseQuickControls) {
            w -= mAddTabOverlap;
        }
        setMeasuredDimension(w, getMeasuredHeight());
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        // use paddingLeft and paddingTop
        int pl = getPaddingLeft();
        int pt = getPaddingTop();
        int sw = mTabs.getMeasuredWidth();
        int w = right - left - pl;
        if (mUseQuickControls) {
            mButtonWidth = 0;
        } else {
            mButtonWidth = mNewTab.getMeasuredWidth() - mAddTabOverlap;
            if (w-sw < mButtonWidth) {
                sw = w - mButtonWidth;
            }
        }
        mTabs.layout(pl, pt, pl + sw, bottom - top);
        // adjust for overlap
        if (!mUseQuickControls) {
            mNewTab.layout(pl + sw - mAddTabOverlap, pt,
                    pl + sw + mButtonWidth - mAddTabOverlap, bottom - top);
        }
    }

    public void onClick(View view) {
        if (mNewTab == view) {
            mUiController.openTabToHomePage();
        } else if (mTabs.getSelectedTab() == view) {
            if (mUseQuickControls) {
                if (mUi.isTitleBarShowing() && !isLoading()) {
                    mUi.stopEditingUrl();
                    mUi.hideTitleBar();
                } else {
                    mUi.stopWebViewScrolling();
                    mUi.editUrl(false);
                }
            } else if (mUi.isTitleBarShowing() && !isLoading()) {
                mUi.stopEditingUrl();
                mUi.hideTitleBar();
            } else {
                showUrlBar();
            }
        } else if (view instanceof TabView) {
            final Tab tab = ((TabView) view).mTab;
            int ix = mTabs.getChildIndex(view);
            if (ix >= 0) {
                mTabs.setSelectedTab(ix);
                mUiController.switchToTab(tab);
            }
        }
    }

    private void showUrlBar() {
        mUi.stopWebViewScrolling();
        mUi.showTitleBar();
    }

    private TabView buildTabView(Tab tab) {
        TabView tabview = new TabView(mActivity, tab);
        mTabMap.put(tab, tabview);
        tabview.setOnClickListener(this);
        return tabview;
    }

    private static Bitmap getDrawableAsBitmap(Drawable drawable, int width, int height) {
        Bitmap b = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(b);
        drawable.setBounds(0, 0, width, height);
        drawable.draw(c);
        c.setBitmap(null);
        return b;
    }

    /**
     * View used in the tab bar
     */
    class TabView extends LinearLayout implements OnClickListener {

        Tab mTab;
        View mTabContent;
        TextView mTitle;
        View mIncognito;
        View mSnapshot;
        ImageView mIconView;
        ImageView mLock;
        ImageView mClose;
        boolean mSelected;
        boolean mInLoad;
        Path mPath;
        Path mFocusPath;
        int[] mWindowPos;

        /**
         * @param context
         */
        public TabView(Context context, Tab tab) {
            super(context);
            setWillNotDraw(false);
            mPath = new Path();
            mFocusPath = new Path();
            mWindowPos = new int[2];
            mTab = tab;
            setGravity(Gravity.CENTER_VERTICAL);
            setOrientation(LinearLayout.HORIZONTAL);
            setPadding(mTabOverlap, 0, mTabSliceWidth, 0);
            LayoutInflater inflater = LayoutInflater.from(getContext());
            mTabContent = inflater.inflate(R.layout.tab_title, this, true);
            mTitle = (TextView) mTabContent.findViewById(R.id.title);
            mIconView = (ImageView) mTabContent.findViewById(R.id.favicon);
            mLock = (ImageView) mTabContent.findViewById(R.id.lock);
            mClose = (ImageView) mTabContent.findViewById(R.id.close);
            mClose.setOnClickListener(this);
            mIncognito = mTabContent.findViewById(R.id.incognito);
            mSnapshot = mTabContent.findViewById(R.id.snapshot);
            mSelected = false;
            mInLoad = false;
            // update the status
            updateFromTab();
        }

        @Override
        public void onClick(View v) {
            if (v == mClose) {
                closeTab();
            }
        }

        private void updateFromTab() {
            String displayTitle = mTab.getTitle();
            if (displayTitle == null) {
                displayTitle = mTab.getUrl();
            }
            setDisplayTitle(displayTitle);
            setProgress(mTab.getLoadProgress());
            if (mTab.getFavicon() != null) {
                setFavicon(mUi.getFaviconDrawable(mTab.getFavicon()));
            }
            updateTabIcons();
        }

        private void updateTabIcons() {
            mIncognito.setVisibility(
                    mTab.isPrivateBrowsingEnabled() ?
                    View.VISIBLE : View.GONE);
            mSnapshot.setVisibility(mTab.isSnapshot()
                    ? View.VISIBLE : View.GONE);
        }

        @Override
        public void setActivated(boolean selected) {
            mSelected = selected;
            mClose.setVisibility(mSelected ? View.VISIBLE : View.GONE);
            mIconView.setVisibility(mSelected ? View.GONE : View.VISIBLE);
            mTitle.setTextAppearance(mActivity, mSelected ?
                    R.style.TabTitleSelected : R.style.TabTitleUnselected);
            setHorizontalFadingEdgeEnabled(!mSelected);
            super.setActivated(selected);
            updateLayoutParams();
            setFocusable(!selected);
            postInvalidate();
        }

        public void updateLayoutParams() {
            LayoutParams lp = (LinearLayout.LayoutParams) getLayoutParams();
            lp.width = mTabWidth;
            lp.height =  LayoutParams.MATCH_PARENT;
            setLayoutParams(lp);
        }

        void setDisplayTitle(String title) {
            mTitle.setText(title);
        }

        void setFavicon(Drawable d) {
            mIconView.setImageDrawable(d);
        }

        void setLock(Drawable d) {
            if (null == d) {
                mLock.setVisibility(View.GONE);
            } else {
                mLock.setImageDrawable(d);
                mLock.setVisibility(View.VISIBLE);
            }
        }

        void setProgress(int newProgress) {
            if (newProgress >= PROGRESS_MAX) {
                mInLoad = false;
            } else {
                if (!mInLoad && getWindowToken() != null) {
                    mInLoad = true;
                }
            }
        }

        private void closeTab() {
            if (mTab == mTabControl.getCurrentTab()) {
                mUiController.closeCurrentTab();
            } else {
                mUiController.closeTab(mTab);
            }
        }

        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            super.onLayout(changed, l, t, r, b);
            setTabPath(mPath, 0, 0, r - l, b - t);
            setFocusPath(mFocusPath, 0, 0, r - l, b - t);
        }

        @Override
        protected void dispatchDraw(Canvas canvas) {
            if (mCurrentTextureWidth != mUi.getContentWidth() ||
                    mCurrentTextureHeight != getHeight()) {
                mCurrentTextureWidth = mUi.getContentWidth();
                mCurrentTextureHeight = getHeight();

                if (mCurrentTextureWidth > 0 && mCurrentTextureHeight > 0) {
                    Bitmap activeTexture = getDrawableAsBitmap(mActiveDrawable,
                            mCurrentTextureWidth, mCurrentTextureHeight);
                    Bitmap inactiveTexture = getDrawableAsBitmap(mInactiveDrawable,
                            mCurrentTextureWidth, mCurrentTextureHeight);

                    mActiveShader = new BitmapShader(activeTexture,
                            Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
                    mActiveShaderPaint.setShader(mActiveShader);

                    mInactiveShader = new BitmapShader(inactiveTexture,
                            Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
                    mInactiveShaderPaint.setShader(mInactiveShader);
                }
            }
            // add some monkey protection
            if ((mActiveShader != null) && (mInactiveShader != null)) {
                int state = canvas.save();
                getLocationInWindow(mWindowPos);
                Paint paint = mSelected ? mActiveShaderPaint : mInactiveShaderPaint;
                drawClipped(canvas, paint, mPath, mWindowPos[0]);
                canvas.restoreToCount(state);
            }
            super.dispatchDraw(canvas);
        }

        private void drawClipped(Canvas canvas, Paint paint, Path clipPath, int left) {
            // TODO: We should change the matrix/shader only when needed
            final Matrix matrix = mSelected ? mActiveMatrix : mInactiveMatrix;
            matrix.setTranslate(-left, 0.0f);
            (mSelected ? mActiveShader : mInactiveShader).setLocalMatrix(matrix);
            canvas.drawPath(clipPath, paint);
            if (isFocused()) {
                canvas.drawPath(mFocusPath, mFocusPaint);
            }
        }

        private void setTabPath(Path path, int l, int t, int r, int b) {
            path.reset();
            path.moveTo(l, b);
            path.lineTo(l, t);
            path.lineTo(r - mTabSliceWidth, t);
            path.lineTo(r, b);
            path.close();
        }

        private void setFocusPath(Path path, int l, int t, int r, int b) {
            path.reset();
            path.moveTo(l, b);
            path.lineTo(l, t);
            path.lineTo(r - mTabSliceWidth, t);
            path.lineTo(r, b);
        }

    }

    private void animateTabOut(final Tab tab, final TabView tv) {
        ObjectAnimator scalex = ObjectAnimator.ofFloat(tv, "scaleX", 1.0f, 0.0f);
        ObjectAnimator scaley = ObjectAnimator.ofFloat(tv, "scaleY", 1.0f, 0.0f);
        ObjectAnimator alpha = ObjectAnimator.ofFloat(tv, "alpha", 1.0f, 0.0f);
        AnimatorSet animator = new AnimatorSet();
        animator.playTogether(scalex, scaley, alpha);
        animator.setDuration(150);
        animator.addListener(new AnimatorListener() {

            @Override
            public void onAnimationCancel(Animator animation) {
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                mTabs.removeTab(tv);
                mTabMap.remove(tab);
                mUi.onRemoveTabCompleted(tab);
            }

            @Override
            public void onAnimationRepeat(Animator animation) {
            }

            @Override
            public void onAnimationStart(Animator animation) {
            }

        });
        animator.start();
    }

    private void animateTabIn(final Tab tab, final TabView tv) {
        ObjectAnimator scalex = ObjectAnimator.ofFloat(tv, "scaleX", 0.0f, 1.0f);
        scalex.setDuration(150);
        scalex.addListener(new AnimatorListener() {

            @Override
            public void onAnimationCancel(Animator animation) {
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                mUi.onAddTabCompleted(tab);
            }

            @Override
            public void onAnimationRepeat(Animator animation) {
            }

            @Override
            public void onAnimationStart(Animator animation) {
                mTabs.addTab(tv);
            }

        });
        scalex.start();
    }

    // TabChangeListener implementation

    public void onSetActiveTab(Tab tab) {
        mTabs.setSelectedTab(mTabControl.getTabPosition(tab));
        TabView tv = mTabMap.get(tab);
        if (tv != null) {
            tv.setProgress(tv.mTab.getLoadProgress());
        }
    }

    public void onFavicon(Tab tab, Bitmap favicon) {
        TabView tv = mTabMap.get(tab);
        if (tv != null) {
            tv.setFavicon(mUi.getFaviconDrawable(favicon));
        }
    }

    public void onNewTab(Tab tab) {
        TabView tv = buildTabView(tab);
        animateTabIn(tab, tv);
    }

    public void onProgress(Tab tab, int progress) {
        TabView tv = mTabMap.get(tab);
        if (tv != null) {
            tv.setProgress(progress);
        }
    }

    public void onRemoveTab(Tab tab) {
        TabView tv = mTabMap.get(tab);
        if (tv != null) {
            animateTabOut(tab, tv);
        } else {
            mTabMap.remove(tab);
        }
    }

    public void onUrlAndTitle(Tab tab, String url, String title) {
        TabView tv = mTabMap.get(tab);
        if (tv != null) {
            if (title != null) {
                tv.setDisplayTitle(title);
            } else if (url != null) {
                tv.setDisplayTitle(UrlUtils.stripUrl(url));
            }
            tv.updateTabIcons();
        }
    }

    private boolean isLoading() {
        TabView tv = mTabMap.get(mTabControl.getCurrentTab());
        if (tv != null) {
            return tv.mInLoad;
        } else {
            return false;
        }
    }

}