package de.uni_hamburg.fs;

/**
 * Represents a parsed list type that can be either a regular list or a non-empty list.
 * This class implements ParsedType interface and provides functionality to handle
 * list types before they are compiled into their final form.
 */
public class ParsedListType implements ParsedType {

    /**
     * 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.
     */
    /**
     * Cached "real" type after compilation. This field serves as a cache to store
     * the compiled type once it has been created through asType() method.
     */
    private Type _type = null;

    /**
     * Indicates whether this list type must contain at least one element.
     * If true, the list is a non-empty list type; if false, it's a regular list that can be empty.
     */
    private boolean _atLeastOne;

    /**
     * The parsed type of elements contained in this list.
     * Represents the type constraint for elements that can be stored in this list.
     */
    private ParsedType _parsedType;

    /**
     * Constructs a new {@code ParsedListType} instance.
     *
     * @param atLeastOne1 a boolean indicating whether at least one element is required in the list.
     * If {@code true}, the list must contain at least one element.
     * @param parsedType1 the {@link ParsedType} object representing the type of elements in this list.
     * This defines the structure or type constraints of the list elements.
     */
    public ParsedListType(boolean atLeastOne1, ParsedType parsedType1) {
        this._atLeastOne = atLeastOne1;
        this._parsedType = parsedType1;
    }

    @Override
    public ParsedType unite(ParsedType that) throws UnificationFailure {
        if (that instanceof ParsedListType) {
            ParsedListType thatPLT = (ParsedListType) that;
            return new ParsedListType(
                _atLeastOne || thatPLT._atLeastOne, _parsedType.unite(thatPLT._parsedType));
        }
        throw new UnificationFailure();
    }

    @Override
    public Type asType() throws UnificationFailure {
        if (_type == null) {
            int subtype = _atLeastOne ? ListType.NELIST : ListType.LIST;
            _type = new ListType(_parsedType.asType(), subtype);
        }
        return _type;
    }

    @Override
    public boolean equals(Object that) {
        if (that instanceof ParsedListType) {
            ParsedListType thatPLT = (ParsedListType) that;
            return _atLeastOne == thatPLT._atLeastOne && _parsedType.equals(thatPLT._parsedType);
        }
        return false;
    }

    @Override
    public int hashCode() {
        return (_atLeastOne ? 1 : 0) + _parsedType.hashCode();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        final int sbSize = 1000;
        final String variableSeparator = ", ";
        final StringBuffer sb = new StringBuffer(sbSize);
        sb.append("ParsedListType(");
        sb.append("type=").append(_type);
        sb.append(variableSeparator);
        sb.append("atLeastOne=").append(_atLeastOne);
        sb.append(")");
        return sb.toString();
    }
}