//*****************************************************************************
/*
** FILE:   eh_Debug.java
**
** (c) 1997 Steve Withall.
**
** HISTORY:
**    14Sep97  stevew  Created this file, based on eh_DebugFrame and C++
**                      class VEDebug.
**    04Apr98  stevew  Switched to Swing.
*/
package eh;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.Throwable;

//*****************************************************************************
/** <p>eh_Debug is a vehicle for recording debug output, which can be passed
 *  to a visual List (which is typically in a debug window - such as
 *  eh_DebugFrame) and/or a flat file. (TBD The flat file part is not yet
 *  implemented - with only ancestral C++ code present.)</p>
 *
 *  <p>Everything about this class is static, for convenience of use - so it can
 *  be invoked all over the place without having to worry about instantiating a
 *  debug object.</p>
 *
 *  <p>For performance reasons, the display output is buffered and only updated
 *  on screen periodically (every 1 second, by default).</p>
 */
public class eh_Debug
{
	/** Working variable into which information to be added to the DebugBox
	 *  may be placed. */
	protected static String         DebugString = "";

    // Attributes controlling indenting.
	/** The string to be shown at the start of each line. */
	protected static String         IndentString = new String();

	/** How many times should the UnitIndentString be shown at the
	 *  beginning of each line? */
	protected static int            IndentLevel = 0;

	/** The characters making up each indent level. */
	protected static String         UnitIndentString = "  ";

    // Attributes relating to on-screen display of debug information.
	/** Manager for debug output to the screen. */
    protected static eh_DebugListModel  DebugListModel;

	/** How important does debug information have to be before it is
	 *  to be placed in the DebugBox? */
	protected static int            DebugBoxImportanceLevel = 6;

    protected static int            DisplayUpdateInterval = 1000;
    /** The frequency with which the debug box is to be updated (in milliseconds). */

    protected static int            NextDisplayTime  = (int)System.currentTimeMillis();
    /** The time at which the debug box becomes due for redisplay. */

    // Attributes relating to the output of debug information to a file.
	/** The name of the debug file. */
	protected static String         DebugFileName = "";

	/** How important does debug information have to be before it is
	 *  to be written to the DebugFile? */
	protected static int            DebugFileImportanceLevel = 0;

    /** The debug file itself. */
//	protected static File           DebugFile;

    //*****************************************************************************
    /** Add InputString to debug output if InputImportanceLevel is high enough.
     *
     *  @param  InputImportanceLevel    The importance level
     *  @param  InputString             The string to output
     */
	public static void add(int     InputImportanceLevel,
					       String  InputString)
    {
        // Is this information important enough to log?
	    if (    (InputImportanceLevel > eh_Debug.DebugBoxImportanceLevel)
		     && (InputImportanceLevel > eh_Debug.DebugFileImportanceLevel) )
		    return;

        // Add indent if required.
	    if (eh_Debug.IndentLevel == 0)
		    eh_Debug.DebugString = InputString;
	    else
		    eh_Debug.DebugString = eh_Debug.IndentString + InputString;

        // Proceed to log the string.
	    eh_Debug.logDebugString(InputImportanceLevel);
    }

    //*****************************************************************************
	/** Output a long string as multiple lines, each of length InputLineLength (or
	 *  80, whichever is less). Each output line will be prefixed by
	 *  InputIndentLevel spaces.
	 */
	public static void addLongString(int     InputImportanceLevel,
						             String  InputString)
	{
    	addLongString(InputImportanceLevel, InputString, 80);
	}

    //*****************************************************************************
	/** Output a long string as multiple lines, each of length InputLineLength (or
	 *  80, whichever is less). Each output line will be prefixed by
	 *  InputIndentLevel spaces.
	 */
	public static void addLongString(int     InputImportanceLevel,
							         String  InputString,
							         int     InputLineLength)
	{
//	    char   NextLine[81];
//	    NextLine[80] = '\0';

//	    const char*  CString      = InputString.gets();
//	    int          StringLength = InputString.size();
//	    int          CurrentLongStringOffset  = 0;

//	    while (CurrentLongStringOffset <= StringLength)
//	    {
//		    strncpy(&NextLine[0],
//				    CString + CurrentLongStringOffset,
//				    InputLineLength);
//		    eh_Debug.add(InputImportanceLevel, &NextLine[0]);
//		    CurrentLongStringOffset += InputLineLength;
//	    }

	    return;
	}

    //*****************************************************************************
    /** If InputCondition is false, output InputString to debug.
     *
     *  @param  InputCondition  The condition we assert should be true
     *  @param  InputString     The string to output
     */
	public static void assert(boolean  InputCondition,
					          String   InputString)
    {
        if (!InputCondition)
            add(10, InputString);
        // (Use a high importance level of 10 to ensure the string is output.)
    }

    //*****************************************************************************
    /** Output the stack trace of the InputThrowable to debug.
     *
     *  @param  InputImportanceLevel  The importance level
     *  @param  InputThrowable        The throwable whose stack trace is to be output
     */
	public static void printStackTrace(int        InputImportanceLevel,
							           Throwable  InputThrowable)
    {
        StringWriter  StackTraceStringWriter = new StringWriter();
        PrintWriter   StackTracePrintWriter  = new PrintWriter(StackTraceStringWriter);
        InputThrowable.printStackTrace(StackTracePrintWriter);
        add(InputImportanceLevel, StackTraceStringWriter.toString() );
    }

