package com.proofpoint.log;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.OutputStreamAppender;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP;
import ch.qos.logback.core.rolling.TimeBasedRollingPolicy;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintStream;
import java.io.Reader;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Handler;
import java.util.logging.LogManager;

public class Logging
{
    private final String PATTERN = "%d{yyyy-MM-dd'T'HH:mm:ss.SSSZ}\\t%5p\\t%t\\t%c\\t%m%n";
    private LoggerContext context;
    private ch.qos.logback.classic.Logger root;
    private Logger log = Logger.get(Logging.class);
    private OutputStreamAppender<ILoggingEvent> consoleAppender;
    private RollingFileAppender<ILoggingEvent> fileAppender;

    public Logging()
    {
        // initialize root logger
        root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);

        // assume SLF4J is bound to logback in the current environment
        context = (LoggerContext) LoggerFactory.getILoggerFactory();
        context.reset();
        root.setLevel(Level.INFO);

        redirectSlf4jToConsole();
        redirectStdStreamsToSlf4j();
        redirectJULToSLF4j();
    }

    private void redirectStdStreamsToSlf4j()
    {
        System.setOut(new PrintStream(new LoggingOutputStream(LoggerFactory.getLogger("stdout"))));
        System.setErr(new PrintStream(new LoggingOutputStream(LoggerFactory.getLogger("stderr"))));
    }

    private void redirectSlf4jToConsole()
    {
        PatternLayoutEncoder encoder = new PatternLayoutEncoder();
        encoder.setPattern(PATTERN);
        encoder.setContext(context);
        encoder.start();

        consoleAppender = new OutputStreamAppender<ILoggingEvent>();
        consoleAppender.setContext(context);
        consoleAppender.setEncoder(encoder);
        consoleAppender.setOutputStream(System.err); // needs to happen after setEncoder()
        consoleAppender.start();
        root.addAppender(consoleAppender);

        log.info("Logging to stderr");
    }

    public void disableConsole()
    {
        log.info("Disabling stderr output");
        root.detachAppender(consoleAppender);
        consoleAppender.stop();
    }

    public void logToFile(String logPath, int maxHistory, long maxSizeInBytes)
    {
        log.info("Logging to %s", logPath);

        PatternLayoutEncoder encoder = new PatternLayoutEncoder();
        encoder.setPattern(PATTERN);
        encoder.setContext(context);
        encoder.start();
        
        fileAppender = new RollingFileAppender<ILoggingEvent>();
        TimeBasedRollingPolicy<ILoggingEvent> rollingPolicy = new TimeBasedRollingPolicy<ILoggingEvent>();
        SizeAndTimeBasedFNATP<ILoggingEvent> triggeringPolicy = new SizeAndTimeBasedFNATP<ILoggingEvent>();

        rollingPolicy.setContext(context);
        rollingPolicy.setFileNamePattern(logPath + "-%d{yyyy-MM-dd}.%i.log.gz");
        rollingPolicy.setMaxHistory(maxHistory);
        rollingPolicy.setTimeBasedFileNamingAndTriggeringPolicy(triggeringPolicy);
        rollingPolicy.setParent(fileAppender);
        rollingPolicy.start();

        triggeringPolicy.setMaxFileSize(Long.toString(maxSizeInBytes));
        triggeringPolicy.setContext(context);
        triggeringPolicy.setTimeBasedRollingPolicy(rollingPolicy);
        triggeringPolicy.start();

        fileAppender.setFile(logPath);
        fileAppender.setAppend(true);
        fileAppender.setEncoder(encoder);
        fileAppender.setRollingPolicy(rollingPolicy);
        fileAppender.setContext(context);
        fileAppender.start();
        root.addAppender(fileAppender);
    }

    private void redirectJULToSLF4j()
    {
        java.util.logging.Logger javaRootLogger = LogManager.getLogManager().getLogger("");
        for (Handler handler : javaRootLogger.getHandlers()) {
            javaRootLogger.removeHandler(handler);
        }
        javaRootLogger.addHandler(new JavaLoggingToSlf4JHandler());
    }

    public void setLevels(File file)
            throws IOException
    {
        Properties properties = new Properties();
        Reader reader = new FileReader(file);
        try {
            properties.load(reader);
        }
        finally {
            reader.close();
        }
        
        processLevels(properties);
    }

    private void processLevels(Properties properties)
            throws IOException
    {
        for (Map.Entry<Object, Object> entry : properties.entrySet()) {
            String name = entry.getKey().toString();

            ch.qos.logback.classic.Logger logger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(name);
            logger.setLevel(Level.toLevel(entry.getValue().toString()));
        }
    }


    public void initialize(LoggingConfiguration config)
            throws IOException
    {
        if (config.getLogPath() != null) {
            logToFile(config.getLogPath(), config.getMaxHistory(), config.getMaxSegmentSizeInBytes());
        }
        if (!config.isConsoleEnabled()) {
            disableConsole();
        }

        if (config.getLevelsFile() != null) {
            setLevels(new File(config.getLevelsFile()));
        }
    }
}

@@ -544,16 +544,45 @@
 eption;%0A
+import java.io.OutputStream;%0A
 import j
@@ -1508,36 +1508,60 @@
   re
-directSlf4jToConsole
+wireStdStreams();%0A        redirectJULToSLF4j
 ();%0A    
     
@@ -1556,28 +1556,42 @@
 ();%0A    
+%7D%0A%0A
     
-red
+private void rew
 ire
-ct
 StdStrea
@@ -1596,43 +1596,158 @@
 eams
-ToSlf4j();%0A        redirectJULToSLF
+()%0A    %7B%0A        OutputStream out = System.err;%0A%0A        redirectSlf4jTo(out);%0A        log.info(%22Logging to stderr%22);%0A%0A        redirectStdStreamsToSlf
 4j()
@@ -2045,24 +2045,36 @@
 tSlf4jTo
-Console(
+(OutputStream stream
 )%0A    %7B%0A
@@ -2439,26 +2439,22 @@
 tStream(
-System.err
+stream
 ); // ne
@@ -2565,48 +2565,8 @@
 er);
-%0A%0A        log.info(%22Logging to stderr%22);
 %0A   

reorganize methods for clarity