package cutils.reflection;

import javax.swing.*;
import java.util.Vector;

/**
 * ReflectUtil shows several examples of using reflection.
 */
public class ReflectUtil {
    private Class c;
    private Object o;
    private MethodList ml;

    /**
     * Instance the ReflectUtil with an Object.
     * ReflectUtils gives extra feature not available with
     * reflection.
     * @param _o  an instance of an object to be inspected
     */
    public ReflectUtil(Object _o) {
        c = _o.getClass();
        o = _o;
        ml = new MethodList(c);
    }

    /**
     * Get a list of all the constructors in the
     * containing class. Default constructors can be returned.
     * For interfaces and primitive data types, no constructor is returned.
     * @return an array of constructors.
     */
    public java.lang.reflect.Constructor[] getConstructors() {
        return
                c.getDeclaredConstructors();
    }

    /**
     * Get all but the inherited fields.
     * @return array of field instances.
     */
    public java.lang.reflect.Field[] getFields() {
        return c.getDeclaredFields();
    }

    /**
     * Get all but inherited methods.
     * @return array of methods
     */
    public java.lang.reflect.Method[] getMethods() {
        return c.getDeclaredMethods();
    }

    /**
     * Obtain a list of all the methods.
     * @return  an array of all methods (including super classes).
     */
    public java.lang.reflect.Method[] getAllMethods() {
        return ml.getMethods();
    }

    public Class[] getSuperClasses() {
        Class sc = c;
        Vector v = new Vector();
        v.addElement(c);

        while ((sc = sc.getSuperclass()) != null)
            v.addElement(sc);

        Class ca[] = new Class[v.size()];
        v.copyInto(ca);
        return ca;
    }

    public Class[] getSuperInterfaces(Class ca[]) {
        Vector v = new Vector();
        for (int i = 0; i < ca.length; i++) {
            Class newArray[] =
                    getSuperInterfaces(ca[i]);
            for (int j = 0; j < newArray.length; j++)
                v.addElement(newArray[j]);
        }
        Class rc[] = new Class[v.size()];
        v.copyInto(rc);
        return rc;
    }

    /**
     * Get all the interfaces implemented by this
     * class, in order of listing.
     * @param ci  class instance to be inspected
     * @return    an array of all the classes that correspond to the interfaces.
     */
    public Class[] getSuperInterfaces(Class ci) {
        return ci.getInterfaces();
    }

    /**
     * Get a list of public class members that are
     * not inherited.
     * @return   an array of Class class instances.
     */
    public Class[] getPublicClassMembers() {
        return c.getClasses();
    }

    public String[] getReadMethodNames() {
        java.lang.reflect.Method m[] = getMethods();
        java.util.Vector v = new java.util.Vector();
        for (int i = 0; i < m.length; i++) {
            String s = getName(m[i]);
            if (s.startsWith("get") || s.startsWith("is")) {
                v.addElement(s);
                System.out.println(s);
            }
        }
        String getterArray[] = new String[v.size()];
        v.copyInto(getterArray);
        return getterArray;
    }

    public String[] getReadMethodNames(int n) {
        java.lang.reflect.Method m[] = getMethodsWithNArgs(n);
        return getReadMethodNames(m);
    }

    private String[] getReadMethodNames(java.lang.reflect.Method[] m) {
        Vector v = new Vector();
        for (int i = 0; i < m.length; i++) {
            String s = getName(m[i]);
            if (s.startsWith("get") || s.startsWith("is"))
                v.addElement(s);
        }
        String getterArray[] = new String[v.size()];
        v.copyInto(getterArray);
        return getterArray;
    }

    /**
     *
     * @return  method in present class
     */
    public String[] getWriteMethodNames() {
        java.lang.reflect.Method m[] = getMethods();
        return getWriteMethodNames(m);
    }

    public String[] getWriteMethodNames(java.lang.reflect.Method[] m) {
        Vector v = new Vector();
        for (int i = 0; i < m.length; i++) {
            String s = getName(m[i]);
            if (s.startsWith("set")) {
                v.addElement(s);
                System.out.println(s);
            }
        }
        String setterArray[] = new String[v.size()];
        for (int i = 0; i < setterArray.length; i++)
            setterArray[i] = (String) v.elementAt(i);
        return setterArray;
    }

    /**
     *
     * @param s a method name
     * @return  a method that corresponds to this name
     */
    public java.lang.reflect.Method getMethod(String s) {
        java.lang.reflect.Method m = null;
        try {
            m = c.getMethod(s, new Class[]{});
        } catch (NoSuchMethodException e) {
            System.out.println(e);
        }
        return m;
    }

