/*************************************************************************

"FreePastry" Peer-to-Peer Application Development Substrate 

Copyright 2002, Rice University. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

- Neither  the name  of Rice  University (RICE) nor  the names  of its
contributors may be  used to endorse or promote  products derived from
this software without specific prior written permission.

This software is provided by RICE and the contributors on an "as is"
basis, without any representations or warranties of any kind, express
or implied including, but not limited to, representations or
warranties of non-infringement, merchantability or fitness for a
particular purpose. In no event shall RICE or contributors be liable
for any direct, indirect, incidental, special, exemplary, or
consequential damages (including, but not limited to, procurement of
substitute goods or services; loss of use, data, or profits; or
business interruption) however caused and on any theory of liability,
whether in contract, strict liability, or tort (including negligence
or otherwise) arising in any way out of the use of this software, even
if advised of the possibility of such damage.

********************************************************************************/
/*
 *  Created on Jun 28, 2005
 *
 */
package rice.environment.logging;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.Hashtable;

import javax.swing.text.DateFormatter;

import rice.environment.logging.simple.SimpleLogger;
import rice.environment.params.ParameterChangeListener;
import rice.environment.params.Parameters;
import rice.environment.time.TimeSource;

/**
 * @version $Id: pretty.settings,v 1.3 2005/03/11 20:22:33 jeffh Exp $
 * @author jstewart
 */
public abstract class AbstractLogManager implements LogManager {
  /**
   * Hashtable of loggers stored by full.class.name[instance]
   */
  protected Hashtable loggers;
  /**
   * DESCRIBE THE FIELD
   */
  protected Parameters params;

  /**
   * DESCRIBE THE FIELD
   */
  protected Logger defaultLogger;

  /**
   * DESCRIBE THE FIELD
   */
  protected TimeSource time;
  /**
   * DESCRIBE THE FIELD
   */
  protected PrintStream ps;
  /**
   * DESCRIBE THE FIELD
   */
  protected String prefix;
  /**
   * DESCRIBE THE FIELD
   */
  protected String dateFormat;

  /**
   * DESCRIBE THE FIELD
   */
  protected boolean enabled;
  private PrintStream nullPrintStream;

  /**
   * DESCRIBE THE FIELD
   */
  public DateFormatter dateFormatter;

  /**
   * Constructor for AbstractLogManager.
   *
   * @param stream DESCRIBE THE PARAMETER
   * @param timeSource DESCRIBE THE PARAMETER
   * @param params DESCRIBE THE PARAMETER
   * @param prefix DESCRIBE THE PARAMETER
   * @param df DESCRIBE THE PARAMETER
   */
  protected AbstractLogManager(PrintStream stream, TimeSource timeSource, Parameters params, String prefix, String df) {
    this.ps = stream;
    this.time = timeSource;
    this.params = params;
    this.prefix = prefix;
    this.dateFormat = df;
    if (this.dateFormat == null) {
      this.dateFormat = params.getString("logging_date_format");
    }
    if (this.dateFormat != null && !this.dateFormat.equals("")) {
      dateFormatter = new DateFormatter(new SimpleDateFormat(this.dateFormat));
//      System.out.println("DateFormat "+this.dateFormat);
    }

    this.enabled = params.getBoolean("logging_enable");
    this.nullPrintStream = new PrintStream(new NullOutputStream());

    this.loggers = new Hashtable();
    this.defaultLogger = constructLogger("", parseVal("loglevel"));

    params.addChangeListener(
      new ParameterChangeListener() {
        public void parameterChange(String paramName, String newVal) {
          if (paramName.equals("logging_enable")) {
            enabled = Boolean.getBoolean(newVal);
          } else if (paramName.equals("loglevel")) {
            ((LogLevelSetter) defaultLogger).setMinLogLevel(parseVal(paramName));
          } else if (paramName.endsWith("_loglevel")) {
            if (loggers.contains(paramName)) {
              ((LogLevelSetter) loggers.get(paramName)).setMinLogLevel(parseVal(paramName));
            }
          }
        }
      });
  }

