/*
** Module   : LogHelper.java
** Abstract : static utility methods to assist logging 
**
** Copyright (c) 2009-2023, Golden Code Development Corporation.
**
** -#- -I- --Date-- -----------------------Description------------------------
** 001 GES 20090212 Static utility methods to assist logging.
** 002 TJD 20220803 Added logging, updated for Java 11
** 003 TJD 20220808 Changed log format, added option to abort TestSet
** 004 TJD 20220809 Added log level FINER
** 005 TJD 20220817 Allow to check detailed log levels
** 006 TJD 20220819 Move completionMsg to Harness.java
** 007 TJD 20230612 Implemented trace log level, Support for IN_PROGRESS test status
*/

/*
** 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.goldencode.util;

import java.util.logging.*;
/**
 * Static utility methods to assist logging.
 */
public class LogHelper
{
   /** Platform-specific line separator. */
   private static final String SEP = System.getProperty("line.separator");
   
   /** Logger */
   private static Logger LOG;
   
   /** Predefined log format */
   static final String logFormat= "%1$tF %1$tT %2$-7.7s %3$-8s %4$s %5$s"+SEP;
   
   /** Logger initialization */
   static 
   {
      LOG = Logger.getAnonymousLogger();
      LOG.setLevel(Level.FINE);
      LOG.setUseParentHandlers(false);
      Formatter logFormatter = new Formatter()
      {
         @Override
         public String format(LogRecord logRecord)
         {
            return String.format(logFormat, logRecord.getMillis(), logRecord.getLevel(), 
                                   logRecord.getSourceClassName(), logRecord.getMessage(),
                                   logRecord.getThrown() != null ? dumpThrowable(logRecord.getThrown()):"");
         }
      };
//      StreamHandler handler = new StreamHandler(System.out, logFormatter);
      Handler handler = new ConsoleHandler();
      handler.setFormatter(logFormatter);
      handler.setLevel(Level.FINEST);
      LOG.addHandler(handler);
   }
   
   
   /**
    * Instances of this class cannot be created.
    */
   private LogHelper()
   {
   }
   
   /**
    * Dump all details of the throwable into a string. Includes exception type,
    * message, complete stack trace, and chained throwables, if any.
    *
    * @param    t
    *           The throwable to dump.
    * 
    * @return   String containing throwable details.
    */
   public static String dumpThrowable(Throwable t)
   {
      StringBuilder sb = new StringBuilder();
      dumpThrowable(t, sb);
      
      return sb.toString();
   }

   /**
    * Dump all details of the throwable into the given buffer. Includes
    * exception type, message, complete stack trace, and chained throwables,
    * if any.
    *
    * @param    t
    *           The throwable to dump.
    * @param    buf
    *           Buffer into which details are written.
    *
    * @return   The given buffer that was modified. This allows chaining.
    */
   public static StringBuilder dumpThrowable(Throwable t, StringBuilder buf)
   {
      Throwable cause = t;
      
      do
      {
         if (cause != t)
         {
            buf.append("Caused by: ");
         }
         
         buf.append(cause.getClass().getName());
         
         String msg = cause.getMessage();
         
         if (msg != null && msg.trim().length() > 0)
         {
            buf.append(": ");
            buf.append(msg);
         }
         buf.append(SEP);
         
         StackTraceElement[] trace = cause.getStackTrace();
         
         for (int i = 0; i < trace.length; i++)
         {
            buf.append("        at ");
            buf.append(trace[i]);
            buf.append(SEP);
         }
         
         cause = cause.getCause();
      }
      while (cause != null);
      
      return buf;
   }
   
   /**
    * Log warning message string
    * @param    message
    *           message to log
    */
   public static void warn(String message)
   {
      if (!LOG.isLoggable(Level.WARNING))
         return;
      String className = getCallingClassName(3);
      LOG.logp(Level.WARNING, className, null, message);
   }
   
   /**
    * Log info message string
    * @param    message
    *           message to log
    */
   public static void info(String message)
   {
      if (!LOG.isLoggable(Level.INFO))
         return;
      String className = getCallingClassName(3);
      LOG.logp(Level.INFO, className, null, message);
   }
   
   /**
    * Log info message string
    * @param    message
    *           message to log
    */
   public static void fine(String message)
   {
      if (!LOG.isLoggable(Level.FINE))
         return;
      String className = getCallingClassName(3);
      LOG.logp(Level.FINE, className, null, message);
   }
   
   /**
    * Log debug message string
    * @param    message
    *           message to log
    */
   public static void finer(String message)
   {
      if (!LOG.isLoggable(Level.FINER))
         return;
      String className = getCallingClassName(3);
      LOG.logp(Level.FINER, className, null, message);
   }
   
   /**
    * Log trace message string
    * @param    message
    *           message to log
    */
   public static void finest(String message)
   {
      if (!LOG.isLoggable(Level.FINEST))
         return;
      String className = getCallingClassName(3);
      LOG.logp(Level.FINEST, className, null, message);
   }
   
   /**
    * Log debug message string with Throwable
    * @param    message
    *           message to log
    * @param    e
    *           throwable to log
    */
   public static void warn(String message, Throwable e)
   {
      if (!LOG.isLoggable(Level.WARNING))
         return;
      String className = getCallingClassName(3);
      LOG.logp(Level.WARNING, className, null, message, e);
   }
   
   /**
    * Enable debug output
    */
   public static void enableDebug()
   {
      LOG.setLevel(Level.FINER);
   }
   
   /**
    * Enable trace output
    */
   public static void enableTrace()
   {
      LOG.setLevel(Level.FINEST);
   }
   
   /**
    * Enable specified log level
    */
   public static void setLogLevel(String logLevel)
   {
      LOG.setLevel(Level.parse(logLevel));
   }
   
   /**
    * Helper method needed to extract calling class name for logging purposes
    * @param    level
    *           level to look for the class back
    * 
    * @return   String with calling class name.
    */
   private static String getCallingClassName(int level)
   {
      StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
      String className = "";
      if (stackTrace.length>level)
      {
         className = stackTrace[level].getClassName();
         if (className.lastIndexOf('.') > 0 && (className.length() >= (className.lastIndexOf('.')+1)))
            className = className.substring(className.lastIndexOf('.')+1);
      }
      return className;
   }
   
   /**
    * Check if level fine is loggable
    * 
    * @return   true if level fine is loggable
    */
   public static boolean isFineLoggable()
   {
      return LOG.isLoggable(Level.FINE);
   }
   
   /**
    * Check if level finer is loggable
    * 
    * @return   true if level finer is loggable
    */
   public static boolean isFinerLoggable()
   {
      return LOG.isLoggable(Level.FINER);
   }
   
   /**
    * Check if level finest/trace is loggable
    * 
    * @return   true if level finer is loggable
    */
   public static boolean isFinestLoggable()
   {
      return LOG.isLoggable(Level.FINEST);
   }
}