    /**
     invoke a methodName string with no arguments.
     */
    public Object invoke(String methodName) {
        java.lang.reflect.Method m = getMethod(methodName);
        Object ret = null;
        try {
            ret = m.invoke(o, null);
        } catch (java.lang.reflect.InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        return ret;
    }

    /**
     *
     * @param m  a method to be inspected
     * @return    modifiers encoded as an int.
     */

    public int getModifiers(java.lang.reflect.Method m) {
        return m.getModifiers();
    }

    /**
     *
     * @param m  method
     * @return   a string representation of the modifiers
     */
    public String getModifierString(java.lang.reflect.Method m) {
        return java.lang.reflect.Modifier.toString(
                getModifiers(m));
    }


    /**
     * Print an array of objects.
     * @param o  an array, printed one element per line.
     */
    public static void println(Object o[]) {
        for (int i = 0; i < o.length; i++)
            System.out.println(o[i]);
    }

    /**
     * Convert an array of <code>Object</code> into a string.
     * @param o  an array of string in
     * @return   a big string with new lines out
     */
    public static String toString(Object o[]) {
        String s = "";
        for (int i = 0; i < o.length; i++)
            s = s + o[i] + "\n";
        return s;
    }

    /**
     * print information about the containing instance
     * to the console.
     */
    public void printInfo() {
        System.out.println("Info on class " + getClassName());
        System.out.println("Constructors:");
        println(getConstructors());
        System.out.println("Fields:");
        println(getFields());
        System.out.println("Methods:");
        println(getMethods());
        System.out.println("Methods with 0 arguments");
        println(getMethodsWithNArgs(0));
        System.out.println("read methods");
        println(getReadMethodNames());
        System.out.println("write methods");
        println(getWriteMethodNames());
        System.out.println("Classes");
        println(getPublicClassMembers());
    }

    /**
     * Get the name of the containing class.
     * @return  classname.
     */
    public String getClassName() {
        return c.getName();
    }

    java.lang.reflect.Method[] getMethodsWithNArgs(int n) {
        java.lang.reflect.Method m[] = getMethods();
        java.util.Vector v = new java.util.Vector();
        for (int i = 0; i < m.length; i++) {
            Class ca[] = m[i].getParameterTypes();
            if (ca.length == n)
                v.addElement(m[i]);
        }
        java.lang.reflect.Method ma[] = new java.lang.reflect.Method[v.size()];
        v.copyInto(ma);
        return ma;
    }

    /**
     * Get A string representation of the method.
     * @param m  method name
     * @return   the name of the method
     */
    public String getName(java.lang.reflect.Method m) {
        return m.getName();
    }

    public String getInfoString(java.lang.reflect.Method m) {
        return
                "for method " + m.getName() +
                "\nThe modifier = " +
                getModifierString(m) +
                "\nThe return type =" +
                m.getReturnType().getName() +
                "\n The arguments for this method are " +
                toString(m.getParameterTypes());
    }

    public void printInfo(java.lang.reflect.Method m) {
        System.out.println(getInfoString(m));
    }

    static class Foo extends JEditorPane
            implements Runnable {
        public void run() {
        }
    }

    /**
     * Example of reflectUtil usage.
     * @param args ignored.
     */
    public static void main(String args[]) {
        Foo f = new Foo();
        ReflectUtil ru = new ReflectUtil(
                f);
        ru.println(
                ru.getSuperInterfaces(ru.getSuperClasses()));
        ru.println(ru.getSuperClasses());
        //String getterNames[]
        //  = ru.getReadMethodNames(0);
        // for (int i=0; i < getterNames.length; i++)
        //  System.out.println(
        //      getterNames[i]+"="
        //          + ru.invoke(getterNames[i]));
        //

        //ru.startCommandLineInterpreter();
        //ru.printInfo();
    }

    /**
     * starts a simple command line interpreter
     * that takes in method names.
     */
    public void startCommandLineInterpreter() {
        String s = null;
        String prompt = "enter command:";
        while (!(s = getString(prompt)).startsWith("quit"))
            try {
                prompt = s + "=" + invoke(s);
            } catch (Exception e) {
                prompt = e.toString();
            }
        ;
    }

    public static String getString(String prompt) {
        return javax.swing.JOptionPane.showInputDialog(prompt);
    }

    /**
     * Get the containing class class instance.
     * @return   The containing class class.
     */
    public Class getC() {
        return c;
    }
}