package cutils.reflection;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Vector;

public class TypeUtil {
  private ReflectUtil ru;
  private Vector allClasses = new Vector();

  public void add(Class c[]) {
    for (int i = 0; i < c.length; i++)
      if (allClasses.contains(c[i]))
        continue;
      else
        allClasses.addElement(c[i]);
  }

  public TypeUtil(Object instance) {
    ru = new ReflectUtil(instance);
  }


  public void initAllFriends() {
    Class[] classes = addClassesAndInterfaces();
    addFieldRefs(classes);
    Method[] methods = addReturnTypes(classes);
    addParameterTypes(methods);

  }

  public Class[] addClassesAndInterfaces() {
    Class[] classes = initSuperClasses();
    addInterfaces(classes);
    return classes;
  }

  private void addInterfaces(Class[] classes) {
    add(ru.getSuperInterfaces(classes));
  }

  private void addParameterTypes(Method[] methods) {
    Class pclasses [] = getReferenceParameterTypes(methods);
    add(pclasses);
  }

  private Method[] addReturnTypes(Class[] classes) {
    Method methods[] = getMethods(classes);
    Class mclasses [] = getReferenceReturnTypes(methods);
    add(mclasses);
    return methods;
  }

  private void addFieldRefs(Class[] classes) {
    add(getReferenceFieldTypes(classes));
  }

  private Class[] initSuperClasses() {
    Class classes[] = ru.getSuperClasses();
    add(classes);
    return classes;
  }

  public Class[] getAllClasses() {
    Class classArray[] = new Class[allClasses.size()];
    allClasses.copyInto(classArray);
    return classArray;
  }

  private Class[] getReferenceFieldTypes(Class p_classes[]) {
    Vector v = new Vector();
    for (int i = 0; i < p_classes.length; i++) {
      ReflectUtil temp_refUtil = new ReflectUtil(p_classes[i]);
      Field fields[] = temp_refUtil.getFields();
      for (int j = 0; j < fields.length; j++) {
        Class c = fields[j].getType();
        if (!c.isPrimitive())
          v.addElement(c);
      }
    }
    Class answer[] = new Class[v.size()];
    v.copyInto(answer);
    return answer;
  }

  private Method[] getMethods(Class p_classes[]) {
    Vector v = new Vector();
    for (int i = 0; i < p_classes.length; i++) {
      ReflectUtil tru = new ReflectUtil(p_classes[i]);
      Method mthds[] = tru.getMethods();
      for (int j = 0; j < mthds.length; j++) {
        v.addElement(mthds[j]);
      }
    }
    Method answer[] = new Method[v.size()];
    v.copyInto(answer);
    return answer;
  }

  private Class[] getReferenceReturnTypes(Method p_methods[]) {
    Vector v = new Vector();
    for (int i = 0; i < p_methods.length; i++) {
      Class c = p_methods[i].getReturnType();
      if (c.isPrimitive()) continue;
      v.addElement(c);
    }
    Class answer[] = new Class[v.size()];
    v.copyInto(answer);
    return answer;
  }

  private Class[] getReferenceParameterTypes(Method p_methods[]) {
    Vector v = new Vector();
    for (int i = 0; i < p_methods.length; i++) {
      Class cl[] = p_methods[i].getParameterTypes();
      for (int j = 0; j < cl.length; j++) {
        if (cl[j].isPrimitive()) continue;
        v.addElement(cl[j]);
      }
    }
    Class answer[] = new Class[v.size()];
    v.copyInto(answer);
    return answer;
  }

  public static void println(Class o[]) {
    for (int i = 0; i < o.length; i++)
      System.out.println(getTypeName(o[i]));
  }

  private void removeDuplicates(Class p_classes[]) {
    for (int i = 0; i < p_classes.length; i++)
      if (!allClasses.contains(p_classes[i]))
        allClasses.addElement(p_classes[i]);
  }

  public static String getTypeName(Class type) {

    if (!type.isArray())
      return type.getName();

    Class cl = type;
    int dimensions = 0;
    while (cl.isArray()) {
      dimensions++;
      cl = cl.getComponentType();
    }
    StringBuffer sb = new StringBuffer();
    sb.append(cl.getName());

    for (int i = 0; i < dimensions; i++)
      sb.append("[]");

    return sb.toString();
  }

  public static void main(String args[]) {
    TypeUtil tu = new TypeUtil(new java.util.Date());
    tu.initAllFriends();
    TypeUtil.println(tu.getAllClasses());
  }

}