package com.zachsthings.libcomponents;

import com.zachsthings.libcomponents.loader.ComponentLoader;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * A simple manager that keeps track of components and what they should do.
 * @author zml2008
 */
public abstract class ComponentManager<T extends AbstractComponent> {
    protected final Logger logger;
    protected final Class<T> componentClass;
    protected final List<ComponentLoader> loaders = new ArrayList<ComponentLoader>();
    protected final Map<String, T> registeredComponents = new LinkedHashMap<String, T>();
    protected final Map<Class<? extends Annotation>, AnnotationHandler<?>> annotationHandlers = new LinkedHashMap<Class<? extends Annotation>, AnnotationHandler<?>>();
    
    public ComponentManager(Logger logger, Class<T> componentCass) {
        this.logger = logger;
        this.componentClass = componentCass;
    }

    public synchronized boolean addComponentLoader(ComponentLoader loader) {
        return loaders.add(loader);
    }


    public synchronized boolean loadComponents() throws InvalidComponentException {
        for (ComponentLoader loader : loaders) {
            for (AbstractComponent baseComponent : loader.loadComponents()) {
                if (!componentClass.isAssignableFrom(baseComponent.getClass())) {
                    throw new InvalidComponentException(baseComponent.getClass(), "Component is not an instance of " + componentClass.getCanonicalName());
                }
                T component = componentClass.cast(baseComponent);
                ComponentInformation info = component.getClass().getAnnotation(ComponentInformation.class);
                component.setUp(loader, info);
                setUpComponent(component);
                registeredComponents.put(info.friendlyName().replaceAll(" ", "-").toLowerCase(), component);
            }
        }
        return true;
    }

    protected abstract void setUpComponent(T component);

    public synchronized void enableComponents() {
        for (T component : registeredComponents.values()) {
            for (Field field : component.getClass().getDeclaredFields()) {
                field.setAccessible(true);
                for (Annotation annotation : field.getAnnotations()) {
                    AnnotationHandler<Annotation> handler =
                            (AnnotationHandler<Annotation>)annotationHandlers.get(annotation.annotationType());
                    if (handler != null) {
                        if (!handler.handle(component, field, annotation)) {
                            logger.log(Level.WARNING, "Component "
                                    + component.getInformation().friendlyName() +
                                    " could not be enabled! Error in annotation handler for field " + field);
                        }
                    }
                }
            }
            component.enable();
            component.setEnabled(true);
            component.saveConfig();
            logger.log(Level.FINEST, "Component " +
                    component.getInformation().friendlyName() + " successfully enabled!");
        }

    }
    
    public synchronized void unloadComponents() {
        for (T component : registeredComponents.values()) {
            component.disable();
        }
        registeredComponents.clear();
    }
    
    public synchronized void reloadComponents() {
        for (T component : registeredComponents.values()) {
            component.reload();
        }
    }
    
    public synchronized <C> C getComponent(Class<C> type) {
        for (T component : registeredComponents.values()) {
            if (component.getClass().equals(type)) {
                return type.cast(component);
            }
        }
        return null;
    }
    
    public Collection<T> getComponents() {
        return Collections.unmodifiableCollection(registeredComponents.values());
    }
    
    public AbstractComponent getComponent(String friendlyName) {
        return registeredComponents.get(friendlyName);
    }
    
    public synchronized <A extends Annotation> void registerAnnotationHandler(Class<A> annotation, AnnotationHandler<A> handler) {
        annotationHandlers.put(annotation, handler);
    }
    
    public synchronized <A extends Annotation> AnnotationHandler<A> getAnnotationHandler(Class<A> annotation) {
        return (AnnotationHandler<A>) annotationHandlers.get(annotation);
    }
}

@@ -2178,32 +2178,54 @@
 nts.values()) %7B%0A
+            try %7B%0A    
             for 
@@ -2279,24 +2279,28 @@
 Fields()) %7B%0A
+    
             
@@ -2342,24 +2342,28 @@
             
+    
 for (Annotat
@@ -2425,16 +2425,20 @@
         
+ 
+   
 Annotati
@@ -2497,16 +2497,20 @@
         
+  
+  
 (Annotat
@@ -2532,16 +2532,17 @@
 tation%3E)
+ 
 annotati
@@ -2602,24 +2602,28 @@
             
+  
+  
 if (handler 
@@ -2629,24 +2629,28 @@
  != null) %7B%0A
+    
             
@@ -2734,32 +2734,36 @@
                 
+    
 logger.log(Level
@@ -2821,16 +2821,20 @@
         
+ 
+   
 + compon
@@ -2871,16 +2871,20 @@
 ame() +%0A
+    
         
@@ -2985,16 +2985,46 @@
 field);%0A
+                            %7D%0A
         
@@ -3081,34 +3081,24 @@
       %7D%0A    
-        %7D%0A
             
@@ -3121,32 +3121,36 @@
 ();%0A            
+   
+ 
 component.setEna
@@ -3165,32 +3165,36 @@
 e);%0A            
+    
 component.saveCo
@@ -3205,32 +3205,36 @@
 ();%0A            
+ 
+   
 logger.log(Level
@@ -3269,32 +3269,36 @@
                 
+  
+  
 component.getInf
@@ -3356,26 +3356,257 @@
 %22);%0A        
+    %7D catch (Throwable t) %7B%0A                logger.log(Level.SEVERE, %22Component %22 +%0A                        component.getInformation().friendlyName() + %22 failed to load!%22);%0A                t.printStackTrace();%0A            %7D%0A        
 %7D
-%0A
 %0A    %7D%0A    %0A

Handle init failure more gracefully