package carray.types;

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

/**
 * A {@code CArrayArrayType} represents an array of base Java types in the
 * carray extension.
 */
public class CArrayArrayType_c extends ArrayType_c implements CArrayArrayType {
    private static final long serialVersionUID = SerialVersionUID.generate();

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

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

    @Override
    public boolean equalsImpl(TypeObject t) {
        if (t instanceof CArrayArrayType) {
            CArrayArrayType ca = (CArrayArrayType) t;
            // Two ArrayType 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 CArrayArrayType) {
            CArrayArrayType a = (CArrayArrayType) t;
            // Two ArrayType 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 CArrayArrayType) {
            // ?1[] = ?2[]
            // Non-const arrays are invariant, so we need to check that ?1 = ?2.
            return ts.typeEquals(base, toType.toArray().base());
        }

        // From this point, toType is a const array.
        // ?1 const[] = ?2[]
        // We can assign a non-const array to a const array only if ?2 is a
        // subtype of ?1. Java arrays have this semantics, so let the base
        // language deal with this.
        return super.isImplicitCastValidImpl(toType);
    }

    @Override
    public boolean isCastValidImpl(Type toType) {
        if (!toType.isArray()) {
            // (?1) ?2[]
            // This 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 CArrayArrayType) {
            // (?1[]) ?2[]
            // Non-const arrays are invariant, so we need to check that ?1 = ?2.
            return ts.typeEquals(base, toType.toArray().base());
        }

        // From this point, toType is a const array.
        // (?1 const[]) ?2[]
        // We can cast a non-const array to a const array only if ?2 can be cast
        // to ?1. Java arrays have this semantics, so let the base
        // language deal with this.
        return super.isCastValidImpl(toType);
    }
}