  /**
   * @param clazz DESCRIBE THE PARAMETER
   * @param instance DESCRIBE THE PARAMETER
   * @return The Logger value
   */
  public Logger getLogger(Class clazz, String instance) {
    Logger temp;
    String baseStr;
    String className = clazz.getName();
    String[] parts = className.split("\\.");

    // Ex: if clazz.getName() == rice.pastry.socket.PingManager, try:
    // 1) rice.pastry.socket.PingManager
    // 2) rice.pastry.socket
    // 3) rice.pastry
    // 4) rice

    // numParts is how much of the prefix we want to use, start with the full name
    for (int numParts = parts.length; numParts >= 0; numParts--) {
      // build baseStr which is the prefix of the clazz up to numParts
      baseStr = parts[0];
      for (int curPart = 1; curPart < numParts; curPart++) {
        baseStr += "." + parts[curPart];
      }

      // try to find one matching a specific instance
      if (instance != null) {
        temp = getLoggerHelper(baseStr + ":" + instance);
        if (temp != null) {
          return temp;
        }
      }

      // try to find one without the instance
      temp = getLoggerHelper(baseStr);
      if (temp != null) {
        return temp;
      }
    }
    return defaultLogger;
  }

  /**
   * Searches the loggers HT for the searchString, then searches the params for
   * the search string.
   *
   * @param clazz
   * @return
   */
  private Logger getLoggerHelper(String clazz) {
    String searchString = clazz + "_loglevel";

    // see if this logger exists
    if (loggers.contains(searchString)) {
      return (Logger) loggers.get(searchString);
    }

    // see if this logger should exist
    if (params.contains(searchString)) {
      Logger logger = constructLogger(clazz, parseVal(searchString));
      loggers.put(clazz, logger);
      return logger;
    }
    return null;
  }

  /**
   * Gets the TimeSource attribute of the AbstractLogManager object
   *
   * @return The TimeSource value
   */
  public TimeSource getTimeSource() {
    return time;
  }

  /**
   * Gets the PrintStream attribute of the AbstractLogManager object
   *
   * @return The PrintStream value
   */
  public PrintStream getPrintStream() {
    if (enabled) {
      return ps;
    } else {
      return nullPrintStream;
    }
  }

  /**
   * Gets the Prefix attribute of the AbstractLogManager object
   *
   * @return The Prefix value
   */
  public String getPrefix() {
    return prefix;
  }

  /**
   * DESCRIBE THE METHOD
   *
   * @param key DESCRIBE THE PARAMETER
   * @return DESCRIBE THE RETURN VALUE
   */
  protected int parseVal(String key) {
    try {
      return params.getInt(key);
    } catch (NumberFormatException nfe) {
      String val = params.getString(key);

      if (val.equalsIgnoreCase("ALL")) {
        return Logger.ALL;
      }
      if (val.equalsIgnoreCase("OFF")) {
        return Logger.OFF;
      }
      if (val.equalsIgnoreCase("SEVERE")) {
        return Logger.SEVERE;
      }
      if (val.equalsIgnoreCase("WARNING")) {
        return Logger.WARNING;
      }
      if (val.equalsIgnoreCase("INFO")) {
        return Logger.INFO;
      }
      if (val.equalsIgnoreCase("CONFIG")) {
        return Logger.CONFIG;
      }
      if (val.equalsIgnoreCase("FINE")) {
        return Logger.FINE;
      }
      if (val.equalsIgnoreCase("FINER")) {
        return Logger.FINER;
      }
      if (val.equalsIgnoreCase("FINEST")) {
        return Logger.FINEST;
      }
      throw new InvalidLogLevelException(key, val);
    }
  }

  /**
   * DESCRIBE THE METHOD
   *
   * @param clazz DESCRIBE THE PARAMETER
   * @param level DESCRIBE THE PARAMETER
   * @return DESCRIBE THE RETURN VALUE
   */
  protected abstract Logger constructLogger(String clazz, int level);

  /**
   * DESCRIBE THE CLASS
   *
   * @version $Id: pretty.settings,v 1.3 2005/03/11 20:22:33 jeffh Exp $
   * @author jeffh
   */
  private static class NullOutputStream extends OutputStream {
    /**
     * DESCRIBE THE METHOD
     *
     * @param arg0 DESCRIBE THE PARAMETER
     * @exception IOException DESCRIBE THE EXCEPTION
     */
    public void write(int arg0) throws IOException {
      // do nothing
    }

    /**
     * DESCRIBE THE METHOD
     *
     * @param buf DESCRIBE THE PARAMETER
     * @exception IOException DESCRIBE THE EXCEPTION
     */
    public void write(byte[] buf) throws IOException {
      // do nothing
    }

    /**
     * DESCRIBE THE METHOD
     *
     * @param buf DESCRIBE THE PARAMETER
     * @param a DESCRIBE THE PARAMETER
     * @param b DESCRIBE THE PARAMETER
     * @exception IOException DESCRIBE THE EXCEPTION
     */
    public void write(byte[] buf, int a, int b) throws IOException {
      // do nothing
    }
  }
}
