import javafx.beans.property.ListProperty;
import javafx.beans.property.MapProperty;
import javafx.beans.property.SetProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.beans.property.SimpleMapProperty;
import javafx.beans.property.SimpleSetProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.MapChangeListener;
import javafx.collections.SetChangeListener;

import java.util.Arrays;

public class ListPropertyTest {

    public static void testStringWorks() {
        System.out.println("==== Test String ==== ");
        StringProperty a = new SimpleStringProperty();
        StringProperty b = new SimpleStringProperty();
        StringProperty c = new SimpleStringProperty();
        c.addListener((obs, ov, nv) -> System.out.println("Change: " + nv));
        c.bind(a);
        c.bind(b);
        b.set("One");
        System.out.println("c contains: " + c.get());
        System.out.println();

        // Output:

        // Change: One
        // c contains: One
    }

    public static void testListFails() {
        System.out.println("==== Test List ==== ");
        ListProperty<String> a = new
                SimpleListProperty<>(FXCollections.observableArrayList());
        ListProperty<String> b = new
                SimpleListProperty<>(FXCollections.observableArrayList());
        ListProperty<String> c = new SimpleListProperty<>();
        c.addListener((ListChangeListener.Change<? extends String> change) ->
                System.out.println("Change: " + change));
        c.bind(a);
        c.bind(b);
        b.add("One");
        b.add("Two");
        System.out.println("c contains: " + Arrays.toString(c.toArray()));
        System.out.println();

        // Output:

        // Change: { [] added at 0 }
        // c contains: []
    }

    public static void testListWorks() {
        System.out.println("==== Test List with unbind ==== ");
        ListProperty<String> a = new
                SimpleListProperty<>(FXCollections.observableArrayList());
        ListProperty<String> b = new
                SimpleListProperty<>(FXCollections.observableArrayList());
        ListProperty<String> c = new SimpleListProperty<>();
        c.addListener((ListChangeListener.Change<? extends String> change) ->
                System.out.println("Change: " + change));
        c.bind(a);
        c.unbind(); // need to explicitly unbind
        c.bind(b);
        b.add("One");
        b.add("Two");
        System.out.println("c contains: " + Arrays.toString(c.toArray()));
        System.out.println();

        // Output:

        // Change: { [] added at 0 }
        // Change: { [] added at 0 }
        // Change: { [One] added at 0 }
        // Change: { [Two] added at 1 }
        // c contains: [One, Two]
    }

    public static void testSetFails() {
        System.out.println("==== Test Set ==== ");
        SetProperty<String> a = new
                SimpleSetProperty<>(FXCollections.observableSet());
        SetProperty<String> b = new
                SimpleSetProperty<>(FXCollections.observableSet());
        SetProperty<String> c = new SimpleSetProperty<>();
        c.addListener((SetChangeListener.Change<? extends String> change) ->
                System.out.println("Change: " + change));
        c.bind(a);
        c.bind(b);
        b.add("One");
        b.add("Two");
        System.out.println("c contains: " + Arrays.toString(c.toArray()));
        System.out.println();

        // Output:

        // c contains: []
    }

    public static void testSetWorks() {
        System.out.println("==== Test Set with unbind ==== ");
        SetProperty<String> a = new
                SimpleSetProperty<>(FXCollections.observableSet());
        SetProperty<String> b = new
                SimpleSetProperty<>(FXCollections.observableSet());
        SetProperty<String> c = new SimpleSetProperty<>();
        c.addListener((SetChangeListener.Change<? extends String> change) ->
                System.out.println("Change: " + change));
        c.bind(a);
        c.unbind(); // need to explicitly unbind
        c.bind(b);
        b.add("One");
        b.add("Two");
        System.out.println("c contains: " + Arrays.toString(c.toArray()));
        System.out.println();

        // Output:

        // Change: added One
        // Change: added Two
        // c contains: [Two, One]
    }

    public static void testMapFails() {
        System.out.println("==== Test Map ==== ");
        MapProperty<String, String> a = new
                SimpleMapProperty<>(FXCollections.observableHashMap());
        MapProperty<String, String> b = new
                SimpleMapProperty<>(FXCollections.observableHashMap());
        MapProperty<String, String> c = new SimpleMapProperty<>();
        c.addListener((MapChangeListener.Change<? extends String, ? extends String> change) ->
                System.out.println("Change: " + change));
        c.bind(a);
        c.bind(b);
        b.put("One", "1");
        b.put("Two", "2");
        System.out.println("c contains: " + c);
        System.out.println();

        // Output:

        // c contains: []
    }

    public static void testMapWorks() {
        System.out.println("==== Test Map with unbind ==== ");
        MapProperty<String, String> a = new
                SimpleMapProperty<>(FXCollections.observableHashMap());
        MapProperty<String, String> b = new
                SimpleMapProperty<>(FXCollections.observableHashMap());
        MapProperty<String, String> c = new SimpleMapProperty<>();
        c.addListener((MapChangeListener.Change<? extends String, ? extends String> change) ->
                System.out.println("Change: " + change));
        c.bind(a);
        c.unbind(); // need to explicitly unbind
        c.bind(b);
        b.put("One", "1");
        b.put("Two", "2");
        System.out.println("c contains: " + c);
        System.out.println();

        // Output:

        // Change: added 1 at key One
        // Change: added 2 at key Two
        // c contains: MapProperty [bound, value: {One=1, Two=2}]
    }

    public static void main(String[] args) {
        ListPropertyTest.testStringWorks();

        ListPropertyTest.testListFails();
        ListPropertyTest.testListWorks();

        ListPropertyTest.testSetFails();
        ListPropertyTest.testSetWorks();

        ListPropertyTest.testMapFails();
        ListPropertyTest.testMapWorks();
    }
}
