/*
 * MiningMart Version 1.0
 * 
 * Copyright (C) 2006 Martin Scholz, Timm Euler, 
 *                    Daniel Hakenjos, Katharina Morik
 *
 * Contact: miningmart@ls8.cs.uni-dortmund.de
 *
 * A list of contributing developers (other than the copyright 
 * holders) can be found at
 * http://mmart.cs.uni-dortmund.de/downloads/download.html
 * 
 * This program 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.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program, see the file MM_HOME/LICENSE; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301, USA.
 */
package edu.udo.cs.miningmart.m4.utils;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.Vector;
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 edu.udo.cs.miningmart.m4.M4Interface;
import edu.udo.cs.miningmart.m4.core.M4InterfaceImpl;

/** 
 * The class <i>Print</i> prints all messages on the screen.
 *
 * @author Timm Euler, Martin Scholz, Daniel Hakenjos
 * @version $Id: Print.java,v 1.5 2006/04/11 14:10:09 euler Exp $
 */
public class Print implements Serializable {

    private static final String MLHOME   		  = "ML_HOME";
    private static final String LOGFILES 		  = "LOG_FILES";

    // public verbosity levels:
    /**
     * Maximum verbosity level.
     */
    public static final Level MAX = new PrintLevel("Max",1100);

	/**
	 * Use this verbosity-level for fatal errors.
	 */
	public static final Level FATAL= new PrintLevel("Fatal",1000);

	/**
	 * Use this verbosity-level for error-messages.
	 */
	public static final Level ERROR =new PrintLevel("Error",950);

	/**
	 * Use this verbosity-level for warnings.
	 */
	public static final Level WARNING = new PrintLevel("Warning",900);

    /**
     * Use this verbosity level for messages that concern the
     * controlling of a case.
     */
    public static final Level COMPILER_CASE_CONTROL = new PrintLevel("Compiler Case Control",860);

    /**
     * Use this verbosity level for messages that concern the
     * controlling of a step.
     */
    public static final Level COMPILER_STEP_CONTROL = new PrintLevel("Compiler Step Control",840);

    /**
     * Use this verbosity level for messages that concern the
     * controlling of an operator.
     */
    public static final Level COMPILER_OP_CONTROL = new PrintLevel("Compiler Operator Control",820);

    /**
     * Use this verbosity level for messages that concern the
     * execution of an operator.
     */
    public static final Level OPERATOR = new PrintLevel("Operator",800);

	/**
	 * Use this verbosity level for messages that concern the
	 * caching of M4 objects in the DB class.
	 */
	public static final Level CACHE = new PrintLevel("M4 Cache",790);

    /**
     * Use this verbosity level for messages that concern the
     * loading, the validity or the status of an operator's parameters.
     */
    public static final Level PARAM = new PrintLevel("Parameters",780);

    /**
     * Use this verbosity level for messages that concern the
     * loading, the validity or the status of any M4 object.
     */
    public static final Level M4_OBJECT = new PrintLevel("M4 Object",770);

    /**
     * Use this verbosity level for messages about updating
     * the database.
     */
    public static final Level DB_WRITE = new PrintLevel("DB Write",760);

    /**
     * Use this verbosity level for messages about reading from
     * the database.
     */
    public static final Level DB_READ = new PrintLevel("DB Read",750);

    /**
     * Use this verbosity level for messages from or about the help sets.
     */
    public static final Level HELP = new PrintLevel("Help",740);

    /**
     * Minimum verbosity level.
     */
    public static final Level MIN = new PrintLevel("Min",730);
    
    private static final Level[] theLevel={MIN,
    															HELP,
    															DB_READ,
    															DB_WRITE,
    															M4_OBJECT,
    															PARAM,
    															CACHE,
    															OPERATOR,
    															COMPILER_OP_CONTROL,
    															COMPILER_STEP_CONTROL,
    															COMPILER_CASE_CONTROL,
    															WARNING,
    															ERROR,
    															FATAL,
    															MAX};

	/**
	 * The default-verbosity-level.
	 */
    public static final Level DEFAULT_VERBOSITY = OPERATOR;
	/**
	 * The maximum number of characters in each line.
	 */
	private static final int LINESIZE = 500;

