package  com.db4o.test;

import  java.lang.reflect.*;
import  com.db4o.lib.*;
import  com.db4o.*;
import  com.db4o.config.*;
import  java.io.*;


public class Regression implements OpenContainer {
    final static boolean debug = false;
    static boolean deactivate = false;
    final public static String nl = System.getProperty("line.separator");
    public static final boolean profileOnly = false;
    private static final boolean jdk2Only = false;
    public static boolean closeFile;
    public static final String file = "regression.yap";
    private static OpenContainer openWith = new Regression();

    public static void main (String[] args) {
        String errors = "";
        new java.io.File(file).delete();
        long time = System.currentTimeMillis();
        if (!jdk2Only) {
            RTestable[] clazzes = debug ? debug() : allClasses();
            errors = mainLoop(clazzes);
        }
        else {
            System.out.println("JDK2 run only.");
        }
        time = System.currentTimeMillis() - time;
        if (Platform.jdk2) {
            try {
                CanRun t2 = (CanRun)Class.forName("com.db4o.test.test2.Regression2").newInstance();
                errors = t2.run(errors);
            } catch (ClassNotFoundException e) {
                errors = errors + "test2.Regression not found" + nl;
            } catch (IllegalAccessException e) {
                errors = errors + "test2.Regression no access" + nl;
            } catch (InstantiationException e) {
                errors = errors + "test2.Regression instantiation failed" +
                        nl;
            }
        }
        System.out.println("Time: " + time + " ms.");
        if (errors.length() == 0) {
            System.out.println(profileOnly ? "Profile run completed." : "Regression Test Passed.");
        }
        else {
            System.out.println("!!! Regression Test Failed. !!!");
        }
        System.out.println(Db4o.version());
        System.out.println(errors);
    }

    public static String mainLoop (RTestable[] clazzes) {
        String errors = "";
        int run = 0;
        for (int k = 0; k < 1; k++) {
            run++;
            System.out.println("db4o regression run:" + run);
            closeFile = true;
            for (int i = 0; i < 2; i++) {
                for (int j = 0; j < clazzes.length; j++) {
                    errors = cycle(clazzes[j], errors, run);
                }
            }
            closeFile = false;
        }
        return  errors;
    }

    static String cycle (RTestable clazz, String errors, int a_run) {
        if (clazz.jdk2() && (!Platform.jdk2)) {
            return  errors;
        }
        Object obj = clazz.newInstance();
        ObjectContainer con = open();
        // remove all
        Object get = clazz.newInstance();
        ObjectSet set = con.get(get);
        while (set.hasNext()) {
            con.delete(set.next());
        }
        // STEP1 add one object added
        clazz.set(obj, 1);
        con.set(obj);
        errors = specificTest(clazz, con, ONE, errors);
        con = close(con);
        // check 1, retrieving no members set
        errors = compare(con, get, clazz, 1, 1, errors);
        // check 1, retrieving all members set
        clazz.set(get, 1);
        errors = compare(con, get, clazz, 1, 1, errors);
        con = close(con);
        // add 4
        for (int i = 0; i < 4; i++) {
            obj = clazz.newInstance();
            clazz.set(obj, 1);
            con.set(obj);
        }
        con = close(con);
        // check 5
        errors = compare(con, get, clazz, 1, 5, errors);
        errors = specificTest(clazz, con, FIVE, errors);
        con = close(con);
        // delete 1
        set = con.get(get);
        obj = set.next();
        con.delete(obj);
        con = close(con);
        // check 4
        errors = compare(con, get, clazz, 1, 4, errors);
        errors = specificTest(clazz, con, DELETED, errors);
        con = close(con);
        // update 1
        set = con.get(get);
        obj = set.next();
        clazz.set(obj, 2);
        con.set(obj);
        con = close(con);
        // check 3
        errors = compare(con, get, clazz, 1, 3, errors);
        errors = specificTest(clazz, con, SAME, errors);
        con = close(con);
        // check 1
        clazz.set(get, 2);
        errors = compare(con, get, clazz, 2, 1, errors);
        errors = specificTest(clazz, con, UPDATED, errors);
        con.close();
        return  errors;
    }

    public static String compare (ObjectContainer con, Object get, RTestable clazz,
            int ver, int count, String errors) {
        ObjectSet set = con.get(get);
        if (profileOnly) {
            return  "";
        }
        set.reset();
        if (set.size() == count) {
            while (set.hasNext()) {
                Object res = set.next();
                errors = clazz.compare(con, res, ver, errors);
                if (deactivate) {
                    con.deactivate(res);
                    con.activate(res);
                    errors = clazz.compare(con, res, ver, errors);
                }
            }
        }
        else {
            errors = errors + clazz.getClass().getName() + ":offcount:expected"
                    + count + ":actual:" + set.size() + nl;
        }
        if (errors.length() > 0) {
            for (int j = 0; j < expectedErrors.length; j++) {
                errors = Str._replace(errors, expectedErrors[j], "");
            }
        }
        return  errors;
    }

    static String specificTest (Object clazz, ObjectContainer con, int step,
            String errors) {
        String methodName = "specific";
        Class[] parameterClasses =  {
            ObjectContainer.class, String.class, Integer.TYPE
        };
        try {
            Method method = clazz.getClass().getMethod(methodName, parameterClasses);
            if (method != null) {
                errors = (String)method.invoke(clazz, new Object[] {
                    con, errors, new Integer(step)
                });
            }
        } catch (Exception e) {}
        return  errors;
    }

    public static ObjectContainer open () {
        return  openWith.openContainer();
    }

