/*
 * Copyright © 2016 - 2019 Schlichtherle IT Services
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package global.namespace.neuron.di.internal;

import global.namespace.neuron.di.java.CachingStrategy;
import global.namespace.neuron.di.java.DependencyProvider;

/**
 * Mirrors {@link CachingStrategy}.
 */
enum RealCachingStrategy {

    /**
     * @see #valueOf(CachingStrategy)
     */
    @SuppressWarnings("unused")
    DISABLED {
        @Override
        <D> DependencyProvider<D> decorate(DependencyProvider<D> provider) {
            return provider;
        }
    },

    /**
     * @see #valueOf(CachingStrategy)
     */
    @SuppressWarnings("unused")
    NOT_THREAD_SAFE {
        @Override
        <D> DependencyProvider<D> decorate(final DependencyProvider<D> provider) {
            return new DependencyProvider<D>() {

                D returnValue;

                @Override
                public D get() throws Throwable {
                    final D value = returnValue;
                    return null != value ? value : (returnValue = provider.get());
                }
            };
        }
    },

    /**
     * @see #valueOf(CachingStrategy)
     */
    @SuppressWarnings("unused")
    THREAD_SAFE {
        @Override
        <D> DependencyProvider<D> decorate(final DependencyProvider<D> provider) {
            return new DependencyProvider<D>() {

                volatile D returnValue;

                @Override
                public D get() throws Throwable {
                    D value;
                    if (null == (value = returnValue)) {
                        synchronized (this) {
                            if (null == (value = returnValue)) {
                                returnValue = value = provider.get();
                            }
                        }
                    }
                    return value;
                }
            };
        }
    },

    /**
     * @see #valueOf(CachingStrategy)
     */
    @SuppressWarnings("unused")
    THREAD_LOCAL {
        @Override
        <D> DependencyProvider<D> decorate(final DependencyProvider<D> provider) {
            return new DependencyProvider<D>() {

                final ThreadLocal<D> results = new ThreadLocal<>();

                @Override
                public D get() throws Throwable {
                    D result = results.get();
                    if (null == result) {
                        results.set(result = provider.get());
                    }
                    return result;
                }
            };
        }
    };

    static RealCachingStrategy valueOf(CachingStrategy strategy) {
        return valueOf(strategy.name());
    }

    abstract <D> DependencyProvider<D> decorate(DependencyProvider<D> provider);
}

@@ -1364,25 +1364,49 @@
         
-D returnV
+boolean init;%0A                D v
 alue;%0A%0A 
@@ -1504,118 +1504,160 @@
     
-final D value = returnValue;%0A                    return null != value ? value : (
+if (!init) %7B%0A                        value = provider.get();%0A                        init = true;%0A                    %7D%0A                    
 return
-V
+ v
 alue
- = provider.get())
 ;%0A  
@@ -1989,17 +1989,50 @@
 ile 
-D returnV
+boolean init;%0A                volatile D v
 alue
@@ -2134,70 +2134,17 @@
     
-D value;%0A                    if (null == (value = returnValue)
+if (!init
 ) %7B%0A
@@ -2225,37 +2225,13 @@
 if (
-null == (value = returnValue)
+!init
 ) %7B%0A
@@ -2265,22 +2265,8 @@
     
- returnValue =
  val
@@ -2278,32 +2278,77 @@
 provider.get();%0A
+                                init = true;%0A
                 
@@ -2785,16 +2785,36 @@
 dLocal%3CD
+ependencyProvider%3CD%3E
 %3E result
@@ -2936,16 +2936,36 @@
        D
+ependencyProvider%3CD%3E
  result 
@@ -3019,24 +3019,136 @@
 = result) %7B%0A
+                        final D value = provider.get(); // must resolve now or else there is no caching at all!%0A
             
@@ -3180,30 +3180,27 @@
 esult = 
-provider.get()
+() -%3E value
 );%0A     
@@ -3249,16 +3249,22 @@
 n result
+.get()
 ;%0A      

Support @Caching of null values.