/*
 * Copyright (C) 2013 The Cat Hive Developers.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.cathive.fx.guice.fxml;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ResourceBundle;
import java.util.logging.Logger;

import com.cathive.fx.guice.GuiceFXMLLoader;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;

import com.cathive.fx.guice.FXMLComponent;
import com.google.inject.MembersInjector;

/**
 * 
 * @author Benjamin P. Jung
 * 
 * @since 2.0.0
 *
 * @param <T>
 */
final class FXMLComponentMembersInjector<T> implements MembersInjector<T> {

    private final GuiceFXMLLoader fxmlLoader;

    /** Logger for this class. */
    private static final Logger LOGGER = Logger.getLogger(FXMLComponentMembersInjector.class.getName());

    private final FXMLComponent annotation;

    FXMLComponentMembersInjector(final GuiceFXMLLoader fxmlLoader, final FXMLComponent annotation) {
        super();
        this.fxmlLoader = fxmlLoader;
        this.annotation = annotation;
    }

    @Override
    public void injectMembers(final T instance) {

        String locationString = annotation.location();
        if (locationString.isEmpty()) {
            locationString = String.format("%s.fxml", instance.getClass().getSimpleName());
            LOGGER.fine(String.format("No location for FXML component has been set for class '%s'. Assuming default ('%s').", instance.getClass().getName(), locationString));
        }
        URL location;
        location = instance.getClass().getResource(locationString);
        if (location == null) {
            LOGGER.fine(String.format("Location '%s' cannot be found on the classpath. Trying to construct a new URL...", locationString));
            try {
                location = new URL(locationString);
            } catch (final MalformedURLException e) {
                throw new RuntimeException(String.format("Cannot construct URL from string '%s'.", locationString), e);
            }
        }
        final FXMLLoader fxmlLoader = new FXMLLoader();
        fxmlLoader.setLocation(location);
        final String resourcesString = annotation.resources();
        if (!resourcesString.isEmpty()) {
            fxmlLoader.setResources(ResourceBundle.getBundle(resourcesString));
        }
        fxmlLoader.setCharset(Charset.forName(annotation.charset()));
        fxmlLoader.setController(instance);
        fxmlLoader.setRoot(instance);

        // Invoke "fxmlLoader.setTemplate(true)" if we are using JavaFX 8.0 or
        // higher to improve performance on objects that are created multiple times.
        try {
            final Method setTemplateMethod = FXMLLoader.class.getMethod("setTemplate", boolean.class);
            setTemplateMethod.invoke(fxmlLoader, Boolean.TRUE);
        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
            // We simply ignore this exception. It means that we are using
            // a JavaFX runtime prior to JavaFX 8.0.
        }

        // Actual instantiation of the component has to happen on the JavaFX thread.
        // We simply delegate the loading.
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                try {
                    final Object loaded = fxmlLoader.load();
                    if (loaded != instance) {
                        throw new IllegalStateException("Loading of FXML component went terribly wrong! :-(");
                    }
                } catch (final IOException e) {
                    throw new RuntimeException(e);
                }

            }
        });

    }

}

@@ -638,16 +638,214 @@
 .fxml;%0A%0A
+import com.cathive.fx.guice.FXMLComponent;%0Aimport com.cathive.fx.guice.GuiceFXMLLoader;%0Aimport com.google.inject.MembersInjector;%0Aimport javafx.application.Platform;%0Aimport javafx.fxml.FXMLLoader;%0A%0A
 import j
@@ -1113,214 +1113,25 @@
 r;%0A%0A
-import com.cathive.fx.guice.GuiceFXMLLoader;%0Aimport javafx.application.Platform;%0Aimport javafx.fxml.FXMLLoader;%0A%0Aimport com.cathive.fx.guice.FXMLComponent;%0Aimport com.google.inject.MembersInjector;%0A%0A/**%0A * 
+/**%0A * @param %3CT%3E
 %0A * 
@@ -1159,12 +1159,8 @@
 ung%0A
- * %0A
  * @
@@ -1175,25 +1175,8 @@
 0.0%0A
- *%0A * @param %3CT%3E%0A
  */%0A
@@ -1306,16 +1306,23 @@
 %0A    /**
+%0A     *
  Logger 
@@ -1336,16 +1336,21 @@
 s class.
+%0A    
  */%0A    
@@ -3878,101 +3878,34 @@
     
-Platform.runLater(new Runnable() %7B%0A            @Override%0A            public void run() %7B%0A    
+Runnable loader = () -%3E %7B%0A
     
@@ -3910,36 +3910,32 @@
           try %7B%0A
-    
                 
@@ -3987,28 +3987,24 @@
             
-    
 if (loaded !
@@ -4013,28 +4013,24 @@
 instance) %7B%0A
-    
             
@@ -4136,33 +4136,26 @@
             
-    %7D%0A   
+%7D%0A
             
@@ -4146,33 +4146,32 @@
   %7D%0A            
- 
 %7D catch (final I
@@ -4178,36 +4178,32 @@
 OException e) %7B%0A
-    
                 
@@ -4249,40 +4249,162 @@
     
-    %7D%0A%0A            %7D%0A        %7D);
+%7D%0A%0A        %7D;%0A%0A        if (Platform.isFxApplicationThread()) %7B%0A            loader.run();%0A        %7D else %7B%0A            Platform.runLater(loader);%0A        %7D
 %0A%0A  

Load FXML immediately when on FX Application Thread to prevent race condition issues