// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.browser.infobar;

import android.content.Context;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.style.ClickableSpan;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;

import org.chromium.base.CalledByNative;
import org.chromium.chrome.browser.infobar.ContentWrapperView;
import org.chromium.chrome.browser.infobar.InfoBar;
import org.chromium.chrome.browser.infobar.InfoBarLayout;
import org.chromium.chrome.browser.infobar.TwoButtonInfoBar;
import org.chromium.content.browser.DeviceUtils;
import org.chromium.chrome.R;

/**
 * Java version of the translate infobar
 */
public class TranslateInfoBar extends TwoButtonInfoBar implements SubPanelListener {
    private static String LOG_TAG = "TranslateInfoBar";

    // Needs to be kept in sync with the Type enum in translate_infobar_delegate.h.
    public static final int BEFORE_TRANSLATE_INFOBAR = 0;
    public static final int TRANSLATING_INFOBAR = 1;
    public static final int AFTER_TRANSLATE_INFOBAR = 2;
    public static final int TRANSLATE_ERROR_INFOBAR = 3;
    public static final int MAX_INFOBAR_INDEX = 4;

    // Defines what subpanel needs to be shown, if any
    public static final int NO_PANEL = 0;
    public static final int LANGUAGE_PANEL = 1;
    public static final int NEVER_PANEL = 2;
    public static final int ALWAYS_PANEL = 3;
    public static final int MAX_PANEL_INDEX = 4;

    private int mInfoBarType;
    private TranslateOptions mOptions;
    private int mOptionsPanelViewType;
    private TranslateSubPanel mSubPanel;
    private final boolean mShouldShowNeverBar;
    private final TranslateInfoBarDelegate mTranslateDelegate;

    public TranslateInfoBar(int nativeInfoBarPtr, TranslateInfoBarDelegate delegate,
            int infoBarType, int sourceLanguageIndex, int targetLanguageIndex,
            boolean autoTranslatePair, boolean shouldShowNeverBar, String[] languages) {
        super(null, BACKGROUND_TYPE_INFO,
                R.drawable.infobar_translate);
        mTranslateDelegate = delegate;
        mOptions = new TranslateOptions(sourceLanguageIndex, targetLanguageIndex, languages,
                autoTranslatePair);
        mInfoBarType = infoBarType;
        mShouldShowNeverBar = shouldShowNeverBar;
        mOptionsPanelViewType = NO_PANEL;
        setNativeInfoBar(nativeInfoBarPtr);
    }

    @Override
    public void onCloseButtonClicked() {
        if (getInfoBarType() == BEFORE_TRANSLATE_INFOBAR && mOptionsPanelViewType == NO_PANEL) {
            // Make it behave exactly as the Nope Button.
            onButtonClicked(false);
        } else {
            nativeOnCloseButtonClicked(mNativeInfoBarPtr);
        }
    }

    @Override
    public void onButtonClicked(boolean isPrimaryButton) {
        if (mSubPanel != null) {
            mSubPanel.onButtonClicked(isPrimaryButton);
            return;
        }

        int action = actionFor(isPrimaryButton);

        if (getInfoBarType() == BEFORE_TRANSLATE_INFOBAR && mOptionsPanelViewType == NO_PANEL
                && action == ACTION_TYPE_CANCEL && needsNeverPanel()) {
            // "Nope" was clicked and instead of dismissing we need to show
            // the extra never panel.
            swapPanel(NEVER_PANEL);
        } else {
            onTranslateInfoBarButtonClicked(action);
        }
    }

    /**
     * Based on the infobar and the button pressed figure out what action needs to happen.
     */
    private int actionFor(boolean isPrimaryButton) {
        int action = InfoBar.ACTION_TYPE_NONE;
        int infobarType = getInfoBarType();
        switch (infobarType) {
            case TranslateInfoBar.BEFORE_TRANSLATE_INFOBAR:
                action = isPrimaryButton
                        ? InfoBar.ACTION_TYPE_TRANSLATE : InfoBar.ACTION_TYPE_CANCEL;
                break;
            case TranslateInfoBar.AFTER_TRANSLATE_INFOBAR:
                if (!isPrimaryButton) {
                    action = InfoBar.ACTION_TYPE_TRANSLATE_SHOW_ORIGINAL;
                }
                break;
            case TranslateInfoBar.TRANSLATE_ERROR_INFOBAR:
                // retry
                action = InfoBar.ACTION_TYPE_TRANSLATE;
                break;
            default:
                break;
        }
        return action;
    }

