package org.xbib.logging.log4j;

import java.util.Collections;

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.ThreadContext;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.message.MessageFactory;
import org.apache.logging.log4j.spi.AbstractLogger;
import org.xbib.logging.ExtLogRecord;

/**
 * An implementation of a log4j2 {@linkplain org.apache.logging.log4j.Logger logger} that delegates
 * to a LogManager logger.
 * <p>
 * Only the {@linkplain Level level} is used to determine the result {@code isEnabled()} methods. All other parameters
 * are ignored.
 * </p>
 */
@SuppressWarnings("serial")
class XbibLogger extends AbstractLogger {

    private transient final org.xbib.logging.Logger logger;

    private transient final LevelTranslator levelTranslator;

    XbibLogger(final org.xbib.logging.Logger logger, final MessageFactory messageFactory) {
        super(logger.getName(), messageFactory);
        this.logger = logger;
        this.levelTranslator = LevelTranslator.getInstance();
    }

    @Override
    public boolean isEnabled(final Level level, final Marker marker, final Message message, final Throwable t) {
        return logger.isLoggable(levelTranslator.translateLevel(level));
    }

    @Override
    public boolean isEnabled(final Level level, final Marker marker, final CharSequence message, final Throwable t) {
        return logger.isLoggable(levelTranslator.translateLevel(level));
    }

    @Override
    public boolean isEnabled(final Level level, final Marker marker, final Object message, final Throwable t) {
        return logger.isLoggable(levelTranslator.translateLevel(level));
    }

    @Override
    public boolean isEnabled(final Level level, final Marker marker, final String message, final Throwable t) {
        return logger.isLoggable(levelTranslator.translateLevel(level));
    }

    @Override
    public boolean isEnabled(final Level level, final Marker marker, final String message) {
        return logger.isLoggable(levelTranslator.translateLevel(level));
    }

    @Override
    public boolean isEnabled(final Level level, final Marker marker, final String message, final Object... params) {
        return logger.isLoggable(levelTranslator.translateLevel(level));
    }

    @Override
    public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0) {
        return logger.isLoggable(levelTranslator.translateLevel(level));
    }

    @Override
    public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0, final Object p1) {
        return logger.isLoggable(levelTranslator.translateLevel(level));
    }

    @Override
    public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0, final Object p1,
            final Object p2) {
        return logger.isLoggable(levelTranslator.translateLevel(level));
    }

    @Override
    public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0, final Object p1,
            final Object p2, final Object p3) {
        return logger.isLoggable(levelTranslator.translateLevel(level));
    }

    @Override
    public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0, final Object p1,
            final Object p2, final Object p3, final Object p4) {
        return logger.isLoggable(levelTranslator.translateLevel(level));
    }

    @Override
    public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0, final Object p1,
            final Object p2, final Object p3, final Object p4, final Object p5) {
        return logger.isLoggable(levelTranslator.translateLevel(level));
    }

    @Override
    public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0, final Object p1,
            final Object p2, final Object p3, final Object p4, final Object p5, final Object p6) {
        return logger.isLoggable(levelTranslator.translateLevel(level));
    }

    @Override
    public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0, final Object p1,
            final Object p2, final Object p3, final Object p4, final Object p5, final Object p6, final Object p7) {
        return logger.isLoggable(levelTranslator.translateLevel(level));
    }

    @Override
    public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0, final Object p1,
            final Object p2, final Object p3, final Object p4, final Object p5, final Object p6, final Object p7,
            final Object p8) {
        return logger.isLoggable(levelTranslator.translateLevel(level));
    }

    @Override
    public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0, final Object p1,
            final Object p2, final Object p3, final Object p4, final Object p5, final Object p6, final Object p7,
            final Object p8, final Object p9) {
        return logger.isLoggable(levelTranslator.translateLevel(level));
    }

    @Override
    public void logMessage(final String fqcn, final Level level, final Marker marker, final Message message,
            final Throwable t) {
        // Ignore null messages
        if (message != null) {
            final ExtLogRecord record = new ExtLogRecord(levelTranslator.translateLevel(level),
                    message.getFormattedMessage(), ExtLogRecord.FormatStyle.NO_FORMAT, fqcn);
            if (message.getParameters() != null) {
                record.setParameters(message.getParameters());
            }
            if (ThreadContext.isEmpty()) {
                record.setMdc(Collections.emptyMap());
            } else {
                record.setMdc(ThreadContext.getContext());
            }
            record.setNdc(getNdc());
            record.setThrown(t == null ? message.getThrowable() : t);
            logger.log(record);
        }
    }

    @Override
    public Level getLevel() {
        final java.util.logging.Level level = logger.getLevel();
        if (level != null) {
            return levelTranslator.translateLevel(level);
        }
        return levelTranslator.translateLevel(logger.getEffectiveLevel());
    }

    private String getNdc() {
        final ThreadContext.ContextStack contextStack = ThreadContext.getImmutableStack();
        if (contextStack.isEmpty()) {
            return "";
        }
        return String.join(".", contextStack);
    }
}
