package com.jscape.util.log;

import com.jscape.util.IOTools;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.nio.file.Path;
import java.util.UUID;
import java.util.logging.*;

import static com.jscape.util.Parameters.assertNotNull;

/**
 * @author Alex Usun
 */
public class DebugStreamLogger
        extends Logger {

    private DebugStreamHandler handler;

    public static DebugStreamLogger systemOutLogger(Level level) {
        return new DebugStreamLogger(new DebugStreamHandler(System.out, new SimpleFormatter()), level);
    }

    public static DebugStreamLogger loggerFor(File file, Level level)
            throws Exception {
        return new DebugStreamLogger(new DebugStreamHandler(new FileOutputStream(file, true), new SimpleFormatter()), level);
    }

    public static DebugStreamLogger loggerFor(Path file, Level level)
            throws Exception {
        return loggerFor(file.toFile(), level);
    }

    public DebugStreamLogger(DebugStreamHandler handler, Level level) {
        super(UUID.randomUUID().toString(), null);

        clearHandlers();
        super.addHandler(handler);
        this.handler = handler;

        setLevel(level);
    }

    @Override
    public void addHandler(Handler handler)
            throws SecurityException {
        // empty
    }

    @Override
    public void removeHandler(Handler handler)
            throws SecurityException {
        // empty
    }

    @Override
    public void setLevel(Level newLevel)
            throws SecurityException {
        super.setLevel(newLevel);
        this.handler.setLevel(newLevel);
    }

    public void stream(OutputStream stream) {
        assertNotNull(stream);

        this.handler.close();
        super.removeHandler(this.handler);

        this.handler = new DebugStreamHandler(stream, new SimpleFormatter());
        Level level = getLevel();
        this.handler.setLevel(level != null ? level : Level.OFF);
        super.addHandler(this.handler);
    }

    public OutputStream stream() {
        return this.handler.stream();
    }

    public void closeStream() {
        IOTools.close(stream());
    }

    private void clearHandlers() {
        for (Handler handler : getHandlers()) {
            super.removeHandler(handler);
        }

        setUseParentHandlers(false);
    }

    public static class DebugStreamHandler
            extends StreamHandler {

        private final OutputStream out;

        public DebugStreamHandler(OutputStream out, Formatter formatter) {
            super(out, formatter);

            this.out = out;

            setLevel(Level.ALL);
        }

        @Override
        public void publish(LogRecord record) {
            if (isLoggable(record)) {
                super.publish(record);
                flush();
            }
        }

        @Override
        public synchronized void close()
                throws SecurityException {
            if (this.out != System.out) {
                super.close();
            }
        }

        public OutputStream stream() {
            return this.out;
        }
    }

}
