/*
 * 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.prefs;

import static java.util.prefs.Preferences.systemNodeForPackage;
import static java.util.prefs.Preferences.userNodeForPackage;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.prefs.Preferences;

import javafx.beans.property.Property;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.beans.value.WritableBooleanValue;
import javafx.beans.value.WritableDoubleValue;
import javafx.beans.value.WritableFloatValue;
import javafx.beans.value.WritableIntegerValue;
import javafx.beans.value.WritableLongValue;
import javafx.beans.value.WritableStringValue;
import javafx.beans.value.WritableValue;

import com.cathive.fx.guice.PersistentProperty;
import com.cathive.fx.guice.PersistentProperty.NodeType;
import com.google.inject.MembersInjector;

/**
 * 
 * @author Benjamin P. Jung
 */
final class PersistentPropertyMembersInjector<T> implements MembersInjector<T> {

    private final Field field;
    private final PersistentProperty annotation;

    PersistentPropertyMembersInjector(final Field field, final PersistentProperty annotation) {
        super();
        this.field = field;
        this.annotation = annotation;
        field.setAccessible(true);
    }

    @Override
    public void injectMembers(final T instance) {

        final NodeType nodeType = annotation.type();
        final Class<?> nodeClass = annotation.clazz();

        final Preferences prefs;
        switch (nodeType) {
        case SYSTEM_NODE:
            prefs = systemNodeForPackage(nodeClass);
            break;
        case USER_NODE:
            prefs = userNodeForPackage(nodeClass);
            break;
        default:
            throw new IllegalStateException(String.format("Unknown Preferences node type: %s!", nodeType));
        }

        updatePropertyField(instance, prefs.get(annotation.key(), null));

        try {
            ((Property<?>) field.get(instance)).addListener(new ChangeListener<Object>() {
                @Override
                public void changed(ObservableValue<?> prop, Object oldValue, Object newValue) {
                    final String curVal = prefs.get(annotation.key(), null);
                    System.out.println("[PROP->PREF] current value: " + curVal + "  new value: " + newValue + "  equals==" + Objects.equals(curVal, newValue));
                    prefs.put(annotation.key(), String.valueOf(newValue));
                }
            });
        } catch (IllegalArgumentException | IllegalAccessException e) {
            // TODO Use a more meaningful exception
            throw new RuntimeException(e);
        }

    }

    private void updatePropertyField(T instance, String newVal) {
        @SuppressWarnings("unchecked")
        final Class<? extends Property<?>> fieldType = (Class<? extends Property<?>>) field.getType();
        try {
            final Object fieldInstance = field.get(instance);
            final Method getter = WritableValue.class.getMethod("getValue");
            final Object curVal = getter.invoke(fieldInstance);

            System.out.println("[PREF->PROP] current value: " + curVal + "  new value: " + newVal + "  equals==" + Objects.equals(curVal, newVal));
            final Method setter = WritableValue.class.getMethod("setValue", Object.class);
            if (WritableStringValue.class.isAssignableFrom(fieldType)) {
                setter.invoke(fieldInstance, newVal);
            } else if (WritableBooleanValue.class.isAssignableFrom(fieldType)) {
                setter.invoke(fieldInstance, newVal == null ? null : Boolean.valueOf(newVal));
            } else if (WritableIntegerValue.class.isAssignableFrom(fieldType)) {
                setter.invoke(fieldInstance, newVal == null ? null : Integer.valueOf(newVal));
            } else if (WritableLongValue.class.isAssignableFrom(fieldType)) {
                setter.invoke(fieldInstance, newVal == null ? null : Long.valueOf(newVal));
            } else if (WritableDoubleValue.class.isAssignableFrom(fieldType)) {
                setter.invoke(fieldInstance, newVal == null ? null : Double.valueOf(newVal));
            } else if (WritableFloatValue.class.isAssignableFrom(fieldType)) {
                setter.invoke(fieldInstance, newVal == null ? null : Float.valueOf(newVal));
            }
        } catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | SecurityException e) {
            // TODO Use a more meaningful exception
            throw new RuntimeException(e);
        }
    }

}

@@ -887,34 +887,8 @@
 od;%0A
-import java.util.Objects;%0A
 impo
@@ -2886,168 +2886,8 @@
 l);%0A
-                    System.out.println(%22%5BPROP-%3EPREF%5D current value: %22 + curVal + %22  new value: %22 + newValue + %22  equals==%22 + Objects.equals(curVal, newValue));%0A
     
@@ -3238,16 +3238,22 @@
 g newVal
+String
 ) %7B%0A    
@@ -3396,32 +3396,33 @@
 ;%0A        try %7B%0A
+%0A
             fina
@@ -3566,293 +3566,179 @@
 nal 
-Object curVal = getter.invoke(fieldInstance);%0A%0A            System.out.println(%22%5BPREF-%3EPROP%5D current value: %22 + curVal + %22  new value: %22 + newVal + %22  equals==%22 + Objects.equals(curVal, newVal));%0A            final Method setter = WritableValue.class.getMethod(%22setValue%22, Object.class);
+Method setter = WritableValue.class.getMethod(%22setValue%22, Object.class);%0A%0A            final Object curVal = getter.invoke(fieldInstance);%0A            final Object newVal;%0A
 %0A   
@@ -3827,44 +3827,29 @@
     
-setter.invoke(fieldInstance,
+newVal =
  newVal
-)
+String
 ;%0A  
@@ -3946,37 +3946,8 @@
     
- setter.invoke(fieldInstance,
  new
@@ -3951,31 +3951,16 @@
 newVal =
-=
- null ? null :
  Boolean
@@ -3966,33 +3966,38 @@
 n.valueOf(newVal
-)
+String
 );%0A            %7D
@@ -4084,60 +4084,16 @@
     
-setter.invoke(fieldInstance, newVal == null ? null :
+newVal =
  Int
@@ -4103,33 +4103,38 @@
 r.valueOf(newVal
-)
+String
 );%0A            %7D
@@ -4218,60 +4218,16 @@
     
-setter.invoke(fieldInstance, newVal == null ? null :
+newVal =
  Lon
@@ -4234,33 +4234,38 @@
 g.valueOf(newVal
-)
+String
 );%0A            %7D
@@ -4351,60 +4351,16 @@
     
-setter.invoke(fieldInstance, newVal == null ? null :
+newVal =
  Dou
@@ -4369,33 +4369,38 @@
 e.valueOf(newVal
-)
+String
 );%0A            %7D
@@ -4485,98 +4485,214 @@
     
-setter.invoke(fieldInstance, newVal == null ? null : Float.valueOf(newVal));%0A            %7D
+newVal = Float.valueOf(newValString);%0A            %7D else %7B%0A                throw new IllegalStateException(%22Cannot inject value into field.%22);%0A            %7D%0A            setter.invoke(fieldInstance, newVal);
 %0A   

Clean up code