package kawa.lang;

import gnu.kawa.functions.DisplayFormat;
import gnu.kawa.io.CharArrayOutPort;
import gnu.lists.LList;
import gnu.mapping.*;

/** Used to implement R7RS "error object" as created by the error procedure.
 * Also used to implement catch/throw named handlers as in Guile:
  * (catch 'key (lambda () ... (throw 'key ARGS ...) ...)
  *             (lambda (KEY ARGS ...) HANDLER))
  */

public class NamedException extends RuntimeException {
    /** Null for objecs created by error; the key for Guile-style throw. */
    Symbol name;

    /** Arguments to throw or error.
     * For Guile-style throw, args include the key - i.e. args[0]== name.
     * If created by error, the array contains the symbol 'misc-error,
     * followed by the object-message, followed by the object irritants.
     */
    Object[] args;

    static SimpleSymbol miscErrorSymbol = Symbol.valueOf("misc-error");

    /** Assume name==args[1], or name==null. */
    public NamedException(Symbol name, Object[] args) {
        this.name = name;
        this.args = args;
    }

    public static NamedException makeError(Object... args) {
        Object[] xargs = new Object[args.length+1];
        xargs[0] = miscErrorSymbol;
        System.arraycopy(args, 0, xargs, 1, args.length);
        return new NamedException(null, xargs);
    }

    public Object applyHandler(Object key, Procedure handler)
        throws Throwable {
        if (key != this.name && key != Boolean.TRUE)
            throw this;
        return handler.applyN(args);
    }

    public Object getObjectMessage() {
        return name == null ? args[1] : name;
    }

     public LList getObjectIrritants() {
         return LList.makeList(args, name==null ? 2 : 1);
    }

    public String toString() {
        CharArrayOutPort buf = new CharArrayOutPort();
        buf.append("#<ERROR");
        int len = args.length;
        // Skip initial 'misc-error as generated by the error procedure.
        int i = name == null ? 1 : 0;
        DisplayFormat format = DisplayFormat.schemeDisplayFormat;
        for ( ;  i < len;  i++) {
            buf.append(' ');
            format.format(args[i], buf);
            format = DisplayFormat.schemeWriteFormat;
        }
        buf.append(">");
        return buf.toString();
    }
}
