import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class JavaVivaDemo {
    static void say(String q, String s) { System.out.printf("%s %s%n", q, s); }

    // ---------- BASICS ----------
    // BEGIN q01
    static void q01() {
        say("Q01", "java.specification.version=" + System.getProperty("java.specification.version")
                + ", vm=" + System.getProperty("java.vm.name"));
    }
    // END q01
    // BEGIN q03
    static void q03() {
        say("Q03", "byte=" + Byte.SIZE + " short=" + Short.SIZE + " char=" + Character.SIZE + " int=" + Integer.SIZE
                + " long=" + Long.SIZE + " float=" + Float.SIZE + " double=" + Double.SIZE + " (bits)");
        say("Q03", "int max=" + Integer.MAX_VALUE + ", max+1=" + (Integer.MAX_VALUE + 1));
    }
    // END q03
    // BEGIN q04
    static int dInt; static boolean dBool; static String dStr; static double dDouble; static char dChar;
    static void q04() {
        say("Q04", "int=" + dInt + " boolean=" + dBool + " String=" + dStr + " double=" + dDouble + " char=" + (int) dChar);
    }
    // END q04
    // BEGIN q05
    static void q05() {
        String a = new String("java"), b = new String("java");
        say("Q05", "a == b: " + (a == b) + ", a.equals(b): " + a.equals(b));
    }
    // END q05
    // BEGIN q06
    static void q06() {
        String s1 = "java", s2 = "java", s3 = new String("java");
        say("Q06", "s1 == s2: " + (s1 == s2) + ", s1 == s3: " + (s1 == s3) + ", s1 == s3.intern(): " + (s1 == s3.intern()));
        String t = "abc"; t.toUpperCase();
        say("Q06", "after t.toUpperCase() t is still " + t);
    }
    // END q06
    // BEGIN q08
    static void q08() {
        Integer a = 127, b = 127, c = 128, d = 128;
        say("Q08", "127 == 127: " + (a == b) + ", 128 == 128: " + (c == d) + ", 128 equals 128: " + c.equals(d));
    }
    // END q08
    // BEGIN q09
    static void change(int n, StringBuilder sb, int[] arr) { n = 99; sb.append("!"); arr = new int[]{7}; }
    static void q09() {
        int n = 1; StringBuilder sb = new StringBuilder("hi"); int[] arr = {1};
        change(n, sb, arr);
        say("Q09", "n=" + n + " sb=" + sb + " arr[0]=" + arr[0]);
    }
    // END q09

    // ---------- OOP ----------
    // BEGIN q13
    static class A { A() { this("x"); System.out.println("Q13 A()"); } A(String s) { System.out.println("Q13 A(String) " + s); } }
    static class B extends A { B() { super(); System.out.println("Q13 B()"); } }
    static void q13() { new B(); }
    // END q13
    // BEGIN q14
    static class Calc {
        String add(int a, int b) { return "int,int"; }
        String add(double a, double b) { return "double,double"; }
        String add(Object a) { return "Object"; }
    }
    static class Calc2 extends Calc { @Override String add(int a, int b) { return "overridden int,int"; } }
    static void q14() {
        Calc c = new Calc2();
        say("Q14", "add(1,2)=" + c.add(1, 2) + ", add(1,2.0)=" + c.add(1, 2.0) + ", add(\"s\")=" + c.add("s"));
    }
    // END q14
    // BEGIN q15
    static class Animal { String name = "animal"; String speak() { return "..."; } }
    static class Dog extends Animal { String name = "dog"; @Override String speak() { return "woof"; } }
    static void q15() {
        Animal a = new Dog();
        say("Q15", "a.speak()=" + a.speak() + " (runtime type), a.name=" + a.name + " (declared type)");
    }
    // END q15
    // BEGIN q17
    static class Base {
        static { System.out.println("Q17 Base static block"); }
        { System.out.println("Q17 Base instance block"); }
        Base() { System.out.println("Q17 Base constructor"); }
    }
    static class Derived extends Base {
        static { System.out.println("Q17 Derived static block"); }
        { System.out.println("Q17 Derived instance block"); }
        Derived() { System.out.println("Q17 Derived constructor"); }
    }
    static void q17() { new Derived(); System.out.println("Q17 -- second object --"); new Derived(); }
    // END q17
    // BEGIN q19
    record Point(int x, int y) { Point { if (x < 0) throw new IllegalArgumentException("x < 0"); } }
    static void q19() {
        Point p = new Point(1, 2);
        say("Q19", p + ", equals=" + p.equals(new Point(1, 2)) + ", hashCode equal=" + (p.hashCode() == new Point(1, 2).hashCode()));
        try { new Point(-1, 0); } catch (IllegalArgumentException e) { say("Q19", "compact constructor rejects: " + e.getMessage()); }
    }
    // END q19
    // BEGIN q20
    static class Bad { int id; Bad(int id) { this.id = id; } @Override public boolean equals(Object o) { return o instanceof Bad b && b.id == id; } }
    static class Good extends Bad { Good(int id) { super(id); } @Override public int hashCode() { return Integer.hashCode(id); } }
    static void q20() {
        Set<Bad> s1 = new HashSet<>(); s1.add(new Bad(1)); s1.add(new Bad(1));
        Set<Bad> s2 = new HashSet<>(); s2.add(new Good(1)); s2.add(new Good(1));
        say("Q20", "equals without hashCode: set size " + s1.size() + "; with hashCode: set size " + s2.size());
    }
    // END q20
    // BEGIN q21
    static class Box implements Cloneable {
        int[] data = {1, 2, 3};
        @Override protected Box clone() throws CloneNotSupportedException { return (Box) super.clone(); }   // shallow
    }
    static void q21() throws Exception {
        Box a = new Box(), shallow = a.clone();
        Box deep = new Box(); deep.data = a.data.clone();
        a.data[0] = 99;
        say("Q21", "after a.data[0]=99: shallow copy sees " + shallow.data[0] + ", deep copy sees " + deep.data[0]);
    }
    // END q21
    // BEGIN q23
    sealed interface Shape permits Circle, Square {}
    record Circle(double r) implements Shape {}
    record Square(double side) implements Shape {}
    static String describe(Object o) {
        return switch (o) {
            case Circle c when c.r() > 10 -> "big circle";
            case Circle c -> "circle r=" + c.r();
            case Square s -> "square " + s.side();
            case String s -> "string of length " + s.length();
            default -> "something else";
        };
    }
    static void q23() {
        Object o = "hello";
        if (o instanceof String s && s.length() > 3) say("Q23", "instanceof pattern: " + s.toUpperCase());
        say("Q23", describe(new Circle(2)) + " | " + describe(new Circle(20)) + " | " + describe(new Square(3)) + " | " + describe("abc") + " | " + describe(1));
    }
    // END q23

    // ---------- EXCEPTIONS ----------
    // BEGIN q26
    @SuppressWarnings("finally")
    static int finallyReturn() { try { return 1; } finally { return 2; } }
    static int finallyOrder() {
        int x = 1;
        try { return x; } finally { x = 5; System.out.println("Q26 finally runs after the return value is fixed, x=" + x); }
    }
    static void q26() {
        say("Q26", "finally { return 2 } overrides try { return 1 }: " + finallyReturn());
        say("Q26", "try { return x } with finally changing x returns " + finallyOrder());
        try { Object o = "s"; Integer i = (Integer) o; }
        catch (ClassCastException e) { say("Q26", "caught " + e.getClass().getSimpleName()); }
        finally { say("Q26", "finally always runs"); }
    }
    // END q26
    // BEGIN q27
    static class Res implements AutoCloseable {
        final String n; Res(String n) { this.n = n; System.out.println("Q27 open " + n); }
        public void close() { System.out.println("Q27 close " + n); }
    }
    static void q27() {
        try (Res a = new Res("A"); Res b = new Res("B")) { System.out.println("Q27 body"); throw new RuntimeException("boom"); }
        catch (RuntimeException e) { System.out.println("Q27 catch " + e.getMessage()); }
    }
    // END q27
    // BEGIN q31
    static int depth = 0;
    static void recurse() { depth++; recurse(); }
    static void q31() {
        try { recurse(); } catch (StackOverflowError e) { say("Q31", "caught " + e.getClass().getSimpleName() + ", depth reached > 1000: " + (depth > 1000)); }
        say("Q31", "StackOverflowError is an Error: " + (new StackOverflowError() instanceof Error) + ", Exception: " + (((Object) new StackOverflowError()) instanceof Exception));
    }
    // END q31
    // BEGIN q32
    static void q32() {
        try {
            try { Integer.parseInt("abc"); }
            catch (NumberFormatException e) { throw new IllegalStateException("config invalid", e); }
        } catch (IllegalStateException e) {
            say("Q32", e.getMessage() + " <- caused by " + e.getCause().getClass().getSimpleName() + ": " + e.getCause().getMessage());
        }
    }
    // END q32

    // ---------- COLLECTIONS ----------
    // BEGIN q35
    static void q35() {
        Map<String, Integer> m = new HashMap<>();
        m.put(null, 1); m.put("a", null);
        say("Q35", "HashMap allows a null key and null value: " + m);
    }
    // END q35
    // BEGIN q36
    static void q36() {
        try { new Hashtable<String, Integer>().put(null, 1); } catch (NullPointerException e) { say("Q36", "Hashtable.put(null, 1) -> NullPointerException"); }
        try { new ConcurrentHashMap<String, Integer>().put("k", null); } catch (NullPointerException e) { say("Q36", "ConcurrentHashMap.put(\"k\", null) -> NullPointerException"); }
    }
    // END q36
    // BEGIN q37
    static void q37() {
        List<String> in = List.of("pear", "apple", "fig", "apple", "banana");
        say("Q37", "HashSet=" + new HashSet<>(in) + " LinkedHashSet=" + new LinkedHashSet<>(in) + " TreeSet=" + new TreeSet<>(in));
    }
    // END q37
    // BEGIN q38
    static void q38() {
        List<Integer> l = new ArrayList<>(List.of(1, 2, 3, 4));
        try { for (Integer x : l) if (x == 2) l.remove(x); }
        catch (ConcurrentModificationException e) { say("Q38", "removing inside for-each -> ConcurrentModificationException"); }
        l = new ArrayList<>(List.of(1, 2, 3, 4));
        for (Iterator<Integer> it = l.iterator(); it.hasNext(); ) if (it.next() % 2 == 0) it.remove();
        say("Q38", "Iterator.remove() is safe: " + l);
    }
    // END q38
    // BEGIN q39
    record Emp(String name, int age) {}
    static void q39() {
        List<Emp> l = new ArrayList<>(List.of(new Emp("Zed", 30), new Emp("Amy", 25), new Emp("Bob", 30)));
        l.sort(Comparator.comparingInt(Emp::age).thenComparing(Emp::name));
        say("Q39", "by age then name: " + l);
        l.sort(Comparator.comparing(Emp::name).reversed());
        say("Q39", "by name descending: " + l);
    }
    // END q39
    // BEGIN q40
    static void q40() {
        List<Integer> fixed = Arrays.asList(1, 2, 3);
        fixed.set(0, 9);
        try { fixed.add(4); } catch (UnsupportedOperationException e) { say("Q40", "Arrays.asList: set works " + fixed + ", add -> UnsupportedOperationException"); }
        try { List.of(1).set(0, 2); } catch (UnsupportedOperationException e) { say("Q40", "List.of(...).set -> UnsupportedOperationException"); }
    }
    // END q40
    // BEGIN q41
    static class Key { int v; Key(int v) { this.v = v; } @Override public boolean equals(Object o) { return o instanceof Key k && k.v == v; } @Override public int hashCode() { return v; } }
    static void q41() {
        Map<Key, String> m = new HashMap<>(); Key k = new Key(1); m.put(k, "x");
        k.v = 2;
        say("Q41", "after mutating the key: get(k)=" + m.get(k) + ", get(new Key(1))=" + m.get(new Key(1)) + ", size=" + m.size());
    }
    // END q41
    // BEGIN q42
    static void q42() {
        PriorityQueue<Integer> pq = new PriorityQueue<>(List.of(5, 1, 4, 2, 3));
        say("Q42", "toString (heap order, not sorted): " + pq);
        StringBuilder sb = new StringBuilder(); while (!pq.isEmpty()) sb.append(pq.poll()).append(' ');
        say("Q42", "poll() order: " + sb.toString().trim());
    }
    // END q42

    // ---------- THREADS ----------
    // BEGIN q46
    static void q46() throws Exception {
        Thread t1 = new Thread(() -> System.out.println("Q46 Runnable lambda on " + Thread.currentThread().getName()), "worker-1");
        t1.start(); t1.join();
        ExecutorService ex = Executors.newSingleThreadExecutor();
        Future<Integer> f = ex.submit(() -> 6 * 7);
        say("Q46", "Callable via executor returned " + f.get());
        ex.shutdown();
    }
    // END q46
    // BEGIN q47
    static void q47() throws Exception {
        Thread t = new Thread(() -> System.out.println("Q47 body runs on " + Thread.currentThread().getName()), "T");
        t.run();
        Thread t2 = new Thread(() -> System.out.println("Q47 body runs on " + Thread.currentThread().getName()), "T");
        t2.start(); t2.join();
    }
    // END q47
    // BEGIN q48
    static void q48() throws Exception {
        Thread t = new Thread(() -> { });
        String s1 = t.getState().toString();
        t.start(); t.join();
        Object lock = new Object();
        Thread w = new Thread(() -> { synchronized (lock) { try { lock.wait(); } catch (InterruptedException e) { } } });
        w.start(); while (w.getState() != Thread.State.WAITING) Thread.sleep(5);
        String s3 = w.getState().toString();
        synchronized (lock) { lock.notifyAll(); } w.join();
        say("Q48", "new=" + s1 + ", after join=" + t.getState() + ", inside wait()=" + s3);
    }
    // END q48
    // BEGIN q49
    static int plain = 0; static final AtomicInteger atomic = new AtomicInteger(); static int sync = 0;
    static synchronized void incSync() { sync++; }
    static void q49() throws Exception {
        Runnable r = () -> { for (int i = 0; i < 200_000; i++) { plain++; atomic.incrementAndGet(); incSync(); } };
        Thread a = new Thread(r), b = new Thread(r); a.start(); b.start(); a.join(); b.join();
        say("Q49", "expected 400000: plain int++=" + plain + ", AtomicInteger=" + atomic.get() + ", synchronized=" + sync);
    }
    // END q49
    // BEGIN q53
    static void q53() throws Exception {
        ExecutorService pool = Executors.newFixedThreadPool(3);
        List<Callable<String>> tasks = new ArrayList<>();
        for (int i = 1; i <= 3; i++) { int n = i; tasks.add(() -> "task" + n); }
        List<String> out = new ArrayList<>();
        for (Future<String> f : pool.invokeAll(tasks)) out.add(f.get());
        pool.shutdown();
        say("Q53", "invokeAll results in submission order: " + out + ", terminated=" + pool.awaitTermination(1, TimeUnit.SECONDS));
    }
    // END q53
    // BEGIN q54
    static void q54() throws Exception {
        Thread v = Thread.ofVirtual().name("v1").start(() -> { });
        v.join();
        say("Q54", "virtual=" + v.isVirtual() + ", daemon=" + v.isDaemon());
        try (var ex = Executors.newVirtualThreadPerTaskExecutor()) {
            List<Future<Integer>> fs = new ArrayList<>();
            for (int i = 0; i < 10_000; i++) { int n = i; fs.add(ex.submit(() -> { Thread.sleep(10); return n; })); }
            long sum = 0; for (Future<Integer> f : fs) sum += f.get();
            say("Q54", "10,000 virtual threads sleeping 10 ms each finished, sum=" + sum);
        }
    }
    // END q54

    public static void main(String[] args) throws Exception {
        q01(); q03(); q04(); q05(); q06(); q08(); q09();
        q13(); q14(); q15(); q17(); q19(); q20(); q21(); q23();
        q26(); q27(); q31(); q32();
        q35(); q36(); q37(); q38(); q39(); q40(); q41(); q42();
        q46(); q47(); q48(); q49(); q53(); q54();
    }
}
