package carray.types;

import polyglot.types.ArrayType_c;
import polyglot.types.Resolver;
import polyglot.types.Type;
import polyglot.types.TypeObject;
import polyglot.types.TypeSystem;
import polyglot.util.Position;
import polyglot.util.SerialVersionUID;

/**
 * A {@code ConstArrayType} represents an array of base java types,
 * whose elements cannot change after initialization.
 */
public class ConstArrayType_c extends ArrayType_c implements ConstArrayType {
    private static final long serialVersionUID = SerialVersionUID.generate();

    /** Used for deserializing types. */
    protected ConstArrayType_c() {
    }

    public ConstArrayType_c(TypeSystem ts, Position pos, Type base) {
        super(ts, pos, base);
    }

    @Override
    public boolean equalsImpl(TypeObject t) {
        if (t instanceof ConstArrayType) {
            ConstArrayType ca = (ConstArrayType) t;
            // Two ConstArrayType objects are equal if their bases are equal.
            return ts.equals(base(), ca.base());
        }
        return false;
    }

    @Override
    public boolean typeEqualsImpl(Type t) {
        if (t instanceof ConstArrayType) {
            ConstArrayType a = (ConstArrayType) t;
            // Two ConstArrayType objects are equal if their bases are equal.
            return ts.typeEquals(base(), a.base());
        }
        return false;
    }

    @Override
    public boolean isImplicitCastValidImpl(Type toType) {
        if (!toType.isArray()) {
            // ?1 = ?2 const[]
            // This const array type is assignable to ?1 only if ?1 is Object.
            // Let the base language check this fact.
            return super.isImplicitCastValidImpl(toType);
        }

        // From this point, toType is an array.
        if (toType instanceof ConstArrayType) {
            // ?1 const[] = ?2 const[]
            // Let the base language check whether ?2 is assignable to ?1.
            return super.isImplicitCastValidImpl(toType);
        }

        // From this point, toType is a non-const array.
        // ?1[] = ?2 const[]
        // We cannot assign a const array to a non-const array.
        return false;
    }

    @Override
    public boolean isCastValidImpl(Type toType) {
        if (!toType.isArray()) {
            // (?1) ?2 const[]
            // This const array type can be cast to ?1 only if ?1 is Object.
            // Let the base language check this fact.
            return super.isCastValidImpl(toType);
        }

        // From this point, toType is an array.
        if (toType instanceof ConstArrayType) {
            // (?1 const[]) ?2 const[]
            // Let the base language check whether ?2 can be cast to ?1.
            return super.isCastValidImpl(toType);
        }

        // From this point, toType is a non-const array.
        // (?1[]) ?2 const[]
        // We cannot cast a const array to a non-const array.
        return false;
    }

    @Override
    public String translate(Resolver c) {
        String result = base.translate(c);
        if (base instanceof ConstArrayType) {
            // If base is also a ConstArrayType, the keyword "const" would
            // have been displayed, so just add additional dimension here.
            result += "[]";
        }
        else result += " const[]";
        return result;
    }

    @Override
    public String toString() {
        String result = base.toString();
        if (base instanceof ConstArrayType) {
            // If base is also a ConstArrayType, the keyword "const" would
            // have been displayed, so just add additional dimension here.
            result += "[]";
        }
        else result += " const[]";
        return result;
    }
}
