// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.sdk.util;

import java.util.concurrent.atomic.AtomicReference;

/**
 * Holds a value yet-to-be-constructed. It gets constructed when method {@link #get()} is
 * called the first time. User must provide {@link LazyConstructable.Factory} that
 * actually constructs the value. Once the value is constructed, the factory is released
 * and may be collected by Java GC.
 * <p>Threads: the class is thread-safe; how it's implementation is lock-free, so a
 * factory may be called several times from several concurrent threads. However
 * method {@link #get()} is guaranteed to always return the same value from to whatever
 * thread.
 */
public class LazyConstructable<T> {
  public interface Factory<T> {
    T construct();
  }

  public static <T> LazyConstructable<T> create(Factory<T> factory) {
    return new LazyConstructable<T>(factory);
  }

  private final AtomicReference<Result<T>> resultRef;

  public LazyConstructable(Factory<T> factory) {
    this.resultRef = new AtomicReference<Result<T>>(new FutureResult(factory));
  }

  /**
   * Constructs a value when called first time and returns it to all subsequent calls.
   */
  public T get() {
    return resultRef.get().get();
  }

  private static abstract class Result<T> {
    abstract T get();
  }

  private class FutureResult extends Result<T> {
    private final Factory<T> factory;

    private FutureResult(Factory<T> factory) {
      this.factory = factory;
    }

    @Override
    T get() {
      T newResult = factory.construct();
      resultRef.compareAndSet(this, new ReadyResult<T>(newResult));
      return resultRef.get().get();
    }
  }

  private static class ReadyResult<T> extends Result<T> {
    private final T result;

    public ReadyResult(T result) {
      this.result = result;
    }

    @Override
    T get() {
      return result;
    }
  }
}
