/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.brooklyn.util.logging;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.LogRecord;

import com.google.common.base.Throwables;

//useful for java.util.logger, which by default puts every message on two lines (horrid)
//however we don't typically use java.util.logger...
public class SimpleOneLineLogFormatter extends Formatter {

    public SimpleOneLineLogFormatter() {
        this(true, false, false);
    }

    public SimpleOneLineLogFormatter(boolean showLevel, boolean showThread, boolean showCaller) {
        this.showLevel = showLevel;
        this.showThread = showThread;
        this.showCaller = showCaller;
    }


    public final boolean showLevel;
    public final boolean showThread;
    public final boolean showCaller;

    // use shared date and formatter to minimize memory/time overhead
    protected final Date date = new Date();
    protected DateFormat dateFormat = new SimpleDateFormat(getDateFormat());

    public String getDateFormat() {
        return "yyyy-MM-dd HH:mm:ss.SSSZ";
    }

    /** uses "YYYY-DD-MM hh:mm:ss.SSS  message" format */ 
    @Override
    public String format(LogRecord record) {
        StringBuffer sb = new StringBuffer();
        appendDate(record, sb);
        appendLevel(record, sb);
        sb.append("  ");
        sb.append(formatMessage(record));
        appendThreadAndCaller(record, sb);
        appendDetailsWithNewLine(sb, record);
        return sb.toString();
    }

    protected void appendLevel(LogRecord record, StringBuffer sb) {
        if (showLevel) {
            sb.append(" [").append(record.getLevel()).append("]");
        }
    }

    protected void appendDate(LogRecord record, StringBuffer sb) {
        synchronized (date) {
            date.setTime(record.getMillis());
            sb.append(dateFormat.format(date));
        }
    }

    protected void appendThreadAndCaller(LogRecord record, StringBuffer sb) {
        if (showThread || showCaller) {
            sb.append(" [");
            if (showThread)
                sb.append(getThreadName(record));
            if (showThread && showCaller) sb.append(", ");
            if (showCaller) {
                if (record.getSourceClassName() != null) {    
                    sb.append(record.getSourceClassName());
                } else {
                    sb.append(record.getLoggerName());
                }
                if (record.getSourceMethodName() != null) {    
                    sb.append(" ");
                    sb.append(record.getSourceMethodName());
                }
            }
            sb.append("]");
        }
    }

    protected void appendDetailsWithNewLine(StringBuffer sb, LogRecord record) {
        if (record.getThrown() != null) {
            try {
                sb.append('\n');
                StringWriter sw = new StringWriter();
                PrintWriter pw = new PrintWriter(sw);
                record.getThrown().printStackTrace(pw);
                pw.close();
                sb.append(sw.toString());
            } catch (Exception ex) {
                //shouldn't happen with printwriter
                throw Throwables.propagate(ex);
            }
        } else {
            sb.append('\n');
        }
    }

    protected String getThreadName(LogRecord record) {
        //try to get the thread's name
        //only possible if we are the thread (unless do something messy like cache or access private fields)
        //fortunately we typically are the thread
        LogRecord lr = new LogRecord(Level.INFO, "");
        if (lr.getThreadID()==record.getThreadID())
            return Thread.currentThread().getName() + " ("+record.getThreadID()+")";
        //otherwise just say the number
        return "thread ("+record.getThreadID()+")";
    }

    public static class LogFormatterWithThreadAndCaller extends SimpleOneLineLogFormatter {
        public LogFormatterWithThreadAndCaller() {
            super(true, true, true);
        }
    }
    
}