    //*****************************************************************************
	/** Open the debug file. If InputClearFlag is TRUE, ensure that all previous
	 *  contents of the file are removed first. A return value of true indicates
	 *  that the file was opened successfully.
	 */
	public static boolean openFile(boolean  InputClearFlag)
	{
        // If the file is open, close it.
//	    if (DebugFile.isOpen() )
//		    DebugFile.close();

        // Set the file name (in case it has been changed since last time).
//	    DebugFile.file(DebugFileName);

        // If the file needs to be cleared, clear it.
	    boolean  SuccessFlag = true;
//	    if (InputClearFlag)
//		    SuccessFlag = DebugFile.truncAt(0);

        // Open the file.
//	    if (SuccessFlag)
//		    SuccessFlag = DebugFile.open(Create);

	    return(SuccessFlag);
	}

    //*****************************************************************************
	/** Close the debug file. A return value of TRUE indicates that the file was
	 *  closed successfully or was already closed.
	 */
	public static boolean closeFile()
	{
//	    if (DebugFile.isOpen() )
//		    DebugFile.close();
	    return true;
	}

    //*****************************************************************************
	/** Clear the debug file. If the file was open prior to this call, it will be
	 *  opened again. A return value of true indicates that everything was
	 *  successful.
	 */
	public static boolean resetFile()
	{
	    closeFile();
    	return(openFile(true) );
	}

    //*****************************************************************************
    // Attribute setting functions.
    //*****************************************************************************
	public static void incrementIndentLevel()
	{
	    IndentLevel++;
	    IndentString = IndentString + UnitIndentString;
	    return;
	}

    //*****************************************************************************
	public static void decrementIndentLevel()
	{
	    if (IndentLevel <= 0)
		    return;

	    IndentLevel--;
	    IndentString = "";

	    if (IndentLevel > 0)
	    {
		    for (int i = 0; i < IndentLevel; i++)
		    {
			    IndentString.concat(UnitIndentString);
		    }
	    }
	    return;
	}

    //*****************************************************************************
	public static void setUnitIndentString(String  InputUnitIndentString)
	{
	    UnitIndentString = InputUnitIndentString;
	}

    //*****************************************************************************
	public static void setDebugListModel(eh_DebugListModel  InputDebugListModel)
	{
        DebugListModel = InputDebugListModel;
	}

    //*****************************************************************************
	public static void setDebugBoxMaxChars(int  InputMaxLines)
	{
	    DebugListModel.setDebugBoxMaxChars(InputMaxLines);
	}

    //*****************************************************************************
	public static void setDebugBoxImportanceLevel(int  InputImportanceLevel)
	{
	    DebugBoxImportanceLevel = InputImportanceLevel;
	}

    //*****************************************************************************
	public static void setDebugFileName(String  InputDebugFileName)
	{
	    DebugFileName = InputDebugFileName;
	}

    //*****************************************************************************
	public static void setDebugFileImportanceLevel(int  InputImportanceLevel)
	{
	    DebugFileImportanceLevel = InputImportanceLevel;
	}

    //*****************************************************************************
    // Attribute retrieval functions.
    //*****************************************************************************
    public static eh_DebugListModel getDebugListModel()
    {
	    return DebugListModel;
    }

    //*****************************************************************************
    public static String getDebugString()
    {
	    return DebugString;
    }

    //*****************************************************************************
    public static void getDebugString(String  OutputDebugString)
    {
	    OutputDebugString = DebugString;
    }

    //*****************************************************************************
    public static int getDebugBoxMaxChars()
    {
	    return DebugListModel.getDebugBoxMaxChars();
    }

    //*****************************************************************************
    public static int getDebugBoxImportanceLevel()
    {
	    return DebugBoxImportanceLevel;
    }

    //*****************************************************************************
    public static String getDebugFileName()
    {
	    return DebugFileName;
    }

    //*****************************************************************************
    public static void getDebugFileName(String  OutputDebugFileName)
    {
	    OutputDebugFileName = DebugFileName;
    }

    //*****************************************************************************
    public static int getDebugFileImportanceLevel()
    {
	    return DebugFileImportanceLevel;
    }

    //*****************************************************************************
    //*****************************************************************************
	/** Log eh_Debug.DebugString to the list box and/or the debug file if required.
	 */
	protected static void logDebugString(int  InputImportanceLevel)
    {
        // Is this information important enough to log to the screen,
        // and is there a box to put it in?
	    if (    (InputImportanceLevel <= eh_Debug.DebugBoxImportanceLevel)
		     && (eh_Debug.DebugListModel != null) )
	    {
	        // Log the string.
	        DebugListModel.add(DebugString);

            int CurrentTime = (int)System.currentTimeMillis();
            if (CurrentTime > NextDisplayTime)
            {
                NextDisplayTime = CurrentTime + DisplayUpdateInterval;

	            // Create thread to update the screen after a delay.
		        eh_DebugThread  ScreenRefreshThread = new eh_DebugThread();
        		ScreenRefreshThread.setDebugListModel(DebugListModel);
                ScreenRefreshThread.setDisplayUpdateInterval(DisplayUpdateInterval);
                ScreenRefreshThread.start();
    	    }
	    }

        // Is this information important enough to log to the file,
        // and is there a file to write to?
	    if (InputImportanceLevel <= eh_Debug.DebugFileImportanceLevel)
	    {
		    boolean  SuccessFlag = true;
//		    if (!DebugFile.isOpen() )
//			    SuccessFlag = openFile(false);

		    // Proceed to log the string.
		    if (SuccessFlag)
		    {
//			    DebugFile.write(eh_Debug.DebugString.gets(),
//							    eh_Debug.DebugString.size() );
//			    DebugFile.write("\r\n", 2);
		    }
	    }

	    return;
    }
}

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