package com.abusalimov.mrcalc.compile.type;

import org.apache.commons.lang3.StringUtils;

import java.util.Objects;

/**
 * Represents a sequence type of certain depth (dimension).
 *
 * @author Eldar Abusalimov
 */
public class SequenceType implements Type {
    private final PrimitiveType primitiveType;
    private final int sequenceDepth;

    public SequenceType(PrimitiveType primitiveType) {
        this(primitiveType, 1);
    }

    /**
     * Creates a new instance with the specified underlying primitiveType at the given depth.
     *
     * @param primitiveType     the underlying primitiveType
     * @param sequenceDepth the sequence dimension
     */
    private SequenceType(PrimitiveType primitiveType, int sequenceDepth) {
        if (sequenceDepth < 1) {
            throw new IllegalArgumentException("Sequence depth must be positive");
        }
        this.primitiveType = Objects.requireNonNull(primitiveType);
        this.sequenceDepth = sequenceDepth;
    }

    /**
     * Creates a new instance representing a sequence of elements of the given type.
     *
     * @param type the element type
     * @return the new {@link SequenceType} instance
     */
    public static SequenceType of(Type type) {
        int sequenceDepth = (type instanceof SequenceType) ? ((SequenceType) type).getSequenceDepth() : 0;
        return new SequenceType(type.getPrimitiveType(), sequenceDepth + 1);
    }

    public Type getElementType() {
        if (sequenceDepth == 1) {
            return getPrimitiveType();
        } else {
            return new SequenceType(primitiveType, sequenceDepth - 1);
        }
    }

    public PrimitiveType getPrimitiveType() {
        return primitiveType;
    }

    @Override
    public Class<?> getTypeClass() {
        return Object.class;
    }

    public int getSequenceDepth() {
        return sequenceDepth;
    }

    @Override
    public int hashCode() {
        return Objects.hash(primitiveType, sequenceDepth);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof SequenceType)) {
            return false;
        }
        SequenceType another = (SequenceType) obj;
        return (this.primitiveType == another.primitiveType &&
                this.sequenceDepth == another.sequenceDepth);
    }

    @Override
    public String toString() {
        return primitiveType.toString() + StringUtils.repeat("[]", sequenceDepth);
    }
}

@@ -38,16 +38,63 @@
 .type;%0A%0A
+import com.abusalimov.mrcalc.runtime.Sequence;%0A
 import o
@@ -390,102 +390,8 @@
 h;%0A%0A
-    public SequenceType(PrimitiveType primitiveType) %7B%0A        this(primitiveType, 1);%0A    %7D%0A%0A
     
@@ -523,20 +523,16 @@
 iveType 
-    
 the unde
@@ -1725,28 +1725,341 @@
     
-return Object.class;
+if (sequenceDepth != 1) %7B%0A            return Sequence.class;%0A        %7D%0A        switch (primitiveType) %7B%0A            case INTEGER:%0A                return Sequence.OfLong.class;%0A            case FLOAT:%0A                return Sequence.OfDouble.class;%0A            case UNKNOWN:%0A            default:%0A                return null;%0A        %7D
 %0A   

Make SequenceType.getTypeClass() return proper runtime type