/* File: Util.java
 * Contains: A class for useful static methods
 * Author: Jeff Dalton <J.Dalton@ed.ac.uk>
 * Created: January 1998
 * Updated: Sun Dec  2 00:27:17 2001 by Jeff Dalton
 * Copyright: (c) 1998 - 2001, AIAI, University of Edinburgh
 */

package ix.util;

import java.util.*;
import java.io.*;

import javax.swing.*;

import ix.util.lisp.*;

/** Class for useful static methods. */

public class Util {

    /** 
     * A method to print the name of the system, the release version,
     * and the release date.
     */
    public static void printGreeting(String name) {
	System.out.println(name
			   + ", I-X version " + ix.Release.version
			   + ", " + ix.Release.date);
	System.out.println("");
    }

    /** 
     * Displays a text message in a dialogue box.
     *
     * @param parentComponent determines the Frame in which dialogs
     *        are displayed.
     * @param text the contents of the message.
     */
    public static void displayMessage(final java.awt.Component parentComponent,
				      final String text) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                JOptionPane.showMessageDialog(parentComponent,
                    text,
                    "Message",
                     JOptionPane.INFORMATION_MESSAGE);
            }
        });
    }

    /**
     * breakStringAtFirst takes a string containing fields separated by
     * a (string) delimiter and returns a two-element string array containing
     * the substring before the first occurrence of the char, and the
     * substring after.  Neither substring contains the delimiter.  If
     * the delimiter does not appear in the string at all, the values
     * are the string and "".
     */
    public static String[] breakStringAtFirst(String s, String separator) {
	int i = s.indexOf(separator);
	if (i == -1)
	    return new String[]{s, ""};
	else
	    return new String[]{
		s.substring(0, i),
	        s.substring(i + separator.length())
	    };
    }

    /**
     * Returns the first line of a string.
     */
    public static String firstLine(String s) {
	// For some reason, it doesn't work on Windows to use
	// System.getProperty("line.separator") here.  /\/

	String[] parts = breakStringAtFirst(s, "\n");
	return parts[0];
    }

    /**
     * Returns the rest of a string after the first line has been removed.
     */
    public static String restLines(String s) {
	// For some reason, it doesn't work on Windows to use
	// System.getProperty("line.separator") here.  /\/

	String[] parts = breakStringAtFirst(s, "\n");
	return parts[1];
    }

    /**
     * Returns a List of the lines in a string.
     */
    public static List breakIntoLines(String text) {
	LListCollector lines = new LListCollector();
	while (!text.equals("")) {
	    // For some reason, it doesn't work on Windows to
	    // use lineSpearator here.  /\/
	    String[] parts = Util.breakStringAtFirst(text, "\n");
	    lines.add(parts[0]);
	    text = parts[1];
	}
	return lines.contents();
    }

    /**
     * Puts double quotes around a string.  This is useful to indicate
     * the boundaries of the string when it's included in other text
     * such as in an error or warning message.
     */
    public static String quote(String text) {
	return "\"" + text + "\"";
    }


    /**
     * Returns a copy of the string in which the first character
     * is in upper case and the rest of the string is left as it was.
     */
    public static String capitalizeString(String s) {
	return s.substring(0,1).toUpperCase() +  s.substring(1);
    }


    /**
     * Returns a String made by appending a specified string <tt>count</tt>
     * times.
     */
    public static String repeatString(String s, int count) {
	StringBuffer buf = new StringBuffer();
	for (int i = 0; i < count; i++)
	    buf.append(s);
	return buf.toString();
    }


    /**
     * Name generator a la gensym.
     */
    public static class NameGenerator {
	protected Map counters = new HashMap();
	public NameGenerator() { }
	public String nextName(String base) {
	    Long count = (Long)counters.get(base);
	    if (count == null) {
		count = new Long(0);
		counters.put(base, count);
	    }
	    long c = count.longValue();
	    counters.put(base, new Long(c + 1));
	    return base + "-" + c;
	}
    }

    private static NameGenerator defaultNameGenerator = new NameGenerator();

    /**
     * Generates a name unique within this VM that begins with the
     * specified base string.  (Note that "unique" means unique among
     * the names generated in this way; the same string might, of course,
     * be constructed in some other way.)
     */
    public static String generateName(String base) {
	return defaultNameGenerator.nextName(base);
    }


    /**
     * Print the elements of a String[] array to System.out as lines.
     */
    public static void printLines(String[] lines) {
	for (int i = 0; i < lines.length; i++) {
	    System.out.println(lines[i]);
	}
    }


    /**
     * Simple, text-based user interaction.  askLine prints a prompt
     * to System.out and returns a String containing the next line
     * from System.in.<p>
     *
     * If askLine blocks when reading, we'd like other threads to be
     * able to run; but that doesn't seem to happen reliably.  Presumably,
     * this is a bug.  In any case, askLine works around the problem by
     * having a loop that checks whether input is available and sleeps
     * for a second if it isn't.
     */
    public static String askLine(String prompt) {
	System.out.print(prompt + " ");
	System.out.flush();
	// /\/: Should we synchronize on System.in?
	try {
	    while (System.in.available() == 0) {
		try { Thread.sleep(1000); }
		catch (InterruptedException e) {}
	    }
	}
	catch (IOException e) {
	    Debug.noteException(e);
	    return "";
	}
	return Util.readLine(System.in);
    }


    /** 
     * Reads a line from an InputStream and returns it as a String.
     * In Java, we seem to have to write this ourself unless we wrap
     * a special stream (or Reader) around whatever we want to read.
     * Here we provide a static method, because that's easier to
     * mix with other operations.  The only InputStream method
     * called is read().
     */
    public static String readLine(InputStream is) {
	ByteArrayOutputStream line = new ByteArrayOutputStream(80);

    readLoop:
	while (true) {
	    int c;		// a character as a "byte"
	    try { c = is.read(); }
	    catch (IOException e) {
		Debug.warn("IOException reading line from " + is);
		break readLoop;
	    }
	    if (c == '\n') 	// newline
		break readLoop;
	    else if (c == -1)	// end of stream
		break readLoop;
	    else if (c == '\r') // CR
		;		// (ignore)
	    else
		line.write(c);
	}
	return line.toString();
    }


}

// Issues:
// * Consider breaking this into separate classes such as StringUtil.