    public ObjectContainer openContainer () {
        Configuration config = Db4o.configure();
		// Set ActivationDepth deep enough for Recursive classes.
		config.activationDepth(12);
        ObjectClass oc = config.objectClass("com.db4o.test.DeepUpdate");
        oc.updateDepth(2);
        return  Db4o.openFile(file);
    }

    public static void openWith (OpenContainer a_with) {
        openWith = a_with;
    }

    public static ObjectContainer close (ObjectContainer con) {
        if (closeFile) {
            con.close();
            return  open();
        }
        return  con;
    }

    static Object newInstance (Class a_class) {
        try {
            return  a_class.newInstance();
        } catch (Throwable t) {
            try {
                Constructor[] constructors = a_class.getDeclaredConstructors();
                for (int i = 0; i < constructors.length; i++) {
                    try {
                        Platform.setAccessible(constructors[i]);
                        Class[] pTypes = constructors[i].getParameterTypes();
                        Object[] parms = new Object[constructors[i].getParameterTypes().length];
                        for (int j = 0; j < parms.length; j++) {
                            for (int k = 0; k < simpleNullWrappers.length; k++) {
                                if (pTypes[j] == simpleClasses[k]) {
                                    parms[j] = simpleNullWrappers[k];
                                    break;
                                }
                            }
                        }
                        Object res = constructors[i].newInstance(parms);
                        if (res != null) {
                            return  res;
                        }
                    } catch (Exception exc) {
                        System.out.println(exc.getClass().getName());
                        System.out.println(exc.getMessage());
                    }
                }
            } catch (Exception ex) {}
            System.out.println("NewInstance failed:" + a_class.getName());
            return  null;
        }
    }
    // The following errors are expected.
    // They occur due to the fact that:
    // - Byte Objects will be instantiated even if the stored Byte object was null
    // - the "Empty" objects does not change on update
    // - the RecursiveTyped objects create more instances of themselves than would be expected
    static String[] expectedErrors =  {
        "1e3==null:com.db4o.test.ArrayTypedPrivate:oByte:" + nl,
		"1e0==null:com.db4o.test.ArrayTypedPrivate:nByte:" + nl,
		"1e3==null:com.db4o.test.ArrayTypedPublic:oByte:" + nl,
		"1e0==null:com.db4o.test.ArrayTypedPublic:nByte:" + nl,
		"com.db4o.test.Empty:offcount:expected3:actual:4" + nl,
		"com.db4o.test.Empty:offcount:expected1:actual:4" + nl,
		"f1==null:com.db4o.test.MasterMonster:ooo:nByte:" + nl,
		"1e3==null:com.db4o.test.MasterMonster:ooo:oByte:" + nl,
		"1e0==null:com.db4o.test.MasterMonster:ooo:nByte:" + nl,
		"com.db4o.test.RecursiveTypedPrivate:offcount:expected1:actual:11" + nl,
		"com.db4o.test.RecursiveUnTypedPrivate:offcount:expected1:actual:11" + nl,
		"com.db4o.test.RecursiveTypedPublic:offcount:expected1:actual:11" + nl,
		"com.db4o.test.RecursiveUnTypedPublic:offcount:expected1:actual:11" + nl,
		"f1==null:com.db4o.test.TypedPrivate:nByte:" + nl,
		"f1==null:com.db4o.test.TypedPublic:nByte:"+ nl
	};

    static Object[] simpleNullWrappers =  {
        new Integer(0), new Long(0), new Character((char)0), new Double((double)0),
                new Float((float)0), new Boolean(false), new Short((short)0),
                new Byte((byte)0)
    };

    static Class[] simpleClasses =  {
        Integer.TYPE, Long.TYPE, Character.TYPE, Double.TYPE, Float.TYPE, Boolean.TYPE,
                Short.TYPE, Byte.TYPE
    };
    public static final int ONE = 1;
    public static final int FIVE = 5;
    public static final int DELETED = 4;
    public static final int SAME = 3;
    public static final int UPDATED = 0;

    public static RTestable[] allClasses () {
        return  new RTestable[] {
            new ArrayInObjectPrivate(),
            new ArrayInObjectPublic(),
            new ArrayMixedInObjectPrivate(),
            new ArrayMixedInObjectPublic(),
            new ArrayMixedTypedPrivate(),
            new ArrayMixedTypedPublic(),
            new ArrayNDimensionalPrivate(),
            new ArrayNDimensionalPublic(),
            new ArrayTypedPrivate(),
            new ArrayTypedPublic(),
            new ArrayUntypedPrivate(),
            new ArrayUntypedPublic(),
            new BiParentTypedPrivate(),
            new BiParentTypedPublic(),
            new BiParentUnTypedPrivate(),
            new BiParentUnTypedPublic(),
            new DeepUpdate(),
			new Empty(),
            new InterfacePrivate(),
            new InterfacePublic(),
            new ObjectSimplePrivate(),
            new ObjectSimplePublic(),
            new ParameterConstructor(0),
            PrivateConstructor.construct(),
            new RecursiveTypedPrivate(),
            new RecursiveTypedPublic(),
            new RecursiveUnTypedPrivate(),
            new RecursiveUnTypedPublic(),
            new RHashtable(),
            new RProperties(),
            new RStack(),
            new RVector(),
            new TypedPrivate(),
            new TypedPublic(),
            new UntypedPrivate(),
            new UntypedPublic(),
            new MasterMonster()
        };
    }

    public static RTestable[] debug () {
        return  new RTestable[] {
            /*
             new RVector(),
             new RHashtable(),
             new RProperties()
			*/
            new DeepUpdate()            
			/*
             new ParameterConstructor(0),
             PrivateConstructor.construct()
            */
        };
    }
}