    @Override
    public CharSequence getMessageText(Context context) {
        switch (getInfoBarType()) {
            case BEFORE_TRANSLATE_INFOBAR:
              String template = context.getString(R.string.translate_infobar_text);
              return formatBeforeInfoBarMessage(template, mOptions.sourceLanguage(),
                      mOptions.targetLanguage(), LANGUAGE_PANEL);
            case AFTER_TRANSLATE_INFOBAR:
                String translated = context.getString(
                        R.string.translate_infobar_translation_done, mOptions.targetLanguage());
                if (needsAlwaysPanel()) {
                    String moreOptions = context.getString(
                            R.string.translate_infobar_translation_more_options);
                    return formatAfterTranslateInfoBarMessage(translated, moreOptions,
                            ALWAYS_PANEL);
                } else {
                    return translated;
                }
            case TRANSLATING_INFOBAR:
                return context.getString(R.string.translate_infobar_translating,
                        mOptions.targetLanguage());
            default:
                return context.getString(R.string.translate_infobar_error);
        }
    }

    @Override
    public String getPrimaryButtonText(Context context) {
        switch (getInfoBarType()) {
            case BEFORE_TRANSLATE_INFOBAR:
                return context.getString(R.string.translate_button);
            case AFTER_TRANSLATE_INFOBAR:
                if (!needsAlwaysPanel()) {
                    return context.getString(R.string.translate_button_done);
                }
                return null;
            case TRANSLATE_ERROR_INFOBAR:
                return context.getString(R.string.translate_retry);
            default:
                return null; // no inner buttons on the remaining infobars
        }
    }

    @Override
    public String getSecondaryButtonText(Context context) {
        switch (getInfoBarType()) {
            case BEFORE_TRANSLATE_INFOBAR:
                return context.getString(R.string.translate_nope);
            case AFTER_TRANSLATE_INFOBAR:
                if (!needsAlwaysPanel()) {
                    return context.getString(R.string.translate_show_original);
                }
                return null;
            default:
                return null;
        }
    }

    @Override
    public void createContent(InfoBarLayout layout) {
        if (mOptionsPanelViewType == NO_PANEL) {
            mSubPanel = null;
        } else {
            mSubPanel = panelFor(mOptionsPanelViewType);
            if (mSubPanel != null) {
                mSubPanel.createContent(getContext(), layout);
            }
            return;
        }

        if (getInfoBarType() == AFTER_TRANSLATE_INFOBAR && !needsAlwaysPanel()) {
            // Long always translate version
            TranslateCheckBox checkBox = new TranslateCheckBox(mOptions, this);
            checkBox.createContent(getContext(), layout);
        }

        super.createContent(layout);
    }

    // SubPanelListener implementation
    @Override
    public void onPanelClosed(int action) {
        setControlsEnabled(false);
        if (mOptionsPanelViewType == LANGUAGE_PANEL) {
            // Close the sub panel and show the infobar again.
            mOptionsPanelViewType = NO_PANEL;
            updateViewForCurrentState(createView());
        } else {
            // Apply options and close the infobar.
            onTranslateInfoBarButtonClicked(action);
        }
    }

    private void onTranslateInfoBarButtonClicked(int action) {
        onOptionsChanged();

        // We need to re-check if the pointer is null now because applying options (like never
        // translate this site) can sometimes trigger closing the InfoBar.
        if (mNativeInfoBarPtr == 0) return;
        nativeOnButtonClicked(mNativeInfoBarPtr, action, "");
    }

    @Override
    public void setControlsEnabled(boolean state) {
        super.setControlsEnabled(state);

        // Handle the "Always Translate" checkbox.
        ContentWrapperView wrapper = getContentWrapper(false);
        if (wrapper != null) {
            CheckBox checkBox = (CheckBox) wrapper.findViewById(R.id.infobar_extra_check);
            if (checkBox != null) checkBox.setEnabled(state);
        }
    }

