/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package gui;


import connectors.NameConnector;
import connectors.VisibleException;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.*;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import mobirentdesktop.entities.ClassNameConnector;

/**
 *
 * @author Csubák Péter
 */
public class AnnotationExtractor {

    /**
     * Megkeresi a gui-beli név alapján az objektumban a megfelelő gettert, és
     * meghívja, majd ennek értékét adja vissza visszatérési értékként.
     * (A működéséhez feltétlenül szükséges, hogy a NameConnector annotáció
     * be legyen állítva az objektum getterein)
     * @param <E> Visszatérési típus, minthogy nem lehet tudni, hogy milyen 
     * típust ad a getter, így ez generikus típus.
     * @param <F> Az obj objektum típusa, szintén nem lehet tudni, hogy milyen 
     * típusú az adott objektum ezért generikus típus.
     * @param guiName A keresett mező gui-beli neve.
     * @param obj Az objektum, aminek a getterét szeretnénk hívni.
     * @return Az objektum getterének a visszatérési eredménye.
     * @throws java.lang.Exception Ha nem található az adott getter, akkor egy 
     * kvétel váltódik, ki a "Nincs ilyen getter" üzenettel.
     */
    public static <E extends Object, F> E getFieldByGuiName(String guiName, F obj) throws Exception {
        for (Method m : obj.getClass().getMethods()) {
            if (m.isAnnotationPresent(NameConnector.class)) {
                NameConnector nc = m.getAnnotation(NameConnector.class);
                if (nc.guiName().equals(guiName) && (m.getName().startsWith("get") || m.getName().startsWith("is"))) {
                    try {
                        return (E) m.invoke(obj);
                    } catch (IllegalAccessException ex) {
                        Logger.getLogger(AnnotationExtractor.class.getName()).log(Level.SEVERE, null, ex);
                    } catch (IllegalArgumentException ex) {
                        Logger.getLogger(AnnotationExtractor.class.getName()).log(Level.SEVERE, null, ex);
                    } catch (InvocationTargetException ex) {
                        Logger.getLogger(AnnotationExtractor.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }
        }
        throw new Exception("Nincs ilyen getter");
    }

    /**
     * Megkeresi a gui-beli név alapján az objektumon a megfelelő settert, és
     * a megadott paraméterezéssel meghívja azt.
     * (A működéséhez feltétlenül szükséges, hogy a NameConnector annotáció
     * be legyen állítva az objektum setterein)
     * @param <F> Az objektum típusa, mivel bármi lehet így generikus.
     * @param <G> Az átadandó paraméterek típusa.
     * @param guiName A keresett mező gui-beli neve
     * @param obj Az objektum amin keressük a settert
     * @param args A setternek átadandó paraméterek
     * @throws java.lang.Exception Ha nem található az adott setter, akkor egy 
     * kvétel váltódik, ki a "Nincs ilyen setter" üzenettel.
     */
    public static <F, G> void setFieldByGuiName(String guiName, F obj, G... args) throws Exception {
        for (Method m : obj.getClass().getMethods()) {
            if (m.isAnnotationPresent(NameConnector.class)) {
                NameConnector nc = m.getAnnotation(NameConnector.class);
                if (nc.guiName().equals(guiName) && m.getName().startsWith("set")) {
                    try {
                        if ((nc.nullable() == false) && ((args == null) || 
                                (args[0] == null) || (args[0].equals("")))){
                            throw new VisibleException("A "+guiName+" mezőt kötelező kitölteni.");
                        }
                        
                        if (nc.type().equals("String")) {
                            m.invoke(obj, args);
                            return;
                        } else if (nc.type().equals("double")) {
                            m.invoke(obj, (Double.parseDouble((String) args[0])));
                            return;
                        } else if (nc.type().equals("int")) {
                            m.invoke(obj, (Integer.parseInt((String) args[0])));
                            return;
                        }
                    } catch (IllegalAccessException ex) {
                        Logger.getLogger(AnnotationExtractor.class.getName()).log(Level.SEVERE, null, ex);
                    } catch (IllegalArgumentException ex) {
                        Logger.getLogger(AnnotationExtractor.class.getName()).log(Level.SEVERE, null, ex);
                    } catch (InvocationTargetException ex) {
                        Logger.getLogger(AnnotationExtractor.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }
        }
        throw new Exception("Nincs ilyen setter: "+guiName);
    }

    private static Class[] getClasses(String packageName) throws ClassNotFoundException, IOException {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        assert classLoader != null;
        String path = packageName.replace('.', '/');
        Enumeration<URL> resources = classLoader.getResources(path);
        List<File> dirs = new ArrayList<File>();
        while (resources.hasMoreElements()) {
            URL resource = resources.nextElement();
            dirs.add(new File(resource.getFile()));
        }
        ArrayList<Class> classes = new ArrayList<Class>();
        for (File directory : dirs) {
            classes.addAll(findClasses(directory, packageName));
        }
        return classes.toArray(new Class[classes.size()]);
    }

    private static List<Class> findClasses(File directory, String packageName) throws ClassNotFoundException {
        List<Class> classes = new ArrayList<Class>();
        if (!directory.exists()) {
            return classes;
        }
        File[] files = directory.listFiles();
        for (File file : files) {
            if (file.isDirectory()) {
                assert !file.getName().contains(".");
                classes.addAll(findClasses(file, packageName + "." + file.getName()));
            } else if (file.getName().endsWith(".class")) {
                classes.add(Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6)));
            }
        }
        return classes;
    }

    /**
     * A gui-beli név alapján megkeresi a megfelelő osztályt. Alapvetően a 
     * beállító formok esetén hasznos, mert segítségével automatizálható a 
     * formok kezelése.
     * (A működéséhez feltétlenül szükséges, hogy a ClassNameConnector annotáció
     * be legyen állítva a dataprocessor azon osztályain amit kereshetővé 
     * szeretnénk tenni guiname alapján)
     * @param guiName Az osztályt meghatározó gui-beli név.
     * @return Az gui-beli név alapján megtalált osztály, illetve null 
     * amennyiben nem található a megadott guiname egyetlen dataprocessor beli
     * osztályban sem.
     */
    public static Class getClassByGuiName(String guiName) {
        Class[] classes = null;
        try {
            classes = getClasses("mobirentdesktop.entities");
        } catch (ClassNotFoundException ex) {
            Logger.getLogger(AnnotationExtractor.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(AnnotationExtractor.class.getName()).log(Level.SEVERE, null, ex);
        }

        for (Class clazz : classes) {
            if (clazz.isAnnotationPresent(ClassNameConnector.class)) {
                ClassNameConnector cnc = (ClassNameConnector) clazz.getAnnotation(ClassNameConnector.class);
                if (cnc.guiName().equals(guiName)) {
                    return clazz;
                }
            }
        }
        return null;
    }
    
    public static String getListIdByForm(javax.swing.JFrame form){
        if (form.getClass().isAnnotationPresent(EntityEditor.class)){
            return form.getClass().getAnnotation(EntityEditor.class).listId();
        }
        return null;
    }
}
