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

package com.google.crypto.tink.internal;

import java.util.Objects;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.security.GeneralSecurityException;
import com.google.crypto.tink.InsecureSecretKeyAccess;
import com.google.crypto.tink.Key;
import java.util.HashMap;
import java.util.Map;

public class PrimitiveRegistry
{
    private final Map<PrimitiveConstructorIndex, PrimitiveConstructor<?, ?>> primitiveConstructorMap;
    private final Map<Class<?>, PrimitiveWrapper<?, ?>> primitiveWrapperMap;
    private final boolean reparseLegacyKeys;
    
    public static Builder builder() {
        return new Builder();
    }
    
    public static Builder builder(final PrimitiveRegistry registry) {
        return new Builder(registry);
    }
    
    private PrimitiveRegistry(final Builder builder) {
        this.primitiveConstructorMap = new HashMap<PrimitiveConstructorIndex, PrimitiveConstructor<?, ?>>(builder.primitiveConstructorMap);
        this.primitiveWrapperMap = new HashMap<Class<?>, PrimitiveWrapper<?, ?>>(builder.primitiveWrapperMap);
        this.reparseLegacyKeys = builder.reparseLegacyKeys;
    }
    
    public <KeyT extends Key, PrimitiveT> PrimitiveT getPrimitive(final KeyT key, final Class<PrimitiveT> primitiveClass) throws GeneralSecurityException {
        if (this.reparseLegacyKeys && key instanceof LegacyProtoKey) {
            final Key reparsedKey = MutableSerializationRegistry.globalInstance().parseKey(((LegacyProtoKey)key).getSerialization(InsecureSecretKeyAccess.get()), InsecureSecretKeyAccess.get());
            return (PrimitiveT)this.getPrimitiveWithoutReparsing(reparsedKey, (Class<Object>)primitiveClass);
        }
        return (PrimitiveT)this.getPrimitiveWithoutReparsing((Key)key, (Class<Object>)primitiveClass);
    }
    
    private <KeyT extends Key, PrimitiveT> PrimitiveT getPrimitiveWithoutReparsing(final KeyT key, final Class<PrimitiveT> primitiveClass) throws GeneralSecurityException {
        final PrimitiveConstructorIndex index = new PrimitiveConstructorIndex((Class)key.getClass(), (Class)primitiveClass);
        if (!this.primitiveConstructorMap.containsKey(index)) {
            throw new GeneralSecurityException("No PrimitiveConstructor for " + index + " available, see https://developers.google.com/tink/faq/registration_errors");
        }
        final PrimitiveConstructor<KeyT, PrimitiveT> primitiveConstructor = (PrimitiveConstructor<KeyT, PrimitiveT>)this.primitiveConstructorMap.get(index);
        return primitiveConstructor.constructPrimitive(key);
    }
    
    private <InnerPrimitiveT, WrappedPrimitiveT> WrappedPrimitiveT wrapWithPrimitiveWrapper(final KeysetHandleInterface keysetHandle, final MonitoringAnnotations annotations, final PrimitiveWrapper<InnerPrimitiveT, WrappedPrimitiveT> wrapper) throws GeneralSecurityException {
        return wrapper.wrap(keysetHandle, annotations, entry -> this.getPrimitive(entry.getKey(), wrapper.getInputPrimitiveClass()));
    }
    
    public <WrappedPrimitiveT> WrappedPrimitiveT wrap(final KeysetHandleInterface keysetHandle, final MonitoringAnnotations annotations, final Class<WrappedPrimitiveT> wrapperClassObject) throws GeneralSecurityException {
        if (!this.primitiveWrapperMap.containsKey(wrapperClassObject)) {
            throw new GeneralSecurityException("No wrapper found for " + wrapperClassObject);
        }
        final PrimitiveWrapper<?, WrappedPrimitiveT> wrapper = (PrimitiveWrapper<?, WrappedPrimitiveT>)this.primitiveWrapperMap.get(wrapperClassObject);
        return this.wrapWithPrimitiveWrapper(keysetHandle, annotations, wrapper);
    }
    
    public static final class Builder
    {
        private final Map<PrimitiveConstructorIndex, PrimitiveConstructor<?, ?>> primitiveConstructorMap;
        private final Map<Class<?>, PrimitiveWrapper<?, ?>> primitiveWrapperMap;
        private boolean reparseLegacyKeys;
        
