// 
// Decompiled by Procyon v0.6.0
// 

package com.google.gson.reflect;

import java.util.Map;
import java.util.HashMap;
import java.lang.reflect.WildcardType;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.TypeVariable;
import com.google.gson.internal.TroubleshootingGuide;
import java.lang.reflect.ParameterizedType;
import java.util.Objects;
import com.google.gson.internal.GsonTypes;
import java.lang.reflect.Type;

public class TypeToken<T>
{
    private final Class<? super T> rawType;
    private final Type type;
    private final int hashCode;
    
    protected TypeToken() {
        this.type = this.getTypeTokenTypeArgument();
        this.rawType = (Class<? super T>)GsonTypes.getRawType(this.type);
        this.hashCode = this.type.hashCode();
    }
    
    private TypeToken(final Type type) {
        this.type = GsonTypes.canonicalize(Objects.requireNonNull(type));
        this.rawType = (Class<? super T>)GsonTypes.getRawType(this.type);
        this.hashCode = this.type.hashCode();
    }
    
    private static boolean isCapturingTypeVariablesForbidden() {
        return !Objects.equals(System.getProperty("gson.allowCapturingTypeVariables"), "true");
    }
    
    private Type getTypeTokenTypeArgument() {
        final Type superclass = this.getClass().getGenericSuperclass();
        if (superclass instanceof ParameterizedType) {
            final ParameterizedType parameterized = (ParameterizedType)superclass;
            if (parameterized.getRawType() == TypeToken.class) {
                final Type typeArgument = GsonTypes.canonicalize(parameterized.getActualTypeArguments()[0]);
                if (isCapturingTypeVariablesForbidden()) {
                    verifyNoTypeVariable(typeArgument);
                }
                return typeArgument;
            }
        }
        else if (superclass == TypeToken.class) {
            throw new IllegalStateException("TypeToken must be created with a type argument: new TypeToken<...>() {}; When using code shrinkers (ProGuard, R8, ...) make sure that generic signatures are preserved.\nSee " + TroubleshootingGuide.createUrl("type-token-raw"));
        }
        throw new IllegalStateException("Must only create direct subclasses of TypeToken");
    }
    
    private static void verifyNoTypeVariable(final Type type) {
        if (type instanceof TypeVariable) {
            final TypeVariable<?> typeVariable = (TypeVariable<?>)type;
            throw new IllegalArgumentException("TypeToken type argument must not contain a type variable; captured type variable " + typeVariable.getName() + " declared by " + typeVariable.getGenericDeclaration() + "\nSee " + TroubleshootingGuide.createUrl("typetoken-type-variable"));
        }
        if (type instanceof GenericArrayType) {
            verifyNoTypeVariable(((GenericArrayType)type).getGenericComponentType());
        }
        else if (type instanceof ParameterizedType) {
            final ParameterizedType parameterizedType = (ParameterizedType)type;
            final Type ownerType = parameterizedType.getOwnerType();
            if (ownerType != null) {
                verifyNoTypeVariable(ownerType);
            }
            for (final Type typeArgument : parameterizedType.getActualTypeArguments()) {
                verifyNoTypeVariable(typeArgument);
            }
        }
        else if (type instanceof WildcardType) {
            final WildcardType wildcardType = (WildcardType)type;
            for (final Type bound : wildcardType.getLowerBounds()) {
                verifyNoTypeVariable(bound);
            }
            for (final Type bound : wildcardType.getUpperBounds()) {
                verifyNoTypeVariable(bound);
            }
        }
        else if (type == null) {
            throw new IllegalArgumentException("TypeToken captured `null` as type argument; probably a compiler / runtime bug");
        }
    }
    
    public final Class<? super T> getRawType() {
        return this.rawType;
    }
    
    public final Type getType() {
        return this.type;
    }
    
    @Deprecated
    public boolean isAssignableFrom(final Class<?> cls) {
        return this.isAssignableFrom((Type)cls);
    }
    
    @Deprecated
    public boolean isAssignableFrom(final Type from) {
        if (from == null) {
            return false;
        }
        if (this.type.equals(from)) {
            return true;
        }
        if (this.type instanceof Class) {
            return this.rawType.isAssignableFrom(GsonTypes.getRawType(from));
        }
        if (this.type instanceof ParameterizedType) {
            return isAssignableFrom(from, (ParameterizedType)this.type, new HashMap<String, Type>());
        }
        if (this.type instanceof GenericArrayType) {
            return this.rawType.isAssignableFrom(GsonTypes.getRawType(from)) && isAssignableFrom(from, (GenericArrayType)this.type);
        }
        throw buildUnsupportedTypeException(this.type, Class.class, ParameterizedType.class, GenericArrayType.class);
    }
    
    @Deprecated
    public boolean isAssignableFrom(final TypeToken<?> token) {
        return this.isAssignableFrom(token.getType());
    }
    
    private static boolean isAssignableFrom(final Type from, final GenericArrayType to) {
        final Type toGenericComponentType = to.getGenericComponentType();
        if (toGenericComponentType instanceof ParameterizedType) {
            Type t = from;
            if (from instanceof GenericArrayType) {
                t = ((GenericArrayType)from).getGenericComponentType();
            }
            else if (from instanceof Class) {
                Class<?> classType;
                for (classType = (Class)from; classType.isArray(); classType = classType.getComponentType()) {}
                t = classType;
            }
            return isAssignableFrom(t, (ParameterizedType)toGenericComponentType, new HashMap<String, Type>());
        }
        return true;
    }
    
