package net.ranides.assira.trace;

import net.ranides.assira.text.Wildcard;
import org.junit.Test;

import java.util.List;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

public class FStackWalker11Test {

    @Test
    public void testCallerType() {
        assertTrue( SI0.runCallerType0() );
    }

    @Test
    public void testCalleeType() {
        assertTrue( SI0.runCalleeType0() );
    }

    @Test
    public void testCallerName() {
        assertTrue( SI0.runCallerName0() );
    }

    @Test
    public void testCalleeName() {
        assertTrue( SI0.runCalleeName0() );
    }

    @Test
    public void testGetTypes0() {
        assertTrue( SI0.runGetTypes0() );
    }

    @Test
    public void testGetNames() {
        assertTrue( SI0.runGetNames0() );
    }

    @Test
    public void testFrame() {
        assertTrue( SI0.runFrame0() );
    }

    private static final class SI0 {

        private SI0() { /* utility class */ }

        public static boolean runCallerType0() {
            SI1.runCallerType1();
            return true;
        }

        public static boolean runCalleeType0() {
            SI1.runCalleeType1();
            return true;
        }

        public static boolean runCallerName0() {
            SI1.runCallerName1();
            return true;
        }

        public static boolean runCalleeName0() {
            SI1.runCalleeName1();
            return true;
        }

        public static boolean runGetTypes0() {
            SI1.runGetTypes1();
            return true;
        }

        public static boolean runGetNames0() {
            SI1.runGetNames1();
            return true;
        }

        public static boolean runFrame0() {
            SI1.runFrame1();
            return true;
        }

    }

    private static final class SI1 {

        private SI1() { /* utility class */ }

        public static void runCallerType1() {
            Class<?> caller = SI0.class;

            Class callerAuto = TraceUtils.getCallerType();
            assertEquals(caller, callerAuto);
        }

        public static void runCalleeType1() {
            Class<?> callee = SI1.class;

            Class calleeAuto = TraceUtils.getCalleeType();
            assertEquals(callee, calleeAuto);
        }

        public static void runCallerName1() {
            String caller = SI0.class.getName();

            String callerAuto = TraceUtils.getCallerName();
            assertEquals(caller, callerAuto);
        }

        public static void runCalleeName1() {
            String callee = SI1.class.getName();

            String calleeAuto = TraceUtils.getCalleeName();
            assertEquals(callee, calleeAuto);
        }

        public static void runGetTypes1() {
            Class<?> callee = SI1.class;
            Class<?> caller = SI0.class;

            List<Class<?>> listAuto = TraceUtils.getTypes();

            if(false) { //NOPMD
                dump(listAuto);
            }

//            assertEquals(listAuto, listSM);
//
//            assertTrue(listSM.size() > 5);
//
//            assertEquals(callee, listSM.get(0));
////            assertEquals(callee, listSR.get(0));
//
//            assertEquals(caller, listSM.get(1));
////            assertEquals(caller, listSR.get(1));
//
//            assertThrows(UnsupportedOperationException.class, ()->{
//                FStackWalker11.test_getTypes(FStackWalker11.RESOLVER_THREAD);
//            });
//            assertThrows(UnsupportedOperationException.class, ()->{
//                FStackWalker11.test_getTypes(FStackWalker11.RESOLVER_EXCEPT);
//            });
        }

        public static void runGetNames1() {
            String callee = SI1.class.getName();
            String caller = SI0.class.getName();

            List<String> listAuto = TraceUtils.getNames();

            if(false) { //NOPMD
                System.out.printf("%n%nauto%n");
                dump(listAuto);
            }

//            assertTrue(
//                    listAuto.equals(listSM) ||
//                            listAuto.equals(listTS) ||
//                            listAuto.equals(listES)
//            );
//
//            assertEquals(listTS, listES);
//
//            assertTrue(listSM.size() > 5);
//            assertTrue(listSM.size() <= listTS.size());
//
//            assertEquals(callee, listSM.get(0));
//            assertEquals(callee, listTS.get(0));
//            assertEquals(callee, listES.get(0));
//
//            assertEquals(caller, listSM.get(1));
//            assertEquals(caller, listTS.get(1));
//            assertEquals(caller, listES.get(1));
        }

        public static void runFrame1() {
            List<StackTraceElement> frames = TraceUtils.getFrames();
            List<String> names = TraceUtils.getNames();

            if(false) { // NOPMD
                System.out.printf("frames = %d%n", frames.size());
                System.out.printf("names = %d%n", names.size());
                dump(frames);
                dump(names);
            }

            Wildcard callee = Wildcard.compile("net.ranides.assira.trace.FStackWalker11Test$SI1.runFrame1(FStackWalker11Test.java:*)");
            Wildcard caller = Wildcard.compile("net.ranides.assira.trace.FStackWalker11Test$SI0.runFrame0(FStackWalker11Test.java:*)");
            Wildcard frame2 = Wildcard.compile("net.ranides.assira.trace.FStackWalker11Test.testFrame(FStackWalker11Test.java:*)");

            assertTrue(callee.test(TraceUtils.getCalleeFrame().toString()));
            assertTrue(caller.test(TraceUtils.getCallerFrame().toString()));

            assertTrue(callee.test(frames.get(0).toString()));
            assertTrue(caller.test(frames.get(1).toString()));
            assertTrue(frame2.test(frames.get(2).toString()));

            assertEquals(frames.size(), names.size());
        }

    }

    @SuppressWarnings("PMD")
    private static void dump(List<?> list) {
        int i = 0;
        for(Object c : list) {
            System.err.printf("%d. %s%n", i++, c);
        }
        System.out.printf("%n");
    }
    
}