// Copyright (c) 1996-2002 Brian D. Carlstrom

package bdc.scheme;

import bdc.scheme.exception.PrimitiveException;
import bdc.scheme.expression.Expression;
import bdc.util.Fmt;
import bdc.util.SystemUtil;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

/**
    The superclass of all other Scheme exceptions. This enables us to
    safely protect ourselves from bugs in user code. Scheme itself
    converts general RuntimeExceptions into PrimitiveExceptions so you
    don't have to catch them.
*/
abstract public class SchemeException extends Exception
{
    /**
        As we unwind the stack of an exception we add expressions to this
    */
    public final List backTrace = new ArrayList();

    public SchemeException ()
    {
    }

    public SchemeException (String string)
    {
        super(string);
    }

    /**
        Return a stack trace as a string

        based on SystemUtil.stackTrace
    */
    public String stackTrace ()
    {
        ByteArrayOutputStream string = new ByteArrayOutputStream();
        PrintWriter stream = new PrintWriter(string);
        stackTrace(stream);
        stream.close();
        return string.toString();
    }

    /**
        print a Scheme stack trace to out

        first prints the error

        then loops over backTrace printing from oldest to newest

        then if it was a PrimitiveException, print the java stack trace
    */
    public void stackTrace (PrintWriter out)
    {
        Fmt.F(out, "%s\n", this);

        /*
            loop over backtrace

            print source if we have it, skipping duplicates
        */
        Pair lastPrinted = null;
        for (int i=0, s=backTrace.size(); i<s; i++) {
            Expression expression = (Expression)backTrace.get(i);
            if (expression.source == null ||
                expression.source == lastPrinted)
            {
                continue;
            }
            lastPrinted = expression.source;
            Fmt.F(out, "%s\n", Expression.stackTrace(expression.source));
        }
        if (this instanceof PrimitiveException) {
            PrimitiveException primitiveException = (PrimitiveException)this;
            primitiveException.throwable.printStackTrace(out);
        }
    }
}
