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

import android.text.TextUtils;
import android.text.util.Rfc822Token;
import android.text.util.Rfc822Tokenizer;
import android.widget.AutoCompleteTextView;

import java.util.regex.Pattern;

/**
 * This class works as a Validator for AutoCompleteTextView for
 * email addresses.  If a token does not appear to be a valid address,
 * it is trimmed of characters that cannot legitimately appear in one
 * and has the specified domain name added.  It is meant for use with
 * {@link Rfc822Token} and {@link Rfc822Tokenizer}.
 *
 * @deprecated In the future make sure we don't quietly alter the user's
 *             text in ways they did not intend.  Meanwhile, hide this
 *             class from the public API because it does not even have
 *             a full understanding of the syntax it claims to correct.
 * @hide
 */
@Deprecated
public class Rfc822Validator implements AutoCompleteTextView.Validator {
    /*
     * Regex.EMAIL_ADDRESS_PATTERN hardcodes the TLD that we accept, but we
     * want to make sure we will keep accepting email addresses with TLD's
     * that don't exist at the time of this writing, so this regexp relaxes
     * that constraint by accepting any kind of top level domain, not just
     * ".com", ".fr", etc...
     */
    private static final Pattern EMAIL_ADDRESS_PATTERN =
            Pattern.compile("[^\\s@]+@([^\\s@\\.]+\\.)+[a-zA-z][a-zA-Z][a-zA-Z]*");

    private String mDomain;
    private boolean mRemoveInvalid = false;

    /**
     * Constructs a new validator that uses the specified domain name as
     * the default when none is specified.
     */
    public Rfc822Validator(String domain) {
        mDomain = domain;
    }

    /**
     * {@inheritDoc}
     */
    public boolean isValid(CharSequence text) {
        Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(text);

        return tokens.length == 1 &&
               EMAIL_ADDRESS_PATTERN.
                   matcher(tokens[0].getAddress()).matches();
    }

    /**
     * Specify if the validator should remove invalid tokens instead of trying
     * to fix them. This can be used to strip results of incorrectly formatted
     * tokens.
     *
     * @param remove true to remove tokens with the wrong format, false to
     *            attempt to fix them
     */
    public void setRemoveInvalid(boolean remove) {
        mRemoveInvalid = remove;
    }

    /**
     * @return a string in which all the characters that are illegal for the username
     * or the domain name part of the email address have been removed.
     */
    private String removeIllegalCharacters(String s) {
        StringBuilder result = new StringBuilder();
        int length = s.length();
        for (int i = 0; i < length; i++) {
            char c = s.charAt(i);

            /*
             * An RFC822 atom can contain any ASCII printing character
             * except for periods and any of the following punctuation.
             * A local-part can contain multiple atoms, concatenated by
             * periods, so do allow periods here.
             */

            if (c <= ' ' || c > '~') {
                continue;
            }

            if (c == '(' || c == ')' || c == '<' || c == '>' ||
                c == '@' || c == ',' || c == ';' || c == ':' ||
                c == '\\' || c == '"' || c == '[' || c == ']') {
                continue;
            }

            result.append(c);
        }
        return result.toString();
    }

    /**
     * {@inheritDoc}
     */
    public CharSequence fixText(CharSequence cs) {
        // Return an empty string if the email address only contains spaces, \n or \t
        if (TextUtils.getTrimmedLength(cs) == 0) return "";

        Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(cs);
        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < tokens.length; i++) {
            String text = tokens[i].getAddress();

            if (mRemoveInvalid && !isValid(text)) {
                continue;
            }
            int index = text.indexOf('@');
            if (index < 0) {
                // append the domain of the account if it exists
                if (mDomain != null) {
                    tokens[i].setAddress(removeIllegalCharacters(text) + "@" + mDomain);
                }
            } else {
                // Otherwise, remove the illegal characters on both sides of the '@'
                String fix = removeIllegalCharacters(text.substring(0, index));
                if (TextUtils.isEmpty(fix)) {
                    // if the address is empty after removing invalid chars
                    // don't use it
                    continue;
                }
                String domain = removeIllegalCharacters(text.substring(index + 1));
                boolean emptyDomain = domain.length() == 0;
                if (!emptyDomain || mDomain != null) {
                    tokens[i].setAddress(fix + "@" + (!emptyDomain ? domain : mDomain));
                }
            }

            sb.append(tokens[i].toString());
            if (i + 1 < tokens.length) {
                sb.append(", ");
            }
        }

        return sb;
    }
}
