package org.xbib.logging.ext;

import org.xbib.logging.LogContext;
import org.xbib.logging.LogContextSelector;

/**
 * A log context selector which stores the chosen log context in a thread-local.
 */
public final class ThreadLocalLogContextSelector implements LogContextSelector {

    private final Object securityKey;
    private final LogContextSelector delegate;
    private final ThreadLocal<LogContext> context = new ThreadLocal<>();

    /**
     * Construct a new instance.
     *
     * @param delegate the selector to delegate to if no context is chosen
     */
    public ThreadLocalLogContextSelector(final LogContextSelector delegate) {
        this(null, delegate);
    }

    /**
     * Construct a new instance.
     *
     * @param securityKey the security key required to push or pop a log context.
     * @param delegate    the selector to delegate to if no context is chosen
     */
    public ThreadLocalLogContextSelector(final Object securityKey, final LogContextSelector delegate) {
        this.securityKey = securityKey;
        this.delegate = delegate;
    }

    public LogContext getLogContext() {
        final LogContext localContext = context.get();
        return localContext != null ? localContext : delegate.getLogContext();
    }

    /**
     * Get and set the log context.
     *
     * @param securityKey the security key to check (ignored if none was set on construction)
     * @param newValue    the new log context value, or {@code null} to clear
     * @return the previous log context value, or {@code null} if none was set
     */
    public LogContext getAndSet(Object securityKey, LogContext newValue) {
        if (this.securityKey != null && securityKey != this.securityKey) {
            throw new IllegalArgumentException("invalid security key for ThreadLocalLogContextSelector modification");
        }
        try {
            return context.get();
        } finally {
            if (newValue == null)
                context.remove();
            else
                context.set(newValue);
        }
    }
}
