package ru.yandex.direct.http.smart.reflection;

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

import javax.annotation.Nullable;

import static com.google.common.base.Preconditions.checkNotNull;
import static ru.yandex.direct.http.smart.reflection.Utils.checkNotPrimitive;

final class ParameterizedTypeImpl implements ParameterizedType {
    private final Type ownerType;
    private final Type rawType;
    private final Type[] typeArguments;

    ParameterizedTypeImpl(@Nullable Type ownerType, Type rawType, Type... typeArguments) {
        if (rawType instanceof Class<?>
                && (ownerType == null) != (((Class<?>) rawType).getEnclosingClass() == null)) {
            throw new IllegalArgumentException();
        }

        for (Type typeArgument : typeArguments) {
            checkNotNull(typeArgument, "typeArgument == null");
            checkNotPrimitive(typeArgument);
        }

        this.ownerType = ownerType;
        this.rawType = rawType;
        this.typeArguments = typeArguments.clone();
    }

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

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

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

    @Override
    public boolean equals(Object other) {
        return other instanceof ParameterizedType && Utils.equals(this, (ParameterizedType) other);
    }

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

    @Override
    public String toString() {
        if (typeArguments.length == 0) {
            return Utils.typeToString(rawType);
        }
        StringBuilder result = new StringBuilder(30 * (typeArguments.length + 1));
        result.append(Utils.typeToString(rawType));
        result.append("<").append(Utils.typeToString(typeArguments[0]));
        for (int i = 1; i < typeArguments.length; i++) {
            result.append(", ").append(Utils.typeToString(typeArguments[i]));
        }
        return result.append(">").toString();
    }
}
