/* $Id: Trace.java,v 1.10 2006/10/20 20:23:07 m31 Exp $
 *
 * This file is part of the project "Hilbert II" - http://www.qedeq.org
 *
 * Copyright 2000-2006,  Michael Meyling <mime@qedeq.org>.
 *
 * "Hilbert II" is free software; you can redistribute
 * it and/or modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 */

package org.qedeq.kernel.log;

import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.qedeq.kernel.utility.IoUtility;


/**
 * Developer trace. If the trace output stream is not set via {@link #setPrintStream(PrintStream)}
 * the output goes to {@link java.lang.System#out}.
 *
 * @version $Revision: 1.10 $
 * @author  Michael Meyling
 */
public final class Trace {

    /** Write trace output to this stream. */
    private static PrintStream out = System.out;

    /** Date output format .*/
    private static final SimpleDateFormat FORMATTER
        = new SimpleDateFormat("yyyy-MM-dd' 'HH:mm:ss,SSS");

    // TODO mime 20050205: make output of qualified classname configurable

    /**
     * Constructor.
     */
    private Trace() {
        // don't call me
    }

    /**
     * Set trace output stream.
     *
     * @param   stream  New trace output.
     */
    public static void setPrintStream(final PrintStream stream) {
        if (stream != null) {
            out = stream;
        } else {
            out.println("Try to set trace output stream to null!");
        }
    }

    /**
     * Trace object.
     *
     * @param   tracingObject   Instance that wants to make a trace entry.
     * @param   method          Method of that object.
     * @param   object          Object to trace.
     */
    public static void trace(final Object tracingObject, final String method,
            final Object object) {
        printObjectMethod(tracingObject, method);
        out.println(object);
    }

    /**
     * Trace object.
     *
     * @param   tracingClass    Class that wants to make a trace entry.
     * @param   method          Method of that class.
     * @param   object          Object to trace.
     */
    public static void trace(final Class tracingClass, final String method,
            final Object object) {
        printClassMethod(tracingClass, method);
        out.println(object);
    }

    /**
     * Trace throwable.
     *
     * @param   tracingObject   Instance that wants to make a trace entry.
     * @param   method          Method of that object.
     * @param   throwable       Throwable to trace.
     */
    public static void trace(final Object tracingObject, final String method,
            final Throwable throwable) {
        printObjectMethod(tracingObject, method);
        out.println(throwable);
        throwable.printStackTrace(out);
    }

    /**
     * Trace throwable.
     *
     * @param   tracingClass    Class that wants to make a trace entry.
     * @param   method          Method of that class.
     * @param   throwable       Throwable to trace.
     */
    public static void trace(final Class tracingClass, final String method,
            final Throwable throwable) {
        printClassMethod(tracingClass, method);
        out.println(throwable);
        throwable.printStackTrace(out);
    }

    /**
     * Trace throwable and extra description.
     *
     * @param   tracingObject   Instance that wants to make a trace entry.
     * @param   method          Method of that object.
     * @param   description     Further information.
     * @param   throwable       Throwable to trace.
     */
    public static void trace(final Object tracingObject, final String method,
            final String description, final Throwable throwable) {
        printObjectMethod(tracingObject, method);
        out.println(description);
        printObjectMethod(tracingObject, method);
        out.println(throwable);
        throwable.printStackTrace(out);
    }

    /**
     * Trace throwable and extra description.
     *
     * @param   tracingClass    Class that wants to make a trace entry.
     * @param   method          Method of that class.
     * @param   description     Further information.
     * @param   throwable       Throwable to trace.
     */
    public static void trace(final Class tracingClass, final String method,
            final String description, final Throwable throwable) {
        printClassMethod(tracingClass, method);
        out.println(description);
        printClassMethod(tracingClass, method);
        out.println(throwable);
        throwable.printStackTrace(out);
    }

    /**
     * Trace method begin. Should be followed by an analogous
     * {@link #traceEnd(Object, String)} call.
     *
     * @param   tracingObject   Instance that wants to make a trace entry.
     * @param   method          Method of that object.
     */
    public static void traceBegin(final Object tracingObject, final String method) {
        trace(tracingObject, method, "begin");
    }