	// --------------------------------------------

    /**
     * Formats the current date and time, which is written before every message.
     */
    private static SimpleDateFormat format = new SimpleDateFormat("yyyy.MM.dd_HH:mm:ss");

	private String fileName;

    private Level currVerbosity;                  // only messages of this level or higher
                                                					// will be printed
    private final boolean onlyOneLevel = false;	 // if TRUE, only messages of level "currVerbosity"
                                                								// will be printed; if FALSE, also those of higher levels

	private static Print theDefaultPrintObject = null;

	private Logger logger;
	
	// --------------------------------------------

	/**
	 * Construct a Print object with the default verbosity.
	 * Messages will be printeed to StdOut, to a log-file.
	 */
	private Print() {
		createLogger();
		final String defVerbProperty = System.getProperty(M4Interface.SYSTEM_PROP_PRINT_VERBOSITY);
		
		this.currVerbosity=getVerbosityLevelForName(defVerbProperty);
	}

	/**
	 * Construct a Print object with the specified verbosity.
	 * Messages will be printed to Standard Output.
	 * 
	 * @param verbosity Use one of the public static fields of this class
	 */
	private Print(Level verbosityLevel) {
		createLogger();
		this.currVerbosity = verbosityLevel;
	}

	// -------------------------------------------

	/**
	 * Creates the <code>Logger</code>.
	 * 
	 */
	public void createLogger(){
	    File homeDir = getLogPath();
	    if (!homeDir.exists())
			homeDir.mkdirs();
		
	    logger = Logger.getLogger("org.musoft.limo");
	    logger.setLevel(Level.INFO);
	    logger.setUseParentHandlers(false);
	    
	    PrintFormatter formatter=new PrintFormatter();
	    
	    Handler console = new ConsoleHandler();
	    console.setFormatter(formatter);
	    logger.addHandler(console);
	    
		try {
			fileName=homeDir.getAbsolutePath() + File.separatorChar +"MiningMart.log";
		    Handler file = new FileHandler(fileName, true);
		    file.setFormatter(formatter);
		    logger.addHandler(file);
		}
		catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Gets the path to the logfile.
	 */
	public static File getLogPath(){
//		File homeDir = new File(System.getProperty("user.home", ".") + File.separatorChar + ".mmart"+File.separatorChar+"log");
		File homeDir = new File(System.getProperty("MM_HOME", ".") + File.separatorChar + "log");
		return homeDir;
	}

	/**
	 * Gets the logfile.
	 */	
	public static File getLogFile(){
		String fileName=getLogPath().getAbsolutePath() + File.separatorChar +"MiningMart.log";
		return new File(fileName);
	}
	
	/**
	 * Gets the <code>Logger</code>.
	 */
	public Logger getLogger(){
		return logger;
	}
		

	
	/**
	 * Access to the one and only global Print object.
	 */
	public static Print getGlobalPrintObject() {
		if (theDefaultPrintObject == null) {
		    theDefaultPrintObject = new Print();
		}
		return theDefaultPrintObject;
	}
	
	/**
	 * Returns the file name (with complete path) of the file that this
	 * Print object prints to.
	 */
	public String getFileName() {
		return this.fileName;
	}
	

   /**
     * Adds an <code>OutputStream</code> as <code>StreamHandler</code> to the <code>Logger</code>.
     */
    public void addHandler(Handler handler){
		logger.addHandler(handler);
    }

	public void removeHandler(Handler handler){
		logger.removeHandler(handler);
	}

    /**
     * This method prints a string to the screen or a to a logfile.
     *
     * @param printString message to be printed
     * @change June, 12th, 2001 method created
     */
    private String formatString(String printString) {
		if (printString == null)
			printString = "<null>";

		final int length   = printString.length();
    	for (int i=0; i<length; i += LINESIZE) {
    		int end = i + LINESIZE;
	    	if (end > length) {
    			end = length;
	    	}
	    	String oneLine = printString.substring(i, end);
	    }
	    
	    return printString;
    }

	// --------------------------------------------

	/**
	 * Prints a message according to the configuration of this
	 * Print object (depending on the constructor that was used).
	 * 
	 * @param verbosityLevel Use one of the public static fields of this class
	 * @param printString The message to be printed
	 */
    public void doPrint(Level verbosityLevel, String printString) {
    	String print=null;
    	if (onlyOneLevel && (verbosityLevel.intValue() == this.currVerbosity.intValue())){   
    		print=formatString(printString);
            return;
        }
	    else if (verbosityLevel.intValue() >= this.currVerbosity.intValue()){   
			print=formatString(printString);  
	    }
	    if (print!=null){
	    	logger.log(verbosityLevel,print);
	    }
    }

	/**
	 * Prints a message according to the configuration of this
	 * Print object (depending on the constructor that was used).
	 * 
	 * @param verbosityLevel Use one of the public static fields of this class
	 * @param printString The message to be printed
	 */
	public void doPrint(Level verbosityLevel, String printString, Throwable throwable) {
		String print=null;
		if (onlyOneLevel && (verbosityLevel.intValue() == this.currVerbosity.intValue())){   
			print=formatString(printString);
			return;
		}
		else if (verbosityLevel.intValue() >= this.currVerbosity.intValue()){   
			print=formatString(printString);  
		}
		if (print!=null){
			logger.log(verbosityLevel,print,throwable);
		}
	}

    /**
     * Changes the current minimum verbosity level that messages must have
     * in order to be printed by the method <code>doPrint(int, String)</code>.
     * 
     * @param newLevel The new minimum verbosity level.
     */
    public void setMinimumVerbosityLevel(Level newLevel) {
    	if (newLevel.intValue() <= MIN.intValue()) {
    		newLevel = MIN;
    	}
		this.currVerbosity = newLevel;
		logger.setLevel(newLevel);
		
		Handler[] handler=logger.getHandlers();
		for(int i=0;i<handler.length;i++){
			handler[i].setLevel(newLevel);
		}
		doPrint(Print.MAX,"Print-verbosity set to: "+newLevel.getName());
    }

	/**
	 * Gets the current minimum verbosity level that messages must have in order to be printed.
	 */    
    public Level getMinimumVerbosityLevel(){
    	return this.currVerbosity;
    }
    	                                               
    /**
     * Returns a fixed String to be used to display the given verbosity level.
     * 
     * @param level Use one of the public static constants of this class.
     * @return a name to be used for that verbosity level
     */
    public static String getNameOfVerbosityLevel(Level level) {
    	return level.getName();
    }
    
    /**
     * Convenience method to get all names of verbosity levels in descending 
     * order of verbosity, but only down to the given level.
     * 
     * @param minVerbosityLevel the minimum level; only names of this level and higher
     *        are returned
     * @return a Collection with the names of the verbosity levels in descending 
     *         order of verbosity, down to the given minimum level
     */
    public static Collection getAllVerbosityLevelNames(Level minVerbosityLevel) {
    	Vector names = new Vector();
    	for (int i =0; i <theLevel.length; i++) {
    		if (theLevel[i].intValue()>=minVerbosityLevel.intValue()){
    	        names.add(0,theLevel[i].getName());
    	    }
    	}
    	names.trimToSize();
    	return names;
    }
    
    /**
     * Returns the verbosity level which is denoted by the given String, 
     * or DEFAULT_VERBOSITY if there is no level with that name.
     * 
     * @param nameOfLevel Use only such names as have been returned by the
     *        method <code>getNameOfVerbosityLevel</code>. One of the public static constants of this class, 
     *        or DEFAULT_VERBOSITY if no 
     *        level with the given name exists.
     */
    public static Level getVerbosityLevelForName(String nameOfLevel) {
    	for (int i = 0; i < theLevel.length; i++) {
    		if (theLevel[i].getName().equals(nameOfLevel)) {
    		    return theLevel[i];
    		}
    	}
    	if (nameOfLevel.equals("OFF")){
    		return Level.OFF;
    	}else if (nameOfLevel.equals("ALL")){
    		return Level.ALL;
		}else if (nameOfLevel.equals("INFO")){
			return OPERATOR;
		}else if (nameOfLevel.equals("CONFIG")){
			return Level.CONFIG;
		}else if (nameOfLevel.equals("FINE")){
			return Level.FINE;
		}else if (nameOfLevel.equals("FINER")){
			return Level.FINER;
		}else if (nameOfLevel.equals("FINEST")){
			return Level.FINEST;
		}else if (nameOfLevel.equals("SEVERE")){
			return FATAL;
		}else if (nameOfLevel.equals("WARNING")){
			return WARNING;
		}

		return DEFAULT_VERBOSITY;
    }
    
    /**
     * Gets all <code>Level</code>.
     * 
     * @return all Levels.
     */
    public Level[] getTheLevel(){
    	return theLevel;
    }

    /** Returns the current system time nicely formatted. */
    private static String getTime() {
        return format.format(new Date());
    }
    
}
/*
 * Historie
 * --------
 * 
 * $Log: Print.java,v $
 * Revision 1.5  2006/04/11 14:10:09  euler
 * Updated license text.
 *
 * Revision 1.4  2006/04/06 16:31:09  euler
 * Prepended license remark.
 *
 * Revision 1.3  2006/03/19 17:00:37  scholz
 * refactoring
 *
 * Revision 1.2  2006/01/05 10:27:38  hakenjos
 * Removed Javadoc Warnings!
 *
 * Revision 1.1  2006/01/03 09:54:03  hakenjos
 * Initial version!
 *
 */

/*
 * Old Historie
 * -------------
 *
 * Revision 1.13  2002/11/28 15:04:41  scholz
 * added default verbosity setting based on new system property
 *
 * Revision 1.12  2002/10/24 08:28:08  euler
 * New Javadoc comments.
 *
 * Revision 1.11  2002/10/17 12:54:58  scholz
 * bugfix
 *
 * Revision 1.10  2002/10/11 14:38:00  scholz
 * added verbosity level to main method
 *
 * Revision 1.9  2002/10/11 13:02:49  scholz
 * added verbosity level methods to
 * interface, Print and CompilerAccessLogic
 *
 * Revision 1.8  2002/10/10 16:29:07  scholz
 * concurrent compiler server accesses via rmi implemented
 * Status:
 * - runs on DM_TIME_SERIES case
 * - caseId logfiles work
 * - reading case status messages via RMI access works
 *
 * Revision 1.7  2002/10/08 18:07:56  scholz
 * Prepared code for parallel execution of multiple compile-Threads.
 * Calls to static fields like DB.m4Db were removed. Now each
 * Thread has its own DB object, reachable via
 * CompilerAccessLogic or Case.
 * The methods getCase() and getM4Db() were added to M4Object.
 * The static methods of Parameter now need an additional
 * parameter of type DB.
 * All direct calls from Operators to these Parameter methods were
 * removed.
 * All old load() and print() routines were removed.
 * The static calls to Print were removed. Now CompilerAccessLogic
 * references a valid Print object for the current Thread. This is
 * reachable via Case. The methods doPrint for messages and
 * Exceptions were added to M4Object.
 * The Print mechanism is not fully functional, yet.
 * A getStatus method was added to the Interface. It is not
 * functional yet for multiple Threads.
 *
 * Status: Compiles.
 *
 * Revision 1.6  2002/07/24 13:06:40  euler
 * Changed package names.
 *
 * Revision 1.5  2002/07/18 17:00:48  euler
 * Implemented the Feature.getCurrent()-methods.
 *
 * Revision 1.4  2002/07/12 12:26:26  euler
 * Updated BA_HIDE, Parameter-suffixes.
 *
 * Revision 1.3  2002/07/11 08:36:35  euler
 * Changed printing of messages.
 *
 * Revision 1.2  2002/07/01 18:23:47  scholz
 * - bufix for "open cursor" problem
 * - bugfix: now you _really_ can switch statistics off
 *
 * Revision 1.1  2002/04/30 13:00:31  wiese
 * compileable version
 *
 * Revision 1.6  2002/04/19 15:23:12  wiese
 * Initialversion nach der uebergabe
 *
 */

