package com.db4o.test;

import com.db4o.ObjectContainer;
import com.db4o.lib.*;
import java.lang.reflect.Array;
import java.lang.reflect.Field;

public class Compare
{

    static boolean hasPublicConstructor(Class a_Class)
    {
		if(a_Class == String.class){
            return false;
		}
		
        try
        {
            Object o = a_Class.newInstance();
            if(o != null)
                return true;
        }
        catch(Throwable t) { }
        return false;
    }

    static Object normalizeNArray(Object a_object)
    {
        if(Array.getLength(a_object) > 0)
        {
            Object first = Array.get(a_object, 0);
            if(first != null && first.getClass().isArray())
            {
                int dim[] = arrayDimensions(a_object);
                Object all = ((Object) (new Object[arrayElementCount(dim)]));
                normalizeNArray1(a_object, all, 0, dim, 0);
                return all;
            }
        }
        return a_object;
    }

    public static String compare(ObjectContainer a_con, Object a_Compare, Object a_With, String a_path, String errors, Stack a_stack)
    {
		if(! Regression.deactivate){
			a_con.activate(a_With);	
		}
		
		if (a_stack == null){
			a_stack = new Stack();
		}
		// takes care of repeating calls to the same object
		if(! a_stack.push(a_Compare)){
			return errors;
		}
		
        if(a_path == null || a_path.length() < 1)
			if(a_Compare != null){
                a_path = a_Compare.getClass().getName() + ":";
			} else{
				if(a_With != null)
				    a_path = a_With.getClass().getName() + ":";
			}
        String path = a_path;
        if(a_Compare == null)
			if(a_With == null){
                return errors;
			}else{
                return errors + "1==null:" + path + nl();
			}
        if(a_With == null)
            return errors + "2==null:" + path + nl();
        Class l_Class = a_Compare.getClass();
        if(!l_Class.isInstance(a_With))
            return errors + "class!=:" + path + l_Class.getName() + ":" + a_With.getClass().getName() + nl();
        Field l_Fields[] = l_Class.getDeclaredFields();
		for(int i = 0; i < l_Fields.length; i++){
			if(Platform.storeableField(l_Class, l_Fields[i])){
				Platform.setAccessible(l_Fields[i]);
				try
				{
				    path = a_path + l_Fields[i].getName() + ":";
				    Object l_Compare = l_Fields[i].get(a_Compare);
				    Object l_With = l_Fields[i].get(a_With);
				    if(l_Compare == null)
				    {
				        if(l_With != null)
				            errors = errors + "f1==null:" + path + nl();
				    } else
				    if(l_With == null)
				        errors = errors + "f2==null:" + path + nl();
				    else
				    if(l_Compare.getClass().isArray())
				    {
				        if(!l_With.getClass().isArray())
				        {
				            errors = errors + "f2!=array:" + path + nl();
				        } else
				        {
				            l_Compare = normalizeNArray(l_Compare);
				            l_With = normalizeNArray(l_With);
				            int l_len = Array.getLength(l_Compare);
				            if(l_len != Array.getLength(l_With))
				            {
				                errors = errors + "arraylen!=:" + path + nl();
				            } else
				            {
				                boolean l_persistentArray = hasPublicConstructor(l_Fields[i].getType().getComponentType());
				                for(int j = 0; j < l_len; j++)
				                {
				                    Object l_ElementCompare = Array.get(l_Compare, j);
				                    Object l_ElementWith = Array.get(l_With, j);
				                    if(l_persistentArray)
				                        errors = compare(a_con, l_ElementCompare, l_ElementWith, path, errors, a_stack);
				                    else
				                    if(l_ElementCompare == null)
				                    {
				                        if(l_ElementWith != null)
				                            errors = errors + "1e" + j + "==null:" + path + nl();
				                    } else
				                    if(l_ElementWith == null)
				                    {
				                        errors = errors + "2e" + j + "==null:" + path + nl();
				                    } else
				                    {
				                        Class elementCompareClass = l_ElementCompare.getClass();
				                        if(elementCompareClass != l_ElementWith.getClass())
				                            errors = errors + "e" + j + "!=class:" + path + elementCompareClass.toString() + ":" + l_ElementWith.getClass().toString() + nl();
				                        else
				                        if(hasPublicConstructor(elementCompareClass))
				                            errors = compare(a_con, l_ElementCompare, l_ElementWith, path, errors, a_stack);
				                        else
				                        if(!l_ElementCompare.equals(l_ElementWith))
				                            errors = errors + "e" + j + "!=:" + path + l_ElementCompare.toString() + ":" + l_ElementWith.toString() + nl();
				                    }
				                }

				            }
				        }
				    } else
				    if(hasPublicConstructor(l_Fields[i].getType()))
				        errors = compare(a_con, l_Compare, l_With, path, errors, a_stack);
				    else
				    if(!l_Compare.equals(l_With))
				        errors = errors + "!=:" + path + nl();
				}
				catch(Exception e)
				{
				    errors = errors + "Exception:" + path + nl();
				}
			}
		}
        return errors;
    }

    static int[] arrayDimensions(Object a_object)
    {
        int count = 0;
        for(Class clazz = a_object.getClass(); clazz.isArray(); clazz = clazz.getComponentType())
            count++;

        int dim[] = new int[count];
        for(int i = 0; i < count; i++)
        {
            dim[i] = Array.getLength(a_object);
            a_object = Array.get(a_object, 0);
        }

        return dim;
    }

    static int normalizeNArray1(Object a_object, Object a_all, int a_next, int a_dim[], int a_index)
    {
        if(a_index == a_dim.length - 1)
        {
            for(int i = 0; i < a_dim[a_index]; i++)
                Array.set(a_all, a_next++, Array.get(a_object, i));

        } else
        {
            for(int i = 0; i < a_dim[a_index]; i++)
                a_next = normalizeNArray1(Array.get(a_object, i), a_all, a_next, a_dim, a_index + 1);

        }
        return a_next;
    }

    static int arrayElementCount(int a_dim[])
    {
        int elements = a_dim[0];
        for(int i = 1; i < a_dim.length; i++)
            elements *= a_dim[i];

        return elements;
    }

    static String nl()
    {
        return System.getProperty("line.separator");
    }
	
	public static class Stack{
		Collection i_compare;
		Collection i_with;
		
		boolean push(Object a_compare){
			if(i_compare == null){
				i_compare = new Collection();
			}else{
				Iterator i = i_compare.iterator();
				while(i.hasNext()){
					if(i.next() == a_compare){
						return false;
					}
				}
			}
			i_compare.add(a_compare);
			return true;
		}
	}
}
