import edu.rice.cs.mint.runtime.*;

import javax.swing.*;

public final class Limitations2 {
    public static interface Lambda {
        public int apply();
    }
    public static final class FinalLambda1 implements Lambda {
        public int apply() { return 1; }
    }
    public static final class FinalLambda2 implements Lambda {
        public int apply() { return 2; }
    }
    
    public static int notSeparable1() {
        // return 1;
        JButton jb = new JButton("Hello");
        return (int)jb.getSize().getWidth();
    }
    
    public static interface CLambda {
        public separable Code<Integer> apply();
    }
    public static final class FinalCLambda1 implements CLambda {
        public separable Code<Integer> apply() {
            // int i = notSeparable1(); // error: not separable
            return <|notSeparable1()|>;
        }
    }
    public static final class FinalCLambda2 implements CLambda {
        public separable Code<Integer> apply() { return <|2|>; }
    }
    
    public static class CodeContainer {
        public Code<Integer> c;
    }
    
    public static JButton jb = new JButton("Foo");
    public static CodeContainer cc = new CodeContainer();
    //    public static ObjFun o;
    
    public static interface ObjFun {
        public Object call();
    }
    public static class CodeObjectThunk {
        public static ObjFun o;
        public separable Code<Object> run(final Code<Integer> cz) {
            o = new ObjFun() {
                public Object call() {
                    return cz;
                }
            };
            return <| o.call() |>;
        }
    }
    
    public separable Code<Object> helper(final Code<Integer> cz) {
        return new CodeObjectThunk().run(cz);
    }
    
    public void run() {
        final FinalLambda1 l1 = new FinalLambda1();
        final FinalLambda2 l2 = new FinalLambda2();
        Code<Integer> ci = <| `(<| l1.apply() |>) |>;
        System.out.println(ci.run());
        
        final FinalCLambda1 cl1 = new FinalCLambda1();
        final FinalCLambda2 cl2 = new FinalCLambda2();
        Code<Integer> cci = <| `(cl1.apply()) |>;
        System.out.println(cci.run());
        
        CodeContainer cc2 = new CodeContainer();
        Code<Void> cv = <| {
            final int i = (int)jb.getSize().getWidth();
            cc.c = <| i |>;
            //cc2.c = <| i |>;
            Object co =
                let final Integer z = 0;
            //     `(let final ObjFun f = new ObjFun() {
            //      public Object call() {
            //   return <| z |>;
            //      }
            //  };
            //       <| f.call() |>);
            `(helper(<| z |>));
        } |>;
        cv.run();
        System.out.println(cc.c.run());
    }
    
    public static void main (String [] args) {
        new Limitations2().run();
    }
}
