package uk.me.nxg.unity;

import java.util.List;

/**
 * A description of the way that a unit is represented in a particular syntax.
 */
public class UnitRepresentation
        implements java.util.Iterator<UnitRepresentation>
//implements java.io.Serializable
{
    private final boolean mayHaveSIPrefixes;
    private final boolean mayHaveBinaryPrefixes;
    private final boolean isDeprecated;
    private final String abbreviation;
    // the next in a possible chain of representations
    private UnitRepresentation nextRep = null;

    private static final long serialVersionUID = 43L;

    UnitRepresentation(boolean mayHaveSIPrefixes,
                       boolean mayHaveBinaryPrefixes,
                       boolean isDeprecated,
                       String abbreviation) {
        if (abbreviation == null) {
            throw new IllegalArgumentException("a Representation must have a non-null abbreviation");
        }
        this.mayHaveSIPrefixes = mayHaveSIPrefixes;
        this.mayHaveBinaryPrefixes = mayHaveBinaryPrefixes;
        this.isDeprecated = isDeprecated;
        this.abbreviation = abbreviation;

        // class invariant
        assert abbreviation != null;
    }

    /**
     * Produce a compact string representation of the unit.
     * This is intended to be a human-readable unit
     * string, such as {@code "m"} for metres.
     * If, however, you wish to get a unit-string representation of
     * a unit expression, do not construct it yourself using this method, but
     * instead use {@link UnitExpr#toString}, the output of which may
     * differ from the output of this method in certain circumstances.
     */
    @Override public String toString() {
        return abbreviation;
    }

    /**
     * Produce a slightly more detailed representation of the object's fields.
     * This is mostly for debugging purposes, and the format of the
     * string is not necessarily stable.
     * @return a string with developer-ready extra information
     */
    public String toDebugString() {
        return abbreviation + "["
                + (mayHaveSIPrefixes ? "s" : "")
                + (mayHaveBinaryPrefixes ? "b" : "")
                + (isDeprecated ? "d" : "") + "]";
    }

    /**
     * Indicates whether this unit may be used with SI prefixes.
     * @return true if this unit may have SI prefixes
     */
    public boolean mayHaveSIPrefixes() {
        return mayHaveSIPrefixes;
    }

    /**
     * Indicates whether this unit may be used with binary prefixes.
     * @return true if this unit may have binary prefixes
     */
    public boolean mayHaveBinaryPrefixes() {
        return mayHaveBinaryPrefixes;
    }

    /**
     * Indicates whether this syntax deprecates (but still
     * permits) the use of this unit.
     * @return true if use of this unit is deprecated
     */
    public boolean isDeprecated() {
        return isDeprecated;
    }

    /**
     * Returns a symbolic abbreviation for this unit in this syntax.
     * For example, it might return "m" for Metres.
     * @return a string abbreviation for this unit
     */
    public String getAbbreviation() {
        return abbreviation;
    }

    /**
     * If there is more than one representation for this unit in this
     * syntax, then this will return the next one.
     * @return true if there is a further representation of this unit
     */
    @Override public boolean hasNext() {
        return nextRep != null;
    }

    /**
     * Returns true if <code>hasNext()</code> would succeed.
     * return true if hasNext() would succeed.
     */
    @Override public UnitRepresentation next() {
        return nextRep;
    }

    @Override public void remove() {
        throw new UnsupportedOperationException("Not supported in UnitRepresentation");
    }

    /**
     * Add a UnitRepresentation in the list just after this one.
     * It's OK if the current representation has a next – the new one is inserted into the list.
     * @param r the representation to be appended
     * @return true (as per the general contract of the Collection.add method).
     */
    boolean add(UnitRepresentation r) {
        if (nextRep != null) {
            // insert r in the list here
            r.add(nextRep);
            nextRep = r;
        } else {
            // this is the end of the list
            nextRep = r;
        }
        return true;
    }
}
