/*
 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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 General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package jdk.nashorn.internal.runtime;

import static jdk.nashorn.internal.parser.TokenType.EOF;

import jdk.nashorn.api.scripting.NashornException;
import jdk.nashorn.internal.parser.Lexer;
import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.parser.TokenStream;
import jdk.nashorn.internal.parser.TokenType;

/**
 * Utilities for debugging Nashorn.
 *
 */
public final class Debug {
    private Debug() {
    }

    /**
     * Return the topmost JavaScript frame in a stack trace
     * @param t throwable that contains the stack trace
     * @return line describing the topmost JavaScript frame
     */
    public static String firstJSFrame(final Throwable t) {
        for (final StackTraceElement ste : t.getStackTrace()) {
            if (ECMAErrors.isScriptFrame(ste)) {
                return ste.toString();
            }
        }
        return "<native code>";
    }

    /**
     * Return the topmost JavaScript frame from the current
     * continuation
     * @return line describing the topmost JavaScript frame
     */
    public static String firstJSFrame() {
        return firstJSFrame(new Throwable());
    }

    /**
     * Return a formatted script stack trace string with frames information separated by '\n'.
     * This is a shortcut for {@code NashornException.getScriptStackString(new Throwable())}.
     * @return formatted stack trace string
     */
    public static String scriptStack() {
        return NashornException.getScriptStackString(new Throwable());
    }

    /**
     * Return the system identity hashcode for an object as a human readable
     * string
     *
     * @param x object
     * @return system identity hashcode as string
     */
    public static String id(final Object x) {
        return String.format("0x%08x", System.identityHashCode(x));
    }

    /**
     * Same as {@link Debug#id} but returns the identity hashcode as
     * an integer
     *
     * @param x object
     * @return system identity hashcode
     */
    public static int intId(final Object x) {
        return System.identityHashCode(x);
    }

    /**
     * Return a stack trace element description at a depth from where we are not
     *
     * @param depth depth
     * @return stack trace element as string
     */
    public static String stackTraceElementAt(final int depth) {
        return new Throwable().getStackTrace()[depth + 1].toString(); // add 1 to compensate for this method
    }

    /**
     * Determine caller for tracing purposes.
     * @param depth depth to trace
     * @param count max depth
     * @param ignores elements to ignore in stack trace
     * @return caller
     */
    public static String caller(final int depth, final int count, final String... ignores) {
        String result = "";
        final StackTraceElement[] callers = Thread.currentThread().getStackTrace();

        int c = count;
loop:
        for (int i = depth + 1; i < callers.length && c != 0; i++) {
            final StackTraceElement element = callers[i];
            final String method = element.getMethodName();

            for (final String ignore : ignores) {
                if (method.compareTo(ignore) == 0) {
                    continue loop;
                }
            }

            result += (method + ":" + element.getLineNumber() +
                       "                              ").substring(0, 30);
            c--;
        }

        return result.isEmpty() ? "<no caller>" : result;
    }

    /**
     * Dump a token stream to stdout
     *
     * TODO: most other bugging goes to stderr, change?
     *
     * @param source the source
     * @param lexer  the lexer
     * @param stream the stream to dump
     */
    public static void dumpTokens(final Source source, final Lexer lexer, final TokenStream stream) {
        TokenType type;
        int k = 0;
        do {
            while (k > stream.last()) {
                // Get more tokens.
                lexer.lexify();
            }

            final long token = stream.get(k);
            type = Token.descType(token);
            System.out.println("" + k + ": " + Token.toString(source, token, true));
            k++;
        } while(type != EOF);
    }

}
