/*
 *  @author Ranides Atterwim <ranides@gmail.com>
 *  @copyright Ranides Atterwim
 *  @license WTFPL
 *  @url http://ranides.net/projects/assira
 */

package net.ranides.assira.trace;

import java.util.Formatter;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import net.ranides.assira.reflection.inspect.FieldInspector;
import net.ranides.assira.system.RuntimeUtils;

/**
 *
 * @author ranides
 */
public final class LoggerUtils {
    
    private static final Map<String, AdvLogger> CACHE = new HashMap<>();

    public static final org.apache.log4j.Layout LAYOUT = new org.apache.log4j.PatternLayout("%d %p [%c %M] %m%n");
    
    public static final org.apache.log4j.Appender PRINTER = new org.apache.log4j.ConsoleAppender(LAYOUT, "System.out");

    private LoggerUtils() { }

    public static final String INVOKE = "invoke";

    public static final String INVOKE_SUCCESS = "invoke success";

    public static final String INVOKE_ERROR = "invoke error";

    public static java.util.logging.Logger resetJavaLogger(java.util.logging.Level level) {
        java.util.logging.LogManager.getLogManager().reset();
        java.util.logging.Logger global = java.util.logging.Logger.getLogger(java.util.logging.Logger.GLOBAL_LOGGER_NAME);
        global.setLevel(level);
        return global;
    }

    public static void initTest() {
        resetLogger4j(RuntimeUtils.invokedInsideSingleTest());
    }

    public static java.util.logging.Logger resetJavaLogger(boolean enable) {
        return resetJavaLogger(enable ? java.util.logging.Level.ALL : java.util.logging.Level.OFF );
    }

    public static org.apache.log4j.Logger resetLogger4j(boolean enable) {
        return resetLogger4j(enable ? org.apache.log4j.Level.ALL : org.apache.log4j.Level.OFF);
    }

    public static org.apache.log4j.Logger resetLogger4j(org.apache.log4j.Level level) {
        return resetLogger4j(level, PRINTER);
    }

    public static org.apache.log4j.Logger resetLogger4j(org.apache.log4j.Level level, org.apache.log4j.Appender appender) {
        org.apache.log4j.Logger logger = org.apache.log4j.Logger.getRootLogger();
        logger.removeAllAppenders();
        logger.addAppender(appender);
        logger.setLevel(level);
        return logger;
    }

    public static AdvLogger getLogger() {
        return getLogger( new Throwable().getStackTrace()[1].getClassName() );
    }

    public static AdvLogger getLogger(Class<?> clazz) {
        return getLogger(clazz.getName());
    }

    public static AdvLogger getLogger(String name) {
        // Może się okazać, że logger INNEJ KLASY został już utworzony dla podanego "name"
        // Wtedy nie ruszy nasza fabryka "FACTORY", tylko dostaniemy
        // instancję wcześniej utworzoną (uwaga! skonfigurowaną!)
        //
        // nawet jeśli chcemy ją zignorować, to musimy przepisać z niej konfigurację

        org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(name, AdvLogger.FACTORY);

        if(logger instanceof AdvLogger) {
            return (AdvLogger)logger;
        } else {
            synchronized(CACHE) {
                if(CACHE.containsKey(name)) {
                    return CACHE.get(name);
                }
                AdvLogger result = new AdvLogger(name);
                FieldInspector.copy(result, logger);
                CACHE.put(name, result);
                return result;
            }
        }

    }

    /**
     * Działa tylko wtedy, jeśli ustawiona jest zmienna systemowa "test".
     * W praktyce oznacza to, że działa wtedy, gdy uruchomiono <b>pojedynczny</b>
     * test jednostkowy: jeśli test uruchomiony jest np podczas budowania projektu,
     * to nie funkcja nie wyświetla komunikatu.
     * @param locale
     * @param format
     * @param args
     */
    @SuppressWarnings({"PMD.SystemPrintln", "NP_ALWAYS_NULL"})
    public static void dump(Locale locale, String format, Object... args) {
        if(RuntimeUtils.invokedInsideSingleTest()) {
            System.out.print(new Formatter().format(locale, format, args).toString());
        }
    }

    /**
     * Działa tylko wtedy, jeśli ustawiona jest zmienna systemowa "test".
     * W praktyce oznacza to, że działa wtedy, gdy uruchomiono <b>pojedynczny</b>
     * test jednostkowy: jeśli test uruchomiony jest np podczas budowania projektu,
     * to nie funkcja nie wyświetla komunikatu.
     * @param format
     * @param args
     */
    public static void dump(String format, Object... args) {
        dump(Locale.ROOT, format, args);
    }

    public static void setThreadLogger(final org.apache.log4j.Logger logger) {
        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread thread, Throwable exception) {
                logger.fatal("Abnormal termination of thread: " + thread.getName(), exception);
            }
        });
    }

    public static void removeHandlers(final java.util.logging.Logger logger) {
        for(java.util.logging.Handler handler : logger.getHandlers()) {
            if( handler instanceof java.util.logging.ConsoleHandler) {
                logger.removeHandler(handler);
            }
        }
    }

}
