/**
 * Copyright (c) 2011, Google Inc.
 *
 * 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.compose;

import android.content.Context;
import android.content.res.Resources;
import android.text.Html;
import android.text.SpannedString;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.LinearLayout;

import com.android.mail.R;
import com.android.mail.providers.Message;
import com.android.mail.utils.Utils;

import java.text.DateFormat;
import java.util.Date;

/*
 * View for displaying the quoted text in the compose screen for a reply
 * or forward. A close button is included in the upper right to remove
 * the quoted text from the message.
 */
class QuotedTextView extends LinearLayout implements OnClickListener {
    // HTML tags used to quote reply content
    // The following style must be in-sync with
    // pinto.app.MessageUtil.QUOTE_STYLE and
    // java/com/google/caribou/ui/pinto/modules/app/messageutil.js
    // BEG_QUOTE_BIDI is also available there when we support BIDI
    private static final String BLOCKQUOTE_BEGIN = "<blockquote class=\"quote\" style=\""
            + "margin:0 0 0 .8ex;" + "border-left:1px #ccc solid;" + "padding-left:1ex\">";
    private static final String BLOCKQUOTE_END = "</blockquote>";
    private static final String QUOTE_END = "</div>";

    // Separates the attribution headers (Subject, To, etc) from the body in
    // quoted text.
    private static final String HEADER_SEPARATOR = "<br type='attribution'>";
    private static final int HEADER_SEPARATOR_LENGTH = HEADER_SEPARATOR.length();

    private CharSequence mQuotedText;
    private WebView mQuotedTextWebView;
    private ShowHideQuotedTextListener mShowHideListener;
    private CheckBox mQuotedTextCheckBox;
    private boolean mIncludeText = true;
    private Button mRespondInlineButton;
    private RespondInlineListener mRespondInlineListener;
    private static String sQuoteBegin;

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

    public QuotedTextView(Context context, AttributeSet attrs) {
        this(context, attrs, -1);
    }