    @Override
    public void onOptionsChanged() {
        if (mNativeInfoBarPtr == 0) return;

        if (mOptions.optionsChanged()) {
            mTranslateDelegate.applyTranslateOptions(mNativeInfoBarPtr,
                    mOptions.sourceLanguageIndex(),
                    mOptions.targetLanguageIndex(),
                    mOptions.alwaysTranslateLanguageState(),
                    mOptions.neverTranslateLanguageState(),
                    mOptions.neverTranslateDomainState());
        }
    }

    private boolean needsNeverPanel() {
        return (getInfoBarType() == TranslateInfoBar.BEFORE_TRANSLATE_INFOBAR
                && mShouldShowNeverBar);
    }

    private boolean needsAlwaysPanel() {
        return (getInfoBarType() == TranslateInfoBar.AFTER_TRANSLATE_INFOBAR
                && mOptions.alwaysTranslateLanguageState() && !DeviceUtils.isTablet(getContext()));
    }

    /**
     * @param newPanel id of the new panel to swap in. Use NO_PANEL to
     *     simply remove the current panel.
     */
    private void swapPanel(int newPanel) {
        assert (newPanel >= NO_PANEL && newPanel < MAX_PANEL_INDEX);
        mOptionsPanelViewType = newPanel;
        updateViewForCurrentState(createView());
    }

    /**
     * @return a panel of the specified {@code type}
     */
    private TranslateSubPanel panelFor(int type) {
        assert (type >= NO_PANEL && type < MAX_PANEL_INDEX);
        switch (type) {
            case LANGUAGE_PANEL:
                return new TranslateLanguagePanel(this, mOptions);
            case NEVER_PANEL:
                return new TranslateNeverPanel(this, mOptions);
            case ALWAYS_PANEL:
                return new TranslateAlwaysPanel(this, mOptions);
            default:
                return null;
        }
    }

    /**
     * Swaps out the current view in the ContentViewWrapper.
     */
    private void updateViewForCurrentState(View replacement) {
        setControlsEnabled(false);
        getInfoBarContainer().swapInfoBarViews(this, replacement);
    }

    /**
     * @return a formatted message with links to {@code panelId}.
     */
    private CharSequence formatBeforeInfoBarMessage(String template, String sourceLanguage,
            String targetLanguage, final int panelId) {

        SpannableString formattedSourceLanguage = new SpannableString(sourceLanguage);
        formattedSourceLanguage.setSpan(new ClickableSpan() {
                @Override
                public void onClick(View view) {
                    swapPanel(panelId);
                }
        }, 0, sourceLanguage.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

        SpannableString formattedTargetLanguage = new SpannableString(targetLanguage);
        formattedTargetLanguage.setSpan(new ClickableSpan() {
                @Override
                public void onClick(View view) {
                    swapPanel(panelId);
                }
        }, 0, targetLanguage.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

        return TextUtils.expandTemplate(template, formattedSourceLanguage, formattedTargetLanguage);
    }

    /**
     * @return a formatted message with a link to {@code panelId}
     */
    private CharSequence formatAfterTranslateInfoBarMessage(String statement, String linkText,
            final int panelId) {
        SpannableStringBuilder result = new SpannableStringBuilder();
        result.append(statement).append(" ");
        SpannableString formattedChange = new SpannableString(linkText);
        formattedChange.setSpan(new ClickableSpan() {
                @Override
                public void onClick(View view) {
                    swapPanel(panelId);
                }
        }, 0, linkText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        result.append(formattedChange);
        return result;
    }

    int getInfoBarType() {
        return mInfoBarType;
    }

    void changeInfoBarTypeAndNativePointer(int infoBarType, int newNativePointer) {
        if (infoBarType >= 0 && infoBarType < MAX_INFOBAR_INDEX) {
            mInfoBarType = infoBarType;
            replaceNativePointer(newNativePointer);
            updateViewForCurrentState(createView());
        } else {
            assert false : "Trying to change the InfoBar to a type that is invalid.";
        }
    }
}