package de.uni_hamburg.fs;

import collections.CollectionEnumeration;

import de.renew.util.Types;


/**
 * Abstract base class representing a Java class type in the feature structure system.
 * This class provides functionality to handle Java classes as types, including support
 * for primitive types and their wrapper classes.
 */
public abstract class JavaClassType implements JavaType, ParsedType {

    /**
     * The Java class type represented by this instance.
     * For primitive types, this holds the corresponding wrapper class.
     */
    private Class<?> _type;

    /**
     * Indicates whether this type represents a primitive Java type.
     * When true, the _type field contains the wrapper class for the primitive type.
     */
    private boolean _isPrimitive;

    /**
     * Creates a new JavaClassType instance with no initial type.
     * This constructor is protected to allow initialization by subclasses.
     */
    protected JavaClassType() {}

    /**
     * Creates a new JavaClassType instance with the specified Java class type.
     * @param type the Java class to initialize this type with
     */
    protected JavaClassType(Class<?> type) {
        setJavaClass(type);
    }

    /**
     * Sets the Java class for this type.
     * If the class is primitive, it is converted to its wrapper type.
     * @param type the Java class to set
     */
    void setJavaClass(Class<?> type) {
        _isPrimitive = type.isPrimitive();
        if (_isPrimitive) {
            this._type = Types.objectify(type);
        } else {
            this._type = type;
        }
    }

    /**
     * Returns the Java class associated with this type.
     * If this type represents a primitive type, returns the primitive class
     * rather than its wrapper class.
     * @return the Java class representing this type
     */
    public Class<?> getJavaClass() {
        if (_isPrimitive) {
            return Types.typify(_type);
        } else {
            return _type;
        }
    }

    /**
     * Return whether the feature {@literal <feature>} is appropriate in this Type.
     */
    @Override
    public boolean isApprop(Name featureName) {
        return false;
    }

    /**
     * Return the required Type of the Value found under the given feature.
     * The feature has to be appropriate for this Type.
     */
    @Override
    public Type appropType(Name featureName) throws NoSuchFeatureException {
        throw new NoSuchFeatureException(featureName, this);
    }

    /**
     * Return an Enumeration of all appropriate features.
     */
    @Override
    public CollectionEnumeration appropFeatureNames() {
        return EmptyEnumeration.INSTANCE;
    }

    /**
     * Return a new node from this type.
     */
    @Override
    public Node newNode() {
        return new NoFeatureNode(this);
    }

    @Override
    public String toString() {
        return getName();
    }

    /**
     * Compares this <code>JavaClassType</code> object to the specified
     * <code>Object</code>.
     * <p>
     * TODO: It seems that this implementation is overridden by all known
     * subclasses. Does this equals/hashCode pair neverlethess define a
     * valid equivalence relation?
     * </p>
     * @param that the <code>Object</code> to compare.
     * @return <code>true</code> if the argument is a <code>JavaClassType</code>
     * instance and encapsulates the identical Java type as this instance.
     **/
    @Override
    public boolean equals(Object that) {
        if (that instanceof JavaClassType) {
            return ((JavaClassType) that).getJavaClass() == getJavaClass();
        }
        return false;
    }

    /**
     * Returns a hashcode for this <code>JavaClassType</code>.
     * <p>
     * TODO: It seems that this implementation is overridden by all known
     * subclasses. Does this equals/hashCode pair neverlethess define a
     * valid equivalence relation?
     * </p>
     * @return a hashcode value for this <code>JavaClassType</code>.
     **/
    @Override
    public int hashCode() {
        return getJavaClass().hashCode();
    }

    @Override
    public ParsedType unite(ParsedType that) throws UnificationFailure {
        if (that.equals(ParsedType.PARSED_TOP)) {
            return this;
        }
        if (that instanceof BasicType) {
            return (BasicType) unify((BasicType) that);
        }
        throw new UnificationFailure();
    }

    @Override
    public Type asType() throws UnificationFailure {
        return this;
    }
}