    public QuotedTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs);
        LayoutInflater factory = LayoutInflater.from(context);
        factory.inflate(R.layout.quoted_text, this);

        mQuotedTextWebView = (WebView) findViewById(R.id.quoted_text_web_view);
        Utils.restrictWebView(mQuotedTextWebView);
        WebSettings settings = mQuotedTextWebView.getSettings();
        settings.setBlockNetworkLoads(true);

        mQuotedTextCheckBox = (CheckBox) findViewById(R.id.hide_quoted_text);
        mQuotedTextCheckBox.setChecked(true);
        mQuotedTextCheckBox.setOnClickListener(this);
        sQuoteBegin = context.getResources().getString(R.string.quote_begin);


        mRespondInlineButton = (Button) findViewById(R.id.respond_inline_button);
        if (mRespondInlineButton != null) {
            mRespondInlineButton.setEnabled(false);
        }
    }

    public void onDestroy() {
        if (mQuotedTextWebView != null) {
            mQuotedTextWebView.destroy();
        }
    }

    /**
     * Allow the user to include quoted text.
     * @param allow
     */
    public void allowQuotedText(boolean allow) {
        if (mQuotedTextCheckBox != null) {
            mQuotedTextCheckBox.setVisibility(allow ? View.VISIBLE : View.INVISIBLE);
        }
    }

    /**
     * Allow the user to respond inline.
     * @param allow
     */
    public void allowRespondInline(boolean allow) {
        if (mRespondInlineButton != null) {
            mRespondInlineButton.setVisibility(allow? View.VISIBLE : View.GONE);
        }
    }

    /**
     * Returns the quoted text if the user hasn't dismissed it, otherwise
     * returns null.
     */
    public CharSequence getQuotedTextIfIncluded() {
        if (mIncludeText) {
            return mQuotedText;
        }
        return null;
    }

    /**
     * Always returns the quoted text.
     */
    public CharSequence getQuotedText() {
        return mQuotedText;
    }

    /**
     * @return whether or not the user has selected to include quoted text.
     */
    public boolean isTextIncluded() {
        return mIncludeText;
    }

    public void setShowHideListener(ShowHideQuotedTextListener listener) {
        mShowHideListener = listener;
    }


    public void setRespondInlineListener(RespondInlineListener listener) {
        mRespondInlineListener = listener;
    }

    @Override
    public void onClick(View v) {
        final int id = v.getId();

        if (id == R.id.respond_inline_button) {
            respondInline();
        } else if (id == R.id.hide_quoted_text) {
            updateCheckedState(mQuotedTextCheckBox.isChecked());
        }
    }

    /**
     * Update the state of the checkbox for the QuotedTextView as if it were
     * tapped by the user. Also updates the visibility of the QuotedText area.
     * @param checked Either true or false.
     */
    public void updateCheckedState(boolean checked) {
        mQuotedTextCheckBox.setChecked(checked);
        updateQuotedTextVisibility(checked);
        if (mShowHideListener != null) {
            mShowHideListener.onShowHideQuotedText(checked);
        }
    }

    private void updateQuotedTextVisibility(boolean show) {
        mQuotedTextWebView.setVisibility(show ? View.VISIBLE : View.GONE);
        mIncludeText = show;
    }

    private void populateData() {
        String fontColor = getContext().getResources().getString(
                R.string.quoted_text_font_color_string);
        String html = "<head><style type=\"text/css\">* body { color: " +
                fontColor + "; }</style></head>" + mQuotedText.toString();
        mQuotedTextWebView.loadDataWithBaseURL(null, html, "text/html", "utf-8", null);
    }

    private void respondInline() {
        // Copy the text in the quoted message to the body of the
        // message after stripping the html.
        final String plainText = Utils.convertHtmlToPlainText(getQuotedText().toString());
        if (mRespondInlineListener != null) {
            mRespondInlineListener.onRespondInline("\n" + plainText);
        }
        // Set quoted text to unchecked and not visible.
        updateCheckedState(false);
        mRespondInlineButton.setVisibility(View.GONE);
        // Hide everything to do with quoted text.
        View quotedTextView = findViewById(R.id.quoted_text_area);
        if (quotedTextView != null) {
            quotedTextView.setVisibility(View.GONE);
        }
    }

    /**
     * Interface for listeners that want to be notified when quoted text
     * is shown / hidden.
     */
    public interface ShowHideQuotedTextListener {
        public void onShowHideQuotedText(boolean show);
    }

    /**
     * Interface for listeners that want to be notified when the user
     * chooses to respond inline.
     */
    public interface RespondInlineListener {
        public void onRespondInline(String text);
    }

    private static String getHtmlText(Message message) {
        if (message.bodyHtml != null) {
            return message.bodyHtml;
        } else if (message.bodyText != null) {
            // STOPSHIP Sanitize this
            return Html.toHtml(new SpannedString(message.bodyText));
        } else {
            return "";
        }
    }

    public void setQuotedText(int action, Message refMessage, boolean allow) {
        setVisibility(View.VISIBLE);
        String htmlText = getHtmlText(refMessage);
        StringBuilder quotedText = new StringBuilder();
        DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT);
        Date date = new Date(refMessage.dateReceivedMs);
        Resources resources = getContext().getResources();
        if (action == ComposeActivity.REPLY || action == ComposeActivity.REPLY_ALL) {
            quotedText.append(sQuoteBegin);
            quotedText
                    .append(String.format(
                            resources.getString(R.string.reply_attribution),
                            dateFormat.format(date),
                            Utils.cleanUpString(
                                    refMessage.getFrom(), true)));
            quotedText.append(HEADER_SEPARATOR);
            quotedText.append(BLOCKQUOTE_BEGIN);
            quotedText.append(htmlText);
            quotedText.append(BLOCKQUOTE_END);
            quotedText.append(QUOTE_END);
        } else if (action == ComposeActivity.FORWARD) {
            quotedText.append(sQuoteBegin);
            quotedText
                    .append(String.format(resources.getString(R.string.forward_attribution), Utils
                            .cleanUpString(refMessage.getFrom(),
                                    true /* remove empty quotes */), dateFormat.format(date), Utils
                            .cleanUpString(refMessage.subject,
                                    false /* don't remove empty quotes */), Utils.cleanUpString(
                            refMessage.getTo(), true)));
            String ccAddresses = refMessage.getCc();
            quotedText.append(String.format(resources.getString(R.string.cc_attribution),
                    Utils.cleanUpString(ccAddresses, true /* remove empty quotes */)));
            quotedText.append(HEADER_SEPARATOR);
            quotedText.append(BLOCKQUOTE_BEGIN);
            quotedText.append(htmlText);
            quotedText.append(BLOCKQUOTE_END);
            quotedText.append(QUOTE_END);
        }
        setQuotedText(quotedText);
        allowQuotedText(allow);
        // If there is quoted text, we always allow respond inline, since this
        // may be a forward.
        allowRespondInline(true);
    }

    public void setQuotedTextFromDraft(CharSequence htmlText, boolean forward) {
        setVisibility(View.VISIBLE);
        setQuotedText(htmlText);
        allowQuotedText(!forward);
        // If there is quoted text, we always allow respond inline, since this
        // may be a forward.
        allowRespondInline(true);
    }

    public void setQuotedTextFromHtml(CharSequence htmlText, boolean shouldQuoteText) {
        setVisibility(VISIBLE);
        if (shouldQuoteText) {
            final StringBuilder quotedText = new StringBuilder();
            final Resources resources = getContext().getResources();
            quotedText.append(sQuoteBegin);
            quotedText.append(
                    String.format(resources.getString(R.string.forward_attribution_no_headers)));
            quotedText.append(HEADER_SEPARATOR);
            quotedText.append(BLOCKQUOTE_BEGIN);
            quotedText.append(htmlText);
            quotedText.append(BLOCKQUOTE_END);
            quotedText.append(QUOTE_END);
            setQuotedText(quotedText);
        } else {
            setQuotedText(htmlText);
        }
        findViewById(R.id.divider_bar).setVisibility(GONE);
        findViewById(R.id.quoted_text_button_bar).setVisibility(GONE);
    }
    /**
     * Set quoted text. Some use cases may not want to display the check box (i.e. forwarding) so
     * allow control of that.
     */
    private void setQuotedText(CharSequence quotedText) {
        mQuotedText = quotedText;
        populateData();
        if (mRespondInlineButton != null) {
            if (!TextUtils.isEmpty(quotedText)) {
                mRespondInlineButton.setVisibility(View.VISIBLE);
                mRespondInlineButton.setEnabled(true);
                mRespondInlineButton.setOnClickListener(this);
            } else {
                // No text to copy; disable the respond inline button.
                mRespondInlineButton.setVisibility(View.GONE);
                mRespondInlineButton.setEnabled(false);
            }
        }
    }

    public static boolean containsQuotedText(String text) {
        int pos = text.indexOf(sQuoteBegin);
        return pos >= 0;
    }

    /**
     * Returns the index of the actual quoted text and NOT the meta information such as:
     * "On April 4, 2013 Joe Smith <jsmith@example.com> wrote:" that is part of the original
     * message when replying and including the original text.
     * @param text HTML text that includes quoted text.
     * @return The offset found.
     */
    public static int getQuotedTextOffset(String text) {
        return text.indexOf(QuotedTextView.HEADER_SEPARATOR)
                + QuotedTextView.HEADER_SEPARATOR_LENGTH;
    }

    /**
     * Find the index of where the entire block of quoted text, quotes, divs,
     * attribution and all, begins.
     */
    public static int findQuotedTextIndex(CharSequence htmlText) {
        if (TextUtils.isEmpty(htmlText)) {
            return -1;
        }
        String textString = htmlText.toString();
        return textString.indexOf(sQuoteBegin);
    }

    public void setUpperDividerVisible(boolean visible) {
        findViewById(R.id.upper_quotedtext_divider_bar).setVisibility(
                visible ? View.VISIBLE : View.GONE);
    }
}
