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

package io.netty.util.concurrent;

import io.netty.util.internal.PlatformDependent;
import java.util.Map;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Set;
import io.netty.util.internal.InternalThreadLocalMap;

public class FastThreadLocal<V>
{
    private final int index;
    
    public static void removeAll() {
        final InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.getIfSet();
        if (threadLocalMap == null) {
            return;
        }
        try {
            final Object v = threadLocalMap.indexedVariable(InternalThreadLocalMap.VARIABLES_TO_REMOVE_INDEX);
            if (v != null && v != InternalThreadLocalMap.UNSET) {
                final Set<FastThreadLocal<?>> variablesToRemove = (Set<FastThreadLocal<?>>)v;
                final FastThreadLocal[] array;
                final FastThreadLocal<?>[] variablesToRemoveArray = array = variablesToRemove.toArray(new FastThreadLocal[0]);
                for (final FastThreadLocal<?> tlv : array) {
                    tlv.remove(threadLocalMap);
                }
            }
        }
        finally {
            InternalThreadLocalMap.remove();
        }
    }
    
    public static int size() {
        final InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.getIfSet();
        if (threadLocalMap == null) {
            return 0;
        }
        return threadLocalMap.size();
    }
    
    public static void destroy() {
        InternalThreadLocalMap.destroy();
    }
    
    private static void addToVariablesToRemove(final InternalThreadLocalMap threadLocalMap, final FastThreadLocal<?> variable) {
        final Object v = threadLocalMap.indexedVariable(InternalThreadLocalMap.VARIABLES_TO_REMOVE_INDEX);
        Set<FastThreadLocal<?>> variablesToRemove;
        if (v == InternalThreadLocalMap.UNSET || v == null) {
            variablesToRemove = Collections.newSetFromMap(new IdentityHashMap<FastThreadLocal<?>, Boolean>());
            threadLocalMap.setIndexedVariable(InternalThreadLocalMap.VARIABLES_TO_REMOVE_INDEX, variablesToRemove);
        }
        else {
            variablesToRemove = (Set)v;
        }
        variablesToRemove.add(variable);
    }
    
    private static void removeFromVariablesToRemove(final InternalThreadLocalMap threadLocalMap, final FastThreadLocal<?> variable) {
        final Object v = threadLocalMap.indexedVariable(InternalThreadLocalMap.VARIABLES_TO_REMOVE_INDEX);
        if (v == InternalThreadLocalMap.UNSET || v == null) {
            return;
        }
        final Set<FastThreadLocal<?>> variablesToRemove = (Set<FastThreadLocal<?>>)v;
        variablesToRemove.remove(variable);
    }
    
    public FastThreadLocal() {
        this.index = InternalThreadLocalMap.nextVariableIndex();
    }
    
    public final V get() {
        final InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
        final Object v = threadLocalMap.indexedVariable(this.index);
        if (v != InternalThreadLocalMap.UNSET) {
            return (V)v;
        }
        return this.initialize(threadLocalMap);
    }
    
    public final V getIfExists() {
        final InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.getIfSet();
        if (threadLocalMap != null) {
            final Object v = threadLocalMap.indexedVariable(this.index);
            if (v != InternalThreadLocalMap.UNSET) {
                return (V)v;
            }
        }
        return null;
    }
    
    public final V get(final InternalThreadLocalMap threadLocalMap) {
        final Object v = threadLocalMap.indexedVariable(this.index);
        if (v != InternalThreadLocalMap.UNSET) {
            return (V)v;
        }
        return this.initialize(threadLocalMap);
    }
    
    private V initialize(final InternalThreadLocalMap threadLocalMap) {
        V v = null;
        try {
            v = this.initialValue();
            if (v == InternalThreadLocalMap.UNSET) {
                throw new IllegalArgumentException("InternalThreadLocalMap.UNSET can not be initial value.");
            }
        }
        catch (final Exception e) {
            PlatformDependent.throwException(e);
        }
        threadLocalMap.setIndexedVariable(this.index, v);
        addToVariablesToRemove(threadLocalMap, this);
        return v;
    }
    
    public final void set(final V value) {
        this.getAndSet(value);
    }
    
    public final void set(final InternalThreadLocalMap threadLocalMap, final V value) {
        this.getAndSet(threadLocalMap, value);
    }
    
    public V getAndSet(final V value) {
        if (value != InternalThreadLocalMap.UNSET) {
            final InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
            return this.setKnownNotUnset(threadLocalMap, value);
        }
        return this.removeAndGet(InternalThreadLocalMap.getIfSet());
    }
    
    public V getAndSet(final InternalThreadLocalMap threadLocalMap, final V value) {
        if (value != InternalThreadLocalMap.UNSET) {
            return this.setKnownNotUnset(threadLocalMap, value);
        }
        return this.removeAndGet(threadLocalMap);
    }
    
    private V setKnownNotUnset(final InternalThreadLocalMap threadLocalMap, final V value) {
        final V old = (V)threadLocalMap.getAndSetIndexedVariable(this.index, value);
        if (old == InternalThreadLocalMap.UNSET) {
            addToVariablesToRemove(threadLocalMap, this);
            return null;
        }
        return old;
    }
    
    public final boolean isSet() {
        return this.isSet(InternalThreadLocalMap.getIfSet());
    }
    
    public final boolean isSet(final InternalThreadLocalMap threadLocalMap) {
        return threadLocalMap != null && threadLocalMap.isIndexedVariableSet(this.index);
    }
    
    public final void remove() {
        this.remove(InternalThreadLocalMap.getIfSet());
    }
    
    public final void remove(final InternalThreadLocalMap threadLocalMap) {
        this.removeAndGet(threadLocalMap);
    }
    
    private V removeAndGet(final InternalThreadLocalMap threadLocalMap) {
        if (threadLocalMap == null) {
            return null;
        }
        final Object v = threadLocalMap.removeIndexedVariable(this.index);
        if (v != InternalThreadLocalMap.UNSET) {
            removeFromVariablesToRemove(threadLocalMap, this);
            try {
                this.onRemoval(v);
            }
            catch (final Exception e) {
                PlatformDependent.throwException(e);
            }
            return (V)v;
        }
        return null;
    }
    
    protected V initialValue() throws Exception {
        return null;
    }
    
    protected void onRemoval(final V value) throws Exception {
    }
}
