package de.uni_hamburg.fs;

/**
 * Represents a parsed conjunctive type that implements ParsedType interface.
 * A conjunctive type is composed of a set of concepts and maintains flags for restriction
 * and printing behavior.
 */
public class ParsedConjunctiveType implements ParsedType {

    /** Constant representing the basic String type. */
    private static final Type STRING_TYPE = new BasicType(String.class);

    /**
     * Cached compiled type. A ParsedType is a data structure for a type that has not yet
     * been compiled into a "real" type. The method asType() does
     * that and returns the "real" type.
     */
    private Type _type = null; // cache

    /** Set of concepts that define this conjunctive type. */
    private ConceptSet _concepts = null;

    /** Flag indicating whether this type is restricted in terms of allowed features. */
    private boolean _restricted = true;

    /** Flag controlling whether ANY type should be printed in string representation. */
    private boolean _printAny = true;

    /**
     * Constructs a ParsedConjunctiveType with a single concept.
     *
     * @param concept the concept to initialize this type with
     */
    public ParsedConjunctiveType(Concept concept) {
        this(new ConceptSet(concept));
    }

    /**
     * Constructs a ParsedConjunctiveType with a set of concepts.
     *
     * @param concepts the set of concepts to initialize this type with
     */
    public ParsedConjunctiveType(ConceptSet concepts) {
        this._concepts = concepts;
    }

    /**
     * Constructs a ParsedConjunctiveType with a set of concepts and restriction flag.
     *
     * @param concepts the set of concepts to initialize this type with
     * @param restricted flag indicating if the type is restricted
     */
    public ParsedConjunctiveType(ConceptSet concepts, boolean restricted) {
        this(concepts);
        this._restricted = restricted;
    }

    /**
     * Constructs a ParsedConjunctiveType with a set of concepts, restriction flag, and dummy type flag.
     *
     * @param concepts the set of concepts to initialize this type with
     * @param restricted flag indicating if the type is restricted
     * @param noDummyType flag controlling dummy type behavior and printing
     */
    public ParsedConjunctiveType(ConceptSet concepts, boolean restricted, boolean noDummyType) {
        this(concepts, noDummyType && restricted);
        this._printAny = noDummyType || !restricted;
    }

    @Override
    public ParsedType unite(ParsedType that) throws UnificationFailure {
        if (that instanceof ParsedConjunctiveType) {
            ParsedConjunctiveType thatPCT = (ParsedConjunctiveType) that;
            ConceptSet united = new ConceptSet(_concepts);
            united.unite(thatPCT._concepts);
            return new ParsedConjunctiveType(
                united, _restricted && thatPCT._restricted, _printAny || thatPCT._printAny);
        }
        throw new UnificationFailure();
    }

    @Override
    public Type asType() throws UnificationFailure {
        if (_type == null) {
            if (_concepts.equals(new ConceptSet(TypeSystem.instance().getJavaConcept(String.class)))
                && _restricted) {
                _type = STRING_TYPE;
            } else {
                _type = new ConjunctiveType(_concepts, _restricted, _printAny);
            }
        }
        return _type;
    }

    @Override
    public boolean equals(Object that) {
        if (that instanceof ParsedConjunctiveType) {
            //logger.debug("Hashcodes: this.concepts: "+hashCode()
            //+" that.concepts: "+that.hashCode());
            ParsedConjunctiveType thatPT = (ParsedConjunctiveType) that;
            return _restricted == thatPT._restricted && _printAny == thatPT._printAny
                && _concepts.equals(thatPT._concepts);
        }
        return false;
    }

    @Override
    public int hashCode() {
        return _concepts.hashCode() + (_restricted ? 0 : 999);
    }

    @Override
    public String toString() {
        return ConjunctiveType.typeToString(_restricted, _printAny, false, _concepts);
    }
}