/*
 *  RapidMiner
 *
 *  Copyright (C) 2001-2014 by RapidMiner and the contributors
 *
 *  Complete list of developers available at our web site:
 *
 *       http://rapidminer.com
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as published by
 *  the Free Software Foundation, either version 3 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 Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see http://www.gnu.org/licenses/.
 */
package com.rapidminer.tools;

import java.io.IOException;
import java.io.OutputStream;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

import com.rapidminer.RapidMiner;
import com.rapidminer.gui.tools.LeanFormatter;


/**
 * <p>Utility class providing static methods for logging.<br>
 * Parameters read from the XML process configuration file:</p>
 * <ul>
 * <li>logfile (filename or "stdout" or "stderr")</li>
 * <li>logverbosity (possible values are in
 * {@link LogService#LOG_VERBOSITY_NAMES}</li>
 * </ul>
 * 
 * <p>Beside the <b>local</b> log service associated with a concrete process and which will
 * be automatically initialized during the setup phase, one <b>global</b> log service exist 
 * which is used for generic log messages not bound to the operators used in a process.
 * This global log service is usually initialized to log messages on system out (at least during the 
 * basic initialization phase of RapidMiner). After the basic intialization phase, 
 * the global messages will be presented in the message viewer (if the RapidMiner GUI
 * is used) or still printed to system out or in any other stream defined via the
 * method {@link #initGlobalLogging(OutputStream, int)}. Alternatively, one could also define an 
 * environment variable named {@link RapidMiner#PROPERTY_RAPIDMINER_GLOBAL_LOG_FILE}.</p>
 * 
 * <p>Usually, operators should only use the log verbosities MINIMUM for messages
 * with a low priority and STATUS for normal information messages. In rare cases, the 
 * verbosity level NOTE could be used for operators stating some message more
 * important then STATUS (hence the user should see the message for the default
 * log verbosity level of INIT) but not as important then WARNING. The verbosity
 * levels WARNING, EXCEPTION, and ERROR should be used in error cases. All other 
 * log verbosity levels should only be used by internal RapidMiner classes and not by 
 * user written operators.</p>
 * 
 * <p>We recommend to set the parameter for the log verbosity level to INIT for the 
 * process design phase (eventually STATUS for debugging) and to the log verbosity 
 * level WARNING in the production phase. This way it is ensured that not too many logging
 * messages are produced in the production phase.</p>
 * 
 * <p>Log messages can be formatted by using the following macros:</p>
 * <ul>
 *  <li>&quot;$b&quot; and &quot;^b&quot; start and end bold mode respectively</li>
 *  <li>&quot;$i&quot; and &quot;^i&quot; start and end italic mode respectively</li>
 *  <li>&quot;$m&quot; and &quot;^m&quot; start and end monospace mode respectively</li>
 *  <li>&quot;$n&quot; and &quot;^n&quot; start and end note color mode respectively</li>
 *  <li>&quot;$w&quot; and &quot;^w&quot; start and end warning color mode respectively</li>
 *  <li>&quot;$e&quot; and &quot;^e&quot; start and end error color mode respectively</li>
 * </ul>
 * 
 * @author Ingo Mierswa
 */
public class LogService extends WrapperLoggingHandler {
	
	// -------------------- Verbosity Level --------------------

    /** Indicates an unknown verbosity level. */
    public static final int UNKNOWN_LEVEL = -1;
    
    /**
     * Indicates the lowest log verbosity. Should only be used for very detailed
     * but not necessary logging.
     */
    public static final int MINIMUM = 0;

	/**
	 * Indicates log messages concerning in- and output. Should only be used by
	 * the class Operator itself and not by its subclasses.
	 */
	public static final int IO = 1;

	/** The default log verbosity for all logging purposes of operators. */
	public static final int STATUS = 2;

	/**
	 * Only the most important logging messaged should use this log verbosity.
	 * Currently used only by the LogService itself.
	 */
	public static final int INIT = 3;

