/*
 * 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 android.text.util;

import android.annotation.Nullable;

/**
 * This class stores an RFC 822-like name, address, and comment,
 * and provides methods to convert them to quoted strings.
 */
public class Rfc822Token {
    @Nullable
    private String mName, mAddress, mComment;

    /**
     * Creates a new Rfc822Token with the specified name, address,
     * and comment.
     */
    public Rfc822Token(@Nullable String name, @Nullable String address, @Nullable String comment) {
        mName = name;
        mAddress = address;
        mComment = comment;
    }

    /**
     * Returns the name part.
     */
    @Nullable
    public String getName() {
        return mName;
    }

    /**
     * Returns the address part.
     */
    @Nullable
    public String getAddress() {
        return mAddress;
    }

    /**
     * Returns the comment part.
     */
    @Nullable
    public String getComment() {
        return mComment;
    }

    /**
     * Changes the name to the specified name.
     */
    public void setName(@Nullable String name) {
        mName = name;
    }

    /**
     * Changes the address to the specified address.
     */
    public void setAddress(@Nullable String address) {
        mAddress = address;
    }

    /**
     * Changes the comment to the specified comment.
     */
    public void setComment(@Nullable String comment) {
        mComment = comment;
    }

    /**
     * Returns the name (with quoting added if necessary),
     * the comment (in parentheses), and the address (in angle brackets).
     * This should be suitable for inclusion in an RFC 822 address list.
     */
    public String toString() {
        StringBuilder sb = new StringBuilder();

        if (mName != null && mName.length() != 0) {
            sb.append(quoteNameIfNecessary(mName));
            sb.append(' ');
        }

        if (mComment != null && mComment.length() != 0) {
            sb.append('(');
            sb.append(quoteComment(mComment));
            sb.append(") ");
        }

        if (mAddress != null && mAddress.length() != 0) {
            sb.append('<');
            sb.append(mAddress);
            sb.append('>');
        }

        return sb.toString();
    }

    /**
     * Returns the name, conservatively quoting it if there are any
     * characters that are likely to cause trouble outside of a
     * quoted string, or returning it literally if it seems safe.
     */
    public static String quoteNameIfNecessary(String name) {
        int len = name.length();

        for (int i = 0; i < len; i++) {
            char c = name.charAt(i);

            if (! ((c >= 'A' && c <= 'Z') ||
                   (c >= 'a' && c <= 'z') ||
                   (c == ' ') ||
                   (c >= '0' && c <= '9'))) {
                return '"' + quoteName(name) + '"';
            }
        }

        return name;
    }

    /**
     * Returns the name, with internal backslashes and quotation marks
     * preceded by backslashes.  The outer quote marks themselves are not
     * added by this method.
     */
    public static String quoteName(String name) {
        StringBuilder sb = new StringBuilder();

        int len = name.length();
        for (int i = 0; i < len; i++) {
            char c = name.charAt(i);

            if (c == '\\' || c == '"') {
                sb.append('\\');
            }

            sb.append(c);
        }

        return sb.toString();
    }

    /**
     * Returns the comment, with internal backslashes and parentheses
     * preceded by backslashes.  The outer parentheses themselves are
     * not added by this method.
     */
    public static String quoteComment(String comment) {
        int len = comment.length();
        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < len; i++) {
            char c = comment.charAt(i);

            if (c == '(' || c == ')' || c == '\\') {
                sb.append('\\');
            }

            sb.append(c);
        }

        return sb.toString();
    }

    public int hashCode() {
        int result = 17;
        if (mName != null) result = 31 * result + mName.hashCode();
        if (mAddress != null) result = 31 * result + mAddress.hashCode();
        if (mComment != null) result = 31 * result + mComment.hashCode();
        return result;
    }

    private static boolean stringEquals(String a, String b) {
        if (a == null) {
            return (b == null);
        } else {
            return (a.equals(b));
        }
    }

    public boolean equals(@Nullable Object o) {
        if (!(o instanceof Rfc822Token)) {
            return false;
        }
        Rfc822Token other = (Rfc822Token) o;
        return (stringEquals(mName, other.mName) &&
                stringEquals(mAddress, other.mAddress) &&
                stringEquals(mComment, other.mComment));
    }
}

