package annotations.field;

/*>>>
import org.checkerframework.checker.nullness.qual.*;
*/

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

import com.google.common.escape.CharEscaperBuilder;
import com.google.common.escape.Escaper;

/**
 * A <code>BasicAFT</code> represents a primitive or {@link String} annotation
 * field type. Get one using {@link #forType(Class)}.
 */
// should be an enum except they can't be generic and can't extend a class
public final class BasicAFT extends ScalarAFT {
    static final Escaper charEscaper =
        new CharEscaperBuilder()
            .addEscape('\b', "\\b")
            .addEscape('\f', "\\f")
            .addEscape('\n', "\\n")
            .addEscape('\r', "\\r")
            .addEscape('\t', "\\t")
            .addEscape('\"', "\\\"")
            .addEscape('\\', "\\\\")
            .addEscape('\'', "\\'")
            .toEscaper();

    /**
     * The Java type backing this annotation field type.
     */
    public final Class<?> type;

    private BasicAFT(Class<?> type) {
        this.type = type;
    }

    /**
     * Returns the <code>BasicAFT</code> for <code>type</code>, which
     * should be primitive (e.g., int.class) or String.  Returns null if
     * <code>type</code> is not appropriate for a basic annotation field
     * type.
     */
    public static BasicAFT forType(Class<?> type) {
        return bafts.get(type);
    }

    /**
     * Maps from {@link #type} to <code>BasicAFT</code>.
     * Contains every BasicAFT.
     */
    // Disgusting reason for being public; need to fix.
    public static final Map<Class<?>, BasicAFT> bafts;

    static {
        Map<Class<?>, BasicAFT> tempBafts =
            new HashMap<Class<?>, BasicAFT>(9);
        tempBafts.put(byte.class, new BasicAFT(byte.class));
        tempBafts.put(short.class, new BasicAFT(short.class));
        tempBafts.put(int.class, new BasicAFT(int.class));
        tempBafts.put(long.class, new BasicAFT(long.class));
        tempBafts.put(float.class, new BasicAFT(float.class));
        tempBafts.put(double.class, new BasicAFT(double.class));
        tempBafts.put(char.class, new BasicAFT(char.class));
        tempBafts.put(boolean.class, new BasicAFT(boolean.class));
        tempBafts.put(String.class, new BasicAFT(String.class));
        // bafts = Collections2.<Class<?>, BasicAFT>unmodifiableKeyedSet(tempBafts);
        // bafts = bafts2;
        bafts = tempBafts;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isValidValue(Object o) {
        return (   (type == byte.class && o instanceof Byte)
                || (type == short.class && o instanceof Short)
                || (type == int.class && o instanceof Integer)
                || (type == long.class && o instanceof Long)
                || (type == float.class && o instanceof Float)
                || (type == double.class && o instanceof Double)
                || (type == char.class && o instanceof Character)
                || (type == boolean.class && o instanceof Boolean)
                || (type == String.class && o instanceof String));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        if (type == String.class) {
            return "String";
        } else {
            return type.getName();
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String format(Object o) {
      return type != String.class ? o.toString()
          : "\"" + charEscaper.escape((String) o) + "\"";
    }

    @Override
    public <R, T> R accept(AFTVisitor<R, T> v, T arg) {
        return v.visitBasicAFT(this, arg);
    }
}