import edu.rice.cs.mint.runtime.Code;
import edu.rice.cs.mint.runtime.MintSerializer;
import edu.rice.cs.mint.runtime.CouldNotSaveCodeException;

import java.io.*;

public class SaveToFileSample {    
    public static void main(String[] args) throws java.io.IOException {
        File dir = new File("tempClasses");
        dir.mkdir();
        
        // simple
        Code<Integer> c = <|123|>;
        File f = new File(dir, "IntegerCode1.jar");
        MintSerializer.save(c, "IntegerCode1", f);
        
        Integer s = MintSerializer.load(f);
        System.out.println(s);
        
        // with escape
        Code<Integer> c2 = <|123 + `c|>;
        File f2 = new File(dir, "IntegerCode2.jar");
        MintSerializer.save(c2, "IntegerCode2", f2);
        
        Integer s2 = MintSerializer.load(f2);
        System.out.println(s2);

        // lifting
        final int lifted = 3;
        Code<Integer> c3 = <| lifted * 123 |>;
        File f3 = new File(dir, "IntegerCode3.jar");
        MintSerializer.save(c3, "IntegerCode3", f3);
        
        Integer s3 = MintSerializer.load(f3);
        System.out.println(s3);

        // CSP
        final int[] csp_ia = new int[] { 3 };
        Code<Integer> c4 = <| csp_ia[0] * 123 |>;
        File f4 = new File(dir, "IntegerCode4.jar");
        MintSerializer.save(c4, "IntegerCode4", f4);
        
        Integer s4 = MintSerializer.load(f4);
        System.out.println(s4);

        Code<Integer> c6 = <|111|>;
        File f6 = new File(dir, "IntegerCode6.jar");
        try {
            MintSerializer.save(c6, "SaveToFileSample", f6);
            throw new AssertionError("This should fail because the SaveToFileSample class already exists");
        }
        catch(CouldNotSaveCodeException e) {
            System.out.println("OK: This was expected to fail because the SaveToFileSample class already exists");
        }
        try {
            MintSerializer.save(c6, "IntegerCode1", f6);
            throw new AssertionError("This should fail because the IntegerCode1 name was already used");
        }
        catch(CouldNotSaveCodeException e) {
            System.out.println("OK: This was expected to fail because the IntegerCode1 name was already used");
        }
        
        Code<Integer> lintc = StagedLint.termFact.peval(StagedLint.env0, StagedLint.fenv0);
        System.out.println(lintc);
        File lintf = new File(dir, "StagedLintFact.jar");
        MintSerializer.save(lintc, "StagedLintFact", lintf);
        System.out.println("fact(10) = "+lintc.run());
        
        Integer lints = MintSerializer.load(lintf);
        System.out.println("fact(10) = "+lints);
        
        Code<? extends StagedLint.IntFun> codeFactFun = <| new StagedLint.IntFun() {
            public int apply(final int param) {
                return `(codeFact(<|param|>));
            }
        } |>;
        System.out.println(codeFactFun);
        File lintf2 = new File(dir, "CodeFactFun.jar");
        MintSerializer.save(codeFactFun, "CodeFactFun", lintf2,
                            MintSerializer.Options.VERBOSE,
                            MintSerializer.Options.MAIN_METHOD);
        StagedLint.IntFun sFactFun = codeFactFun.run();
        System.out.println("sFactFun(10) = "+sFactFun.apply(10));
        System.out.println("sFactFun(12) = "+sFactFun.apply(12));
        System.out.println("sFactFun(15) = "+sFactFun.apply(15));
        
        StagedLint.IntFun sFactFun2 = MintSerializer.load(lintf2);
        System.out.println("sFactFun2(10) = "+sFactFun2.apply(10));
        System.out.println("sFactFun2(12) = "+sFactFun2.apply(12));
        System.out.println("sFactFun2(15) = "+sFactFun2.apply(15));
    }
    
    public static separable Code<Integer> codeFact(Code<Integer> param) {
        return new StagedLint.Program
                    (new StagedLint.App("f", new StagedLint.Int(param)),
                     new StagedLint.Declaration
                         ("f", "x", new StagedLint.Ifz
                              (new StagedLint.Var("x"),
                               new StagedLint.Int(1),
                               new StagedLint.Mul(new StagedLint.Var("x"),
                                                  new StagedLint.App
                                                      ("f",
                                                       new StagedLint.Sub
                                                           (new StagedLint.Var("x"),
                                                            new StagedLint.Int(1))))))).peval(StagedLint.env0, StagedLint.fenv0);
    }
}
