package net.sf.anathema.lib.testing.clone;

import org.junit.Assert;

import java.awt.Color;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

public class DeepCloneChecker {

  @SuppressWarnings("serial")
  private List<Class<?>> checkEqualsClasses = new ArrayList<Class<?>>() {
    {
      add(Byte.class);
      add(Double.class);
      add(Float.class);
      add(Integer.class);
      add(Long.class);
      add(Short.class);
      add(Boolean.class);
      add(String.class);
      add(Color.class);
    }
  };

  public void assertDeepClonedIgnoringTransientField(Object first, Object second) throws Exception {
    Class< ? > classObject = first.getClass();
    Assert.assertSame(classObject, second.getClass());
    Assert.assertNotSame(first, second);
    while (classObject != null) {
      checkField(classObject.getDeclaredFields(), first, second);
      classObject = classObject.getSuperclass();
    }
  }

  private void checkField(Field[] fields, Object first, Object second) throws Exception {
    AccessibleObject.setAccessible(fields, true);
      for (Field currentField : fields) {
          checkField(currentField, currentField.get(first), currentField.get(second));
      }
  }

  private void checkField(Field currentField, Object first, Object second) throws Exception {
    int modifiers = currentField.getModifiers();
    if (Modifier.isTransient(modifiers) || Modifier.isStatic(modifiers)) {
      return;
    }
    if (first == null) {
      Assert.assertNull("Must be null " + currentField, second);
      return;
    }
    Class< ? > firstClazz = first.getClass();
    Assert.assertNotNull("Must not be null " + currentField, second);
    Assert.assertSame(firstClazz, second.getClass());
    if (currentField.isEnumConstant() || checkEqualsClasses.contains(firstClazz)) {
      Assert.assertEquals("Enum " + currentField, first, second);
      return;
    }
    Assert.assertNotSame("Field " + currentField, first, second);
    assertDeepClonedIgnoringTransientField(first, second);
  }
}