/**
 * File:    Logger.java
 * Created: 11.12.2005
 *
 *
/*******************************************************************************
 * Copyright (c) 2005-2011
 * University of Augsburg, Germany <www.ds-lab.org>
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Markus Bauer, Programming distributed Systems Lab, University of Augsburg 
 *    	- initial API and implementation
 *******************************************************************************/

package org.eclipse.jwt.we.misc.logging;

import java.io.Serializable;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;

import org.eclipse.jwt.we.PluginProperties;
import org.eclipse.jwt.we.misc.logging.internal.Level;


/**
 * A logger with support for some more levels.
 * 
 * @version $Id: Logger.java,v 1.8 2009-11-26 12:41:18 chsaad Exp $
 * @author Markus Bauer (markusbauer@users.sourceforge.net), Programming distributed
 *         Systems Lab, University of Augsburg, Germany, www.ds-lab.org
 * @since 0.6.0
 */
public class Logger
		extends java.util.logging.Logger
		implements Serializable
{

	/**
	 * The serial version UID
	 */
	private static final long serialVersionUID = 7716168226984601813L;

	/**
	 * If already a java.util.logging.Logger exist, the class will only be a wrapper for
	 * it.
	 */
	private java.util.logging.Logger internalLogger = null;


	/**
	 * @param logger
	 *            The java.util.logging.Logger.
	 */
	public Logger(java.util.logging.Logger logger)
	{
		super(logger.getName(), logger.getResourceBundleName());
		internalLogger = logger;

		setLevel(getLoggingLevel());
		internalLogger.setLevel(getLoggingLevel());
	}


	/**
	 * @param name
	 *            A name for the logger.
	 * @param resourceBundleName
	 *            Name of ResourceBundle to be used for localizing messages for this
	 *            logger.
	 */
	public Logger(String name, String resourceBundleName)
	{
		super(name, resourceBundleName);

		setLevel(getLoggingLevel());
	}


	/**
	 * Returns the configured logging level.
	 * 
	 * @return The logging level.
	 */
	private java.util.logging.Level getLoggingLevel()
	{
		if (PluginProperties.logging_level.equals(Level.DEBUG.getName()))
		{
			return Level.DEBUG;
		}
		else if (PluginProperties.logging_level.equals(Level.DEBUG_WARNING.getName()))
		{
			return Level.DEBUG_WARNING;
		}
		else if (PluginProperties.logging_level.equals(Level.ENTER.getName()))
		{
			return Level.ENTER;
		}
		else if (PluginProperties.logging_level.equals(Level.EXIT.getName()))
		{
			return Level.EXIT;
		}
		else if (PluginProperties.logging_level.equals(Level.INSTANCE_CREATION.getName()))
		{
			return Level.INSTANCE_CREATION;
		}
		else if (PluginProperties.logging_level.equals(Level.THROWING.getName()))
		{
			return Level.THROWING;
		}
		else if (PluginProperties.logging_level.equals(Level.VALUE_CHANGED.getName()))
		{
			return Level.VALUE_CHANGED;
		}
		else if (PluginProperties.logging_level.equals(Level.ALL.getName()))
		{
			return Level.ALL;
		}
		else if (PluginProperties.logging_level.equals(Level.CONFIG.getName()))
		{
			return Level.CONFIG;
		}
		else if (PluginProperties.logging_level.equals(Level.FINE.getName()))
		{
			return Level.FINE;
		}
		else if (PluginProperties.logging_level.equals(Level.FINER.getName()))
		{
			return Level.FINER;
		}
		else if (PluginProperties.logging_level.equals(Level.FINEST.getName()))
		{
			return Level.FINEST;
		}
		else if (PluginProperties.logging_level.equals(Level.INFO.getName()))
		{
			return Level.INFO;
		}
		else if (PluginProperties.logging_level.equals(Level.OFF.getName()))
		{
			return Level.OFF;
		}
		else if (PluginProperties.logging_level.equals(Level.SEVERE.getName()))
		{
			return Level.SEVERE;
		}
		else if (PluginProperties.logging_level.equals(Level.WARNING.getName()))
		{
			return Level.WARNING;
		}

		return null;
	}


	/**
	 * @param clazz
	 *            The class to create the logger for.
	 * @return a suitable Logger
	 */
	public static synchronized Logger getLogger(Class clazz)
	{
		String name = clazz.getName();
		LogManager manager = LogManager.getLogManager();
		java.util.logging.Logger logger = manager.getLogger(name);

		if (logger == null)
		{
			logger = new Logger(name, null);
			manager.addLogger(logger);
			logger = manager.getLogger(name);
		}

		Logger result;
		if (logger instanceof Logger)
		{
			result = (Logger) logger;
		}
		else
		{
			// create wrapper
			result = new Logger(logger);
		}

		return result;
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see java.util.logging.Logger#log(java.util.logging.LogRecord)
	 */
	@Override
	public void log(LogRecord record)
	{
		// Get the stack trace.
		StackTraceElement stack[] = (new Throwable()).getStackTrace();
		// First, search back to a method in the Logger class.
		int ix = 0;
		String thisClass = this.getClass().getName();
		String utilLoggerClass = java.util.logging.Logger.class.getName();
		while (ix < stack.length)
		{
			StackTraceElement frame = stack[ix];
			String cname = frame.getClassName();
			if (cname.equals(thisClass))
			{
				break;
			}
			ix++;
		}
		// Now search for the first frame before the "Logger" class.
		while (ix < stack.length)
		{
			StackTraceElement frame = stack[ix];
			String cname = frame.getClassName();
			if (!cname.equals(thisClass) && !cname.equals(utilLoggerClass))
			{
				// We've found the relevant frame.
				record.setSourceClassName(cname);
				record.setSourceMethodName(frame.getMethodName());
				break;
			}
			ix++;
		}

		if (internalLogger == null)
		{
			super.log(record);
		}
		else
		{
			internalLogger.log(record);
		}
	}


	/**
	 * @return <code>true</code>, if {@link Level#DEBUG} is enabled.
	 */
	public boolean isDebugEnabled()
	{
		return isLoggable(Level.DEBUG);
	}


	/**
	 * Log a changed value or property.
	 * 
	 * @param name
	 *            Name of the value.
	 */
	public void valueChanged(String name)
	{
		log(Level.VALUE_CHANGED, name);
	}


	/**
	 * Log a changed value or property.
	 * 
	 * @param name
	 *            Name of the value.
	 * @param newValue
	 *            The new value.
	 */
	public void valueChanged(String name, Object newValue)
	{
		log(Level.VALUE_CHANGED, name + " = \"{0}\"", newValue); //$NON-NLS-1$
	}


	/**
	 * Log a changed property.
	 * 
	 * @param name
	 *            Name of the property.
	 * @param oldValue
	 *            The old value.
	 * @param newValue
	 *            The new value.
	 */
	public void valueChanged(String name, Object oldValue, Object newValue)
	{
		log(Level.VALUE_CHANGED, name + ": \"{0}\" -> \"{1}\"", new Object[] //$NON-NLS-1$
				{ oldValue, newValue });
	}


	/**
	 * A new instance of current class was created.
	 */
	public void instanceCreation()
	{
		log(Level.INSTANCE_CREATION, null);
	}


	/**
	 * A new instance of an object was created.
	 * 
	 * @param name
	 *            The name of the object.
	 */
	public void instanceCreation(String name)
	{
		log(Level.INSTANCE_CREATION, name);
	}


	/**
	 * Log the entering of a method.
	 */
	public void enter()
	{
		log(Level.ENTER, null);
	}


	/**
	 * Log the entering of a method.
	 * 
	 * @param message
	 *            The message.
	 */
	public void enter(String message)
	{
		log(Level.ENTER, message);
	}


	/**
	 * Log the exiting of a method.
	 */
	public void exit()
	{
		log(Level.EXIT, null);
	}


	/**
	 * Log the exiting of a method.
	 * 
	 * @param result
	 *            The result being returned.
	 */
	public void exit(Object result)
	{
		log(Level.EXIT, null, result);
	}


	/**
	 * Log the exiting of a method.
	 * 
	 * @param message
	 *            The message.
	 */
	public void exit(String message)
	{
		log(Level.EXIT, message);
	}


	/**
	 * Log a debug message.
	 * 
	 * @param message
	 *            The message.
	 */
	public void debug(String message)
	{
		log(Level.DEBUG, message);
	}


	/**
	 * Log a debug message.
	 * 
	 * @param exception
	 *            A catched exception.
	 */
	public void debug(Throwable exception)
	{
		log(Level.DEBUG, null, exception);
	}


	/**
	 * Log a DEBUG_WARNING message.
	 * 
	 * @param msg
	 *            The string message.
	 */
	public void debugWarning(String msg)
	{
		log(Level.DEBUG_WARNING, msg);
	}


	/**
	 * Log a DEBUG_WARNING message.
	 * 
	 * @param msg
	 *            The string message.
	 * @param exception
	 *            A catched exception.
	 */
	public void debugWarning(String msg, Throwable exception)
	{
		log(Level.DEBUG_WARNING, msg, exception);
	}


	/**
	 * Log a WARNING message.
	 * 
	 * @param exception
	 *            A catched exception.
	 */
	public void warning(Throwable exception)
	{
		log(java.util.logging.Level.WARNING, null, exception);
	}


	/**
	 * Log a WARNING message.
	 * 
	 * @param msg
	 *            The string message.
	 * @param exception
	 *            A catched exception.
	 */
	public void warning(String msg, Throwable exception)
	{
		log(java.util.logging.Level.WARNING, msg, exception);
	}


	/**
	 * Log a WARNING message.
	 * 
	 * @param msg
	 *            The string message.
	 * @param param
	 *            A parameter to the message.
	 * @param exception
	 *            A catched exception.
	 */
	public void warning(String msg, Object param, Throwable exception)
	{
		LogRecord record = new LogRecord(java.util.logging.Level.WARNING, msg);
		record.setParameters(new Object[]
		{ param });
		record.setThrown(exception);
		log(record);
	}


	/**
	 * Log a SEVERE message.
	 * 
	 * @param msg
	 *            The string message.
	 * @param exception
	 *            A catched exception.
	 */
	public void severe(String msg, Throwable exception)
	{
		log(java.util.logging.Level.SEVERE, msg, exception);
	}
}
