/*
 *  @author Ranides Atterwim <ranides@gmail.com>
 *  @copyright Ranides Atterwim
 *  @license WTFPL
 *  @url http://ranides.net/projects/assira
 */

package net.ranides.assira.trace;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;
import java.util.regex.Matcher;
import net.ranides.assira.collection.list.ListAdapter;

/**
 *
 * @author ranides
 */
public final class StackInspector {

    public static final int CALLEE = 1;
    public static final int CALLER = 2;

    private StackInspector() { }
    
    public static StackTraceElement frame() {
        return new Throwable().getStackTrace()[CALLEE];
    }

    public static StackTraceElement frame(int index) {
        return new Throwable().getStackTrace()[index];
    }

    public static Class<?> frameClass(int index) {
        try {
            return Class.forName( new Throwable().getStackTrace()[index].getClassName() );
        } catch (ClassNotFoundException ex) {
            throw new NoClassDefFoundError(ex.getMessage());
        }
    }

    public static String frameClassName(int index) {
        return new Throwable().getStackTrace()[index].getClassName();
    }

    public static String frameFile(int index) {
        return new Throwable().getStackTrace()[index].getFileName();
    }

    public static StackTraceList getFrames() {
        return new StackTraceList(0, new Throwable().getStackTrace());
    }

    public static StackTraceList getCallers() {
        return new StackTraceList(CALLER, new Throwable().getStackTrace());
    }

/* ************************************************************************** */

    public static class StackTraceList extends ArrayList<StackTraceElement> {

        public StackTraceList() {
            /* do nothing */
        }

        public StackTraceList(int size) {
            super(size);
        }

        public StackTraceList(int from, StackTraceElement[] values) {
            super(values.length - from);
            for(int i=from; i<values.length; i++) { add(values[i]); }
        }

        public List<String> classes() {
            return new ListAdapter<String, StackTraceElement>(this){
                @Override public String apply(StackTraceElement source) {
                    return source.getClassName();
                }
            };
        }

        public List<String> files() {
            return new ListAdapter<String, StackTraceElement>(this){
                @Override public String apply(StackTraceElement source) {
                    return source.getFileName();
                }
            };
        }

        public List<String> methods() {
            return new ListAdapter<String, StackTraceElement>(this){
                @Override public String apply(StackTraceElement source) {
                    return source.getMethodName();
                }
            };
        }

        public StackTraceList selectClass(Class<?> clazz) {
            String name = clazz.getName();
            BitSet allowed = new BitSet(size());
            for(int i=0, n=size(); i<n; i++) {
                allowed.set(i, get(i).getClassName().equals(name) );
            }
            return filter(allowed);
        }

        public StackTraceList selectClassName(String name) {
            BitSet allowed = new BitSet(size());
            for(int i=0, n=size(); i<n; i++) {
                allowed.set(i, get(i).getClassName().startsWith(name) );
            }
            return filter(allowed);
        }

        public StackTraceList selectMethod(String name) {
            BitSet allowed = new BitSet(size());
            for(int i=0, n=size(); i<n; i++) {
                allowed.set(i, get(i).getMethodName().equals(name) );
            }
            return filter(allowed);
        }

        public StackTraceList selectFile(String name) {
            String mask = Matcher.quoteReplacement(name.trim()).replaceAll("\\*", ".*");
            BitSet allowed = new BitSet(size());
            for(int i=0, n=size(); i<n; i++) {
                allowed.set(i, get(i).getFileName().matches(mask) );
            }
            return filter(allowed);
        }

        private StackTraceList filter(BitSet allowed) {
            StackTraceList list = new StackTraceList(allowed.cardinality());
            for(int i=0, n=size(); i<n; i++) {
                if(allowed.get(i)) { list.add(get(i)); }
            }
            return list;
        }


    }

}