	/** Use this log verbosity for logging of important notes, i.e. things less important than warnings 
	 *  but important enough to see for all not interested in the detailed status messages. */
	public static final int NOTE = 4;
	
	/** Use this log verbosity for logging of warnings. */
	public static final int WARNING = 5;

	/** Use this log verbosity for logging of errors. */
	public static final int ERROR = 6;

	/**
	 * Use this log verbosity for logging of fatal errors which will stop
	 * process running somewhere in the future.
	 */
	public static final int FATAL = 7;
    
	/**
	 * Normally this log verbosity should not be used by operators. Messages
	 * with this verbosity will always be displayed.
	 */
    public static final int MAXIMUM = 8;
    
    /** For switching off logging during testing. */
    public static final int OFF = 9;

	public static final String LOG_VERBOSITY_NAMES[] = { "all", "io", "status", "init", "notes", "warning", "error", "fatal", "almost_none", "off" };

	/** The prefix used to indicate the global logger. */
    public static final String GLOBAL_PREFIX = "$gG^g";
    
	private static final Logger GLOBAL_LOGGER = Logger.getLogger("com.rapidminer", "com.rapidminer.resources.i18n.LogMessages");
	private static final LogService GLOBAL_LOGGING = new LogService(GLOBAL_LOGGER);
 
	// ------ methods for init -------
   
    private LogService(Logger logger) {
    	super(logger);
    	
    	// setup a log file if the execution mode can access the filesystem (e.g. not for RA)
    	// we want our logfile to look the same regardless of any user settings, so ignore 
    	// a possible user logging property config file
    	if (RapidMiner.getExecutionMode().canAccessFilesystem()) {
    		try {
    			FileHandler logFileHandler = new FileHandler(FileSystemService.getLogFile().getAbsolutePath(), false);
    			logFileHandler.setLevel(Level.ALL);
    			logFileHandler.setFormatter(new SimpleFormatter());
    			LogService.getRoot().addHandler(logFileHandler);
    		} catch (IOException e) {
    			LogService.getRoot().log(Level.WARNING, "com.rapidminer.logservice.logfile.failed_to_init", e.getMessage());
    		}
    	}
    }
        
    /** Returns the global logging. If no logging was otherwise create, this
     *  method creates the default standard out log service if no log file
     *  was defined in the property {@link RapidMiner#PROPERTY_RAPIDMINER_GLOBAL_LOG_FILE}.
     *  Alternatively, developers can invoke the method {@link #initGlobalLogging(OutputStream, int)}. 
     *
     *  @deprecated use {@link #getRoot()} instead 
     */
    @Deprecated
    public static LogService getGlobal() {
    	return GLOBAL_LOGGING;
    }

    public static Logger getRoot() {
    	return GLOBAL_LOGGER;
    }

//	private void addConsole(Level level) {
//		StreamHandler handler = new ConsoleHandler();
//		handler.setLevel(Level.ALL);
//		getRoot().setLevel(level);
//		getRoot().addHandler(handler);	
//	}

	public void setVerbosityLevel(int level) {
	    getRoot().setLevel(LEVELS[level]);
	}

	/** The methods in {@link Logger} do not provide a means to pass an exception AND I18N arguments,
	 *  so this method provides a shortcut for this. */
	public static void log(Logger logger, Level level, Throwable exception, String i18NKey, Object ... arguments) {
		logger.log(level, I18N.getMessage(logger.getResourceBundle(), i18NKey, arguments), exception);
	}
	
	/**
	 * @deprecated Use {@link Logger#isLoggable(Level)}
	 */
	@Deprecated
	public boolean isSufficientLogVerbosity(int level) {
		return GLOBAL_LOGGER.isLoggable(LEVELS[level]);
	}

	// -------------------- Methoden zum Protokollieren --------------------
    
    /**
     * Writes the message to the output stream if the verbosity level is high
     * enough.
     * 
     * @deprecated please do not use this log method any longer, use the method {@link #log(String, int)} instead
     */
    @Deprecated
    public static void logMessage(String message, int verbosityLevel) {
        getGlobal().log(message, verbosityLevel);
    } 
}
