/*
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */
package net.ranides.assira.trace;

import java.lang.management.ManagementFactory;
import java.util.concurrent.atomic.AtomicInteger;
import net.ranides.assira.text.ResolveFormatUtils;

/**
 * [N]o [S]tack [T]race Runtime Exception.
 * This exception does not store stacktrace.
 * You can use it for security of performance reasons.
 *
 * Please note, that performance gain is quite big:
 * stacktrace generation is very expensive operation when exception is thrown.
 *
 * Please note, that this exception has unique ID generated at construction time.
 * It uses global, non-blocking, atomic counter.
 *
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 */
public class NSTException extends RuntimeException {

    private static final String EMPTY_TEXT = "";
    
    private static final Object[] EMPTY_ARGS = new Object[0];
    
    private static final AtomicInteger COUNTER = new AtomicInteger(0);
    
    private static final class Li { // NOPMD - lazy init idiom
        
        public static final String VMNAME = ManagementFactory.getRuntimeMXBean().getName();
        
    }
    
    private final String uid;
    private final String pattern;
    private final Object[] arguments;

    /**
     * Creates new exception
     */
    public NSTException() {
        this(EMPTY_TEXT, EMPTY_ARGS);
    }

    /**
     * Creates new exception
     *
     * @param message message
     */
    public NSTException(String message) {
        this(message, EMPTY_ARGS);
    }

    /**
     * Creates new exception.
     * Message is lazily formatted using {@link ResolveFormatUtils#vformat(String, Object...)}
     *
     * @param message message
     * @param arguments arguments
     */
    public NSTException(String message, Object... arguments) {
        this.uid = initUID();
        this.pattern = message;
        this.arguments = arguments;
    }

    /**
     * Creates new exception.
     *
     * @param cause cause
     */
    public NSTException(Throwable cause) {
        this(cause, EMPTY_TEXT, EMPTY_ARGS);
    }

    /**
     * Creates new exception.
     *
     * @param cause cause
     * @param message message
     */
    public NSTException(Throwable cause, String message) {
        this(cause, message, EMPTY_ARGS);
    }

    /**
     * Creates new exception.
     * Mssage is lazily formatted using {@link ResolveFormatUtils#vformat(String, Object...)}
     *
     * @param cause cause
     * @param message message
     * @param arguments arguments
     */
    public NSTException(Throwable cause, String message, Object... arguments) {
        super(cause);
        this.uid = initUID();
        this.pattern = message;
        this.arguments = arguments;
    }

    /**
     * Returns UID generated at construction time.
     * Format of generated UID is undefined and it could be any string.
     *
     * UID will be unique in current instance of VM.
     *
     * We try to generate UID unique across VMs, but it is not guaranteed.
     *
     * @return String
     */
    public String uid() {
        return uid;
    }
    
    @Override
    public Throwable fillInStackTrace() {
        return this;
    }
    
    @Override
    public String getMessage() {
        return ResolveFormatUtils.vformat(pattern, arguments);
    }

    @Override
    public String getLocalizedMessage() {
        return getMessage();
    }

    @Override
    public String toString() {
        String s = getClass().getName();
        String m = getLocalizedMessage();
        return s + "(" + uid + "): " + m;
    }
    
    private static String initUID() {
        return Li.VMNAME + "@" + COUNTER.incrementAndGet();
    }
}