    /**
     * Trace method begin. Should be followed by an analogous {@link #traceEnd(Class, String)} call.
     *
     * @param   tracingClass    Class that wants to make a trace entry.
     * @param   method          Method of that class.
     */
    public static void traceBegin(final Class tracingClass, final String method) {
        trace(tracingClass, method, "begin");
    }

    /**
     * Trace method end.
     *
     * @param   tracingObject   Instance that wants to make a trace entry.
     * @param   method          Method of that object.
     */
    public static void traceEnd(final Object tracingObject, final String method) {
        trace(tracingObject, method, "end");
    }

    /**
     * Trace method end.
     *
     * @param   tracingClass    Class that wants to make a trace entry.
     * @param   method          Method of that class.
     */
    public static void traceEnd(final Class tracingClass, final String method) {
        trace(tracingClass, method, "end");
    }

    /**
     * Trace parameter.
     *
     * @param   tracingObject   Instance that wants to make a trace entry.
     * @param   method          Method of that object.
     * @param   param           Parameter to trace.
     * @param   value           Value of parameter.
     */
    public static void traceParam(final Object tracingObject, final String method,
            final String param, final Object value) {
        trace(tracingObject, method, param + "=" + value);
    }

    /**
     * Trace parameter.
     *
     * @param   tracingClass    Class that wants to make a trace entry.
     * @param   method          Method of that class.
     * @param   param           Parameter to trace.
     * @param   value           Value of parameter.
     */
    public static void traceParam(final Class tracingClass, final String method,
            final String param, final Object value) {
        trace(tracingClass, method, param + "=" + value);
    }

    /**
     * Trace parameter.
     *
     * @param   tracingObject   Instance that wants to make a trace entry.
     * @param   method          Method of that object.
     * @param   param           Parameter to trace.
     * @param   value           Value of parameter.
     */
    public static void traceParam(final Object tracingObject, final String method,
            final String param, final int value) {
        trace(tracingObject, method, param + "=" + value);
    }

    /**
     * Trace parameter.
     *
     * @param   tracingClass    Class that wants to make a trace entry.
     * @param   method          Method of that class.
     * @param   param           Parameter to trace.
     * @param   value           Value of parameter.
     */
    public static void traceParam(final Class tracingClass, final String method,
            final String param, final int value) {
        trace(tracingClass, method, param + "=" + value);
    }

    /**
     * Write stacktrace into trace.
     *
     * @param   tracingObject   Instance that wants to make a trace entry.
     * @param   method          Method of that object.
     */
    public static void traceStack(final Object tracingObject, final String method) {
        try {
            throw new Exception("Stacktrace");
        } catch (Exception e) {
            trace(tracingObject, method, e);
        }
    }

    /**
     * Write stacktrace into trace.
     *
     * @param   tracingClass    Class that wants to make a trace entry.
     * @param   method          Method of that class.
     */
    public static final void traceStack(final Class tracingClass, final String method) {
        try {
            throw new Exception("Stacktrace");
        } catch (Exception e) {
            trace(tracingClass, method, e);
        }
    }

    /**
     * Write traced location into trace.
     *
     * @param   tracingObject   Instance that wants to make a trace entry.
     * @param   method          Method of that class.
     */
    private static final void printObjectMethod(final Object tracingObject, final String method) {
        out.print(getTimestamp() + " [" + (tracingObject != null
                ? IoUtility.getClassName(tracingObject.getClass()) + "." : "")
                + method + "] ");
    }

    /**
     * Write traced location into trace.
     *
     * @param   tracingClass    Class that wants to make a trace entry.
     * @param   method          Method of that class.
     */
    private static final void printClassMethod(final Class tracingClass, final String method) {
        out.print(getTimestamp() + " [" + IoUtility.getClassName(tracingClass) + "."
            + method + "] ");
    }

    /**
     * Get current time.
     *
     * @return  Current timestamp.
     */
    private static final String getTimestamp() {
        return FORMATTER.format(new Date());
    }

}
