import java.io.*;
import java.lang.reflect.*;

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

public class Serializer2 {
    public static ObjectOutputStream _oos;
    public static
        void writeByte(byte val) {
//         System.out.println("Writing byte "+val);
        try { _oos.writeInt(val); }
        catch(IOException ioe) { throw new SerializerException(); }
    }
    public static
        void writeInt(int val) {
//         System.out.println("Writing int "+val);
        try { _oos.writeInt(val); }
        catch(IOException ioe) { throw new SerializerException(); }
    }
    public static
        void writeString(String val) {
//         System.out.println("Writing string "+val);
        try { _oos.writeUTF(val); }
        catch(IOException ioe) { throw new SerializerException(); }
    }
    
    public static separable
        <A,B> Code<Void> sserializeField(FieldCode<A,B> fc, final Code<A> obj) {
        return <| { `(sserialize(fc.getFieldClassCode(),
                                 fc.get(obj))); } |>;
    }
    
    public static separable
        <A> Code<Void> sserialize(ClassCode<A> type,
                                  final Code<A> obj) {
        if (type.getCodeClass()==Byte.class)
            return <| { writeByte(`((Code<Byte>)obj)); } |>;
        else if (type.getCodeClass()==Integer.class)
            return <| { writeInt(`((Code<Integer>)obj)); } |>;
        else if (type.getCodeClass()==String.class)
            return <| { writeString(`((Code<String>)obj)); } |>;
        
        Code<Void> result = <| { } |>;
        for(FieldCode<A,?> fc: type.getFields()) {
            result = <| { `result; `(sserializeField(fc, obj)); } |>;
        }
        
        return result;
        
    }
    
    public static
        <A,B> void serializeField(Field fc, final A obj) throws IllegalAccessException {
        serialize((Class<B>)fc.getType(), (B)fc.get(obj));
    }
    
    public static
        <A> void serialize(Class<A> type,
                           final A obj) throws IllegalAccessException {
        if (type==Byte.class || type==Byte.TYPE)
            writeByte((Byte)obj);
        else if (type==Integer.class || type==Integer.TYPE)
            writeInt((Integer)obj);
        else if (type==String.class) {
            writeString((String)obj);
        }
        else {
            for(Field fc: type.getFields()) {
                serializeField(fc, obj);
            }
        }
    }
    
    public static void main(String[] args) throws IOException {
        final Dummy2 d2 = new Dummy2();
        d2.x = 3;
        d2.y = 4;
        final Dummy3 d3 = new Dummy3();
        d3.z = 5;
        d3.d2 = d2;
        final Dummy4 d4 = new Dummy4();
        d4.d2 = d2;
        d4.d3 = d3;
        
        _oos = new ObjectOutputStream(new OutputStream() {
            public void write(int b) { }
        });
        
//        try {
//           serialize(ProgramAppDeclarationIfzVarIntIfzSubIntAdd.class, termFib);
//        }
//        catch(Exception e) { }
        System.out.println("serialize");
        Benchmark.TimedTask[] results =
            Benchmark.benchmark(Benchmark.stagingTasks(new Benchmark.Task() {
            public void run() {
                //
                // call unstaged code here
                //
                try {
                    serialize(Dummy4.class, d4);
//                    serialize(ProgramAppDeclarationIfzVarIntIfzSubIntAdd.class, termFib);
                }
                catch(Exception e) { }
                //
                //
                //
            }
        }, new Benchmark.Thunk<Code<? extends Benchmark.Task>>() {
            public Code<? extends Benchmark.Task> value() {
                return <| new Benchmark.Task() {
                    public void run() {
                        //
                        // splice in staged code here
                        //
                        `(sserialize(new ClassCode<Dummy4>(Dummy4.class), <| d4 |>));
//                        `(sserialize(new ClassCode<ProgramAppDeclarationIfzVarIntIfzSubIntAdd>(ProgramAppDeclarationIfzVarIntIfzSubIntAdd.class),  <| termFib |>));
                        //
                        //
                        //
                    }
                } |>;
            }
        }));
        
//        Benchmark.print(0, results); // relative to unstaged
//        System.out.println();
        Benchmark.print(results.length-1, results); // relative to staged
    }
    
    public static final class Dummy2 {
        public int x;
        public int y;
    }
    public static final class Dummy3 {
        public int z;
        public Dummy2 d2;
    }
    public static final class Dummy4 {
        public Dummy3 d3;
        public Dummy2 d2;
    }
    
    public static final class SerializerException extends RuntimeException {
        public SerializerException() { }
    }
    
    public static ProgramAppDeclarationIfzVarIntIfzSubIntAdd termFib = new ProgramAppDeclarationIfzVarIntIfzSubIntAdd
        (new AppInt("f", new Int(10)),
         new DeclarationIfzVarIntIfzSubIntAdd("f", "x", new IfzVarIntIfzSubIntAdd
                             (new Var("x"),
                              new Int(0),
                              new IfzSubIntAdd
                                  (new SubVarInt
                                       (new Var("x"),
                                        new Int(1)),
                                   new Int(1),
                                   new AddAppApp
                                       (new AppSub
                                            ("f",
                                             new SubVarInt
                                                 (new Var("x"),
                                                  new Int(1))),
                                        new AppSub
                                            ("f",
                                             new SubVarInt
                                                 (new Var("x"),
                                                  new Int(2))))))));
    public static final class Int {
        public int _value;
        public Int(int value) {
            _value = value;
        }
    }
    
    public static final class Var {
        public String _s;
        public Var(String s) {
            _s = s;
        }
    }
    
    public static final class AppInt {
        public String _s;
        public Int _body;
        public AppInt(String s, Int body) {
            _s = s;
            _body = body;
        }
    }
    
    public static final class AppSub {
        public String _s;
        public SubVarInt _body;
        public AppSub(String s, SubVarInt body) {
            _s = s;
            _body = body;
        }
    }
    
    public static final class AddAppApp {
        public AppSub _left, _right;
        public AddAppApp(AppSub left, AppSub right) {
            _left = left;
            _right = right;
        }
    }
    
    public static final class SubVarInt {
        public Var _left;
        public Int _right;
        public SubVarInt(Var left, Int right) {
            _left = left;
            _right = right;
        }
    }
    
    public static final class IfzSubIntAdd {
        public SubVarInt _test;
        public Int _conseq;
        public AddAppApp _alt;
        public IfzSubIntAdd(SubVarInt test, Int conseq, AddAppApp alt) {
            _test = test;
            _conseq = conseq;
            _alt = alt;
        }
    }

    public static final class IfzVarIntIfzSubIntAdd {
        public Var _test;
        public Int _conseq;
        public IfzSubIntAdd _alt;
        public IfzVarIntIfzSubIntAdd(Var test, Int conseq, IfzSubIntAdd alt) {
            _test = test;
            _conseq = conseq;
            _alt = alt;
        }
    }
    
    public static final class DeclarationIfzVarIntIfzSubIntAdd {
        public String _fun, _param;
        public IfzVarIntIfzSubIntAdd _body;
        public DeclarationIfzVarIntIfzSubIntAdd(String fun, String param, IfzVarIntIfzSubIntAdd body) {
            _fun = fun;
            _param = param;
            _body = body;
        }
    }
    
    public static final class ProgramAppDeclarationIfzVarIntIfzSubIntAdd {
        public DeclarationIfzVarIntIfzSubIntAdd _def;
        public AppInt _body;
        public ProgramAppDeclarationIfzVarIntIfzSubIntAdd(AppInt body, DeclarationIfzVarIntIfzSubIntAdd def) {
            _def = def;
            _body = body;
        }
    }
}