    private static boolean isAssignableFrom(final Type from, final ParameterizedType to, final Map<String, Type> typeVarMap) {
        if (from == null) {
            return false;
        }
        if (to.equals(from)) {
            return true;
        }
        final Class<?> clazz = GsonTypes.getRawType(from);
        ParameterizedType ptype = null;
        if (from instanceof ParameterizedType) {
            ptype = (ParameterizedType)from;
        }
        if (ptype != null) {
            final Type[] tArgs = ptype.getActualTypeArguments();
            final TypeVariable<?>[] tParams = clazz.getTypeParameters();
            for (int i = 0; i < tArgs.length; ++i) {
                Type arg = tArgs[i];
                final TypeVariable<?> var = tParams[i];
                while (arg instanceof TypeVariable) {
                    final TypeVariable<?> v = (TypeVariable<?>)arg;
                    arg = typeVarMap.get(v.getName());
                }
                typeVarMap.put(var.getName(), arg);
            }
            if (typeEquals(ptype, to, typeVarMap)) {
                return true;
            }
        }
        for (final Type itype : clazz.getGenericInterfaces()) {
            if (isAssignableFrom(itype, to, new HashMap<String, Type>(typeVarMap))) {
                return true;
            }
        }
        final Type sType = clazz.getGenericSuperclass();
        return isAssignableFrom(sType, to, new HashMap<String, Type>(typeVarMap));
    }
    
    private static boolean typeEquals(final ParameterizedType from, final ParameterizedType to, final Map<String, Type> typeVarMap) {
        if (from.getRawType().equals(to.getRawType())) {
            final Type[] fromArgs = from.getActualTypeArguments();
            final Type[] toArgs = to.getActualTypeArguments();
            for (int i = 0; i < fromArgs.length; ++i) {
                if (!matches(fromArgs[i], toArgs[i], typeVarMap)) {
                    return false;
                }
            }
            return true;
        }
        return false;
    }
    
    private static IllegalArgumentException buildUnsupportedTypeException(final Type token, final Class<?>... expected) {
        final StringBuilder exceptionMessage = new StringBuilder("Unsupported type, expected one of: ");
        for (final Class<?> clazz : expected) {
            exceptionMessage.append(clazz.getName()).append(", ");
        }
        exceptionMessage.append("but got: ").append(token.getClass().getName()).append(", for type token: ").append(token.toString());
        return new IllegalArgumentException(exceptionMessage.toString());
    }
    
    private static boolean matches(final Type from, final Type to, final Map<String, Type> typeMap) {
        return to.equals(from) || (from instanceof TypeVariable && to.equals(typeMap.get(((TypeVariable)from).getName())));
    }
    
    @Override
    public final int hashCode() {
        return this.hashCode;
    }
    
    @Override
    public final boolean equals(final Object o) {
        return o instanceof TypeToken && GsonTypes.equals(this.type, ((TypeToken)o).type);
    }
    
    @Override
    public final String toString() {
        return GsonTypes.typeToString(this.type);
    }
    
    public static TypeToken<?> get(final Type type) {
        return new TypeToken<Object>(type);
    }
    
    public static <T> TypeToken<T> get(final Class<T> type) {
        return new TypeToken<T>(type);
    }
    
    public static TypeToken<?> getParameterized(final Type rawType, final Type... typeArguments) {
        Objects.requireNonNull(rawType);
        Objects.requireNonNull(typeArguments);
        if (!(rawType instanceof Class)) {
            throw new IllegalArgumentException("rawType must be of type Class, but was " + rawType);
        }
        final Class<?> rawClass = (Class<?>)rawType;
        final TypeVariable<?>[] typeVariables = rawClass.getTypeParameters();
        final int expectedArgsCount = typeVariables.length;
        final int actualArgsCount = typeArguments.length;
        if (actualArgsCount != expectedArgsCount) {
            throw new IllegalArgumentException(rawClass.getName() + " requires " + expectedArgsCount + " type arguments, but got " + actualArgsCount);
        }
        if (typeArguments.length == 0) {
            return get(rawClass);
        }
        if (GsonTypes.requiresOwnerType(rawType)) {
            throw new IllegalArgumentException("Raw type " + rawClass.getName() + " is not supported because it requires specifying an owner type");
        }
        for (int i = 0; i < expectedArgsCount; ++i) {
            final Type typeArgument = Objects.requireNonNull(typeArguments[i], "Type argument must not be null");
            final Class<?> rawTypeArgument = GsonTypes.getRawType(typeArgument);
            final TypeVariable<?> typeVariable = typeVariables[i];
            for (final Type bound : typeVariable.getBounds()) {
                final Class<?> rawBound = GsonTypes.getRawType(bound);
                if (!rawBound.isAssignableFrom(rawTypeArgument)) {
                    throw new IllegalArgumentException("Type argument " + typeArgument + " does not satisfy bounds for type variable " + typeVariable + " declared by " + rawType);
                }
            }
        }
        return new TypeToken<Object>(GsonTypes.newParameterizedTypeWithOwner(null, rawClass, typeArguments));
    }
    
    public static TypeToken<?> getArray(final Type componentType) {
        return new TypeToken<Object>(GsonTypes.arrayOf(componentType));
    }
}