        private Builder() {
            this.reparseLegacyKeys = false;
            this.primitiveConstructorMap = new HashMap<PrimitiveConstructorIndex, PrimitiveConstructor<?, ?>>();
            this.primitiveWrapperMap = new HashMap<Class<?>, PrimitiveWrapper<?, ?>>();
        }
        
        private Builder(final PrimitiveRegistry registry) {
            this.reparseLegacyKeys = false;
            this.primitiveConstructorMap = new HashMap<PrimitiveConstructorIndex, PrimitiveConstructor<?, ?>>(registry.primitiveConstructorMap);
            this.primitiveWrapperMap = new HashMap<Class<?>, PrimitiveWrapper<?, ?>>(registry.primitiveWrapperMap);
        }
        
        @CanIgnoreReturnValue
        public <KeyT extends Key, PrimitiveT> Builder registerPrimitiveConstructor(final PrimitiveConstructor<KeyT, PrimitiveT> primitiveConstructor) throws GeneralSecurityException {
            if (primitiveConstructor == null) {
                throw new NullPointerException("primitive constructor must be non-null");
            }
            final PrimitiveConstructorIndex index = new PrimitiveConstructorIndex((Class)primitiveConstructor.getKeyClass(), (Class)primitiveConstructor.getPrimitiveClass());
            if (this.primitiveConstructorMap.containsKey(index)) {
                final PrimitiveConstructor<?, ?> existingConstructor = this.primitiveConstructorMap.get(index);
                if (!existingConstructor.equals(primitiveConstructor) || !primitiveConstructor.equals(existingConstructor)) {
                    throw new GeneralSecurityException("Attempt to register non-equal PrimitiveConstructor object for already existing object of type: " + index);
                }
            }
            else {
                this.primitiveConstructorMap.put(index, primitiveConstructor);
            }
            return this;
        }
        
        @CanIgnoreReturnValue
        public <InputPrimitiveT, WrapperPrimitiveT> Builder registerPrimitiveWrapper(final PrimitiveWrapper<InputPrimitiveT, WrapperPrimitiveT> wrapper) throws GeneralSecurityException {
            if (wrapper == null) {
                throw new NullPointerException("wrapper must be non-null");
            }
            final Class<WrapperPrimitiveT> wrapperClassObject = wrapper.getPrimitiveClass();
            if (this.primitiveWrapperMap.containsKey(wrapperClassObject)) {
                final PrimitiveWrapper<?, ?> existingPrimitiveWrapper = this.primitiveWrapperMap.get(wrapperClassObject);
                if (!existingPrimitiveWrapper.equals(wrapper) || !wrapper.equals(existingPrimitiveWrapper)) {
                    throw new GeneralSecurityException("Attempt to register non-equal PrimitiveWrapper object or input class object for already existing object of type" + wrapperClassObject);
                }
            }
            else {
                this.primitiveWrapperMap.put(wrapperClassObject, wrapper);
            }
            return this;
        }
        
        @CanIgnoreReturnValue
        public Builder allowReparsingLegacyKeys() {
            this.reparseLegacyKeys = true;
            return this;
        }
        
        public PrimitiveRegistry build() {
            return new PrimitiveRegistry(this, null);
        }
    }
    
    private static final class PrimitiveConstructorIndex
    {
        private final Class<?> keyClass;
        private final Class<?> primitiveClass;
        
        private PrimitiveConstructorIndex(final Class<?> keyClass, final Class<?> primitiveClass) {
            this.keyClass = keyClass;
            this.primitiveClass = primitiveClass;
        }
        
        @Override
        public boolean equals(final Object o) {
            if (!(o instanceof PrimitiveConstructorIndex)) {
                return false;
            }
            final PrimitiveConstructorIndex other = (PrimitiveConstructorIndex)o;
            return other.keyClass.equals(this.keyClass) && other.primitiveClass.equals(this.primitiveClass);
        }
        
        @Override
        public int hashCode() {
            return Objects.hash(this.keyClass, this.primitiveClass);
        }
        
        @Override
        public String toString() {
            return this.keyClass.getSimpleName() + " with primitive type: " + this.primitiveClass.getSimpleName();
        }
    }
}
