package wayerr.reflect;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;

final class ParameterizedTypeImpl implements ParameterizedType {

    private final Class rawType;
    private final Type[] actualTypeArguments;
    private final Type ownerType;

    public ParameterizedTypeImpl(Class rawType, Type[] actualTypeArguments, Type ownerType) {
        this.rawType = rawType;
        this.actualTypeArguments = actualTypeArguments;
        if(ownerType != null) {
            this.ownerType = ownerType;
        } else {
            this.ownerType = this.rawType.getDeclaringClass();
        }
    }

    @Override
    public Type getRawType() {
        return rawType;
    }

    @Override
    public Type[] getActualTypeArguments() {
        return actualTypeArguments.clone();
    }

    @Override
    public Type getOwnerType() {
        return ownerType;
    }

    @Override
    public boolean equals(Object o) {
        if(!(o instanceof ParameterizedType)) {
            return false;
        } else {
            // Check that information is equivalent
            ParameterizedType that = (ParameterizedType)o;
            if(this == that) {
                return true;
            }

            Type thatOwner = that.getOwnerType();
            Type thatRawType = that.getRawType();

            return (ownerType == null ? thatOwner == null : ownerType.equals(thatOwner)) && (rawType == null ? thatRawType == null : rawType.equals(thatRawType)) && Arrays.equals(actualTypeArguments, that.getActualTypeArguments());
        }
    }

    @Override
    public int hashCode() {
        return Arrays.hashCode(actualTypeArguments) ^ (ownerType == null ? 0 : ownerType.hashCode()) ^ (rawType == null ? 0 : rawType.hashCode());
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();

        if(ownerType != null) {
            if(ownerType instanceof Class) {
                sb.append(((Class)ownerType).getName());
            } else {
                sb.append(ownerType.toString());
            }

            sb.append(".");

            if(ownerType instanceof ParameterizedTypeImpl) {
        // Find simple name of nested type by removing the
                // shared prefix with owner.
                sb.append(rawType.getName().replace(((ParameterizedTypeImpl)ownerType).rawType.getName() + "$",
                        ""));
            } else {
                sb.append(rawType.getName());
            }
        } else {
            sb.append(rawType.getName());
        }

        if(actualTypeArguments != null
                && actualTypeArguments.length > 0) {
            sb.append("<");
            boolean first = true;
            for(Type t : actualTypeArguments) {
                if(!first) {
                    sb.append(", ");
                }
                if(t instanceof Class) {
                    sb.append(((Class)t).getName());
                } else {
                    sb.append(t.toString());
                }
                first = false;
            }
            sb.append(">");
        }

        return sb.toString();
    }
}
