package de.renew.formalism.java;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Represents a suggestion for using a specific field of a class.
 */
public class FieldSuggestion extends Suggestion {

    /**
     * Logger for debugging field suggestions.
     */
    public static final org.apache.log4j.Logger LOGGER =
        org.apache.log4j.Logger.getLogger(FieldSuggestion.class);
    private final String _attemptedName;
    private Field _field;

    /**
     * Finds public fields in a class that match the given name and modifier.
     *
     * @param clazz the class to check
     * @param name the name to match
     * @param modifier required modifiers
     * @return a list of matching field suggestions
     */
    public static List<FieldSuggestion> suggest(Class<?> clazz, String name, int modifier) {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug(JavaHelper.class.getName() + ": Class = " + clazz.toString());
        }

        List<Field> resultFieldList = new ArrayList<Field>();

        Field[] fields = clazz.getFields();
        if (fields.length != 0) {
            String fieldPattern = name;

            boolean filter = false;

            // do filter if field name suffixes with "_"
            if (fieldPattern.endsWith("_")) {
                fieldPattern = fieldPattern.substring(0, fieldPattern.length() - 1);
                filter = true;
            }

            // do filtering when number of fields is large
            if (fields.length > 20) {
                filter = true;
            }

            // force no filtering by typing only "_"
            if (name.equals("_")) {
                filter = false;
            }

            for (Field field : fields) {
                int mod = field.getModifiers();
                String fieldName = field.getName();
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Name " + fieldName + " pattern: " + fieldPattern);
                    LOGGER.debug(
                        JavaHelper.class.getName() + ": modifier for " + field.getName() + "= "
                            + mod + " " + !filter + fieldName.startsWith(fieldPattern));
                }
                if (!filter || fieldName.startsWith(fieldPattern)) {
                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug(
                            JavaHelper.class.getName() + ": passed filter "
                                + ((modifier & mod) != 0));
                    }
                    if ((modifier & mod) != 0) {
                        resultFieldList.add(field);
                    }
                }
            }
        }

        List<FieldSuggestion> result = new ArrayList<FieldSuggestion>();
        for (Field field : resultFieldList) {
            result.add(new FieldSuggestion(field, name));
        }
        Collections.sort(result);

        return result;
    }

    /**
     * Creates a new suggestion for the given field and attempted name.
     *
     * @param field the given field
     * @param attemptedName the name that was attempted
     */
    public FieldSuggestion(Field field, String attemptedName) {
        super(field.getName(), field.getType().getSimpleName());
        this._field = field;
        this._attemptedName = attemptedName;
    }

    /**
     * Gets the name of the class where the field is defined.
     *
     * @return the class name
     */
    public String getDeclaringClassName() {
        return _field.getDeclaringClass().getSimpleName();
    }

    /**
     * Returns the field this suggestion is based on.
     *
     * @return the field
     */
    public Field getField() {
        return _field;
    }

    /**
     * Returns the name that was used to attempt accessing the field.
     *
     * @return the attempted field name
     */
    public String getAttemptedName() {
        return _attemptedName;
    }

    @Override
    public String toString() {
        return "<html>" + getName() + " : " + getTypeName() + " <font color=gray>- "
            + getDeclaringClassName() + "</font></html>";
    }
}