import java.util.ArrayList;
import java.util.List;

/** Properti teramati mini: meniru semantik JavaFX di kelas Java biasa. */
class Properti<T> {
    private T nilai;
    private final List<Runnable> pendengarInvalidasi = new ArrayList<>();
    private final List<ListenerPerubahan<T>> pendengarPerubahan = new ArrayList<>();

    interface ListenerPerubahan<T> {
        void saatBerubah(T lama, T baru);
    }

    Properti(T awal) {
        this.nilai = awal;
    }

    T get() {
        return nilai;
    }

    void set(T baru) {
        if (baru.equals(nilai)) {
            return; // nilai sama: tidak ada kejadian apa pun
        }
        T lama = nilai;
        nilai = baru;
        pendengarInvalidasi.forEach(Runnable::run);
        pendengarPerubahan.forEach(p -> p.saatBerubah(lama, baru));
    }

    void saatInvalid(Runnable r) {
        pendengarInvalidasi.add(r);
    }

    void saatBerubah(ListenerPerubahan<T> l) {
        pendengarPerubahan.add(l);
    }
}

public class PropertiMini {
    public static void main(String[] args) {
        Properti<Integer> harga = new Properti<>(5000);
        Properti<Integer> jumlah = new Properti<>(3);

        harga.saatInvalid(() -> System.out.println("[invalidasi] harga ditandai usang"));
        harga.saatBerubah((lama, baru) ->
                System.out.println("[perubahan ] harga " + lama + " -> " + baru));

        // Ikat satu arah: total selalu harga x jumlah.
        Runnable hitungTotal = () -> System.out.println(
                "[binding   ] total kini = " + (harga.get() * jumlah.get()));
        harga.saatBerubah((l, b) -> hitungTotal.run());
        jumlah.saatBerubah((l, b) -> hitungTotal.run());

        System.out.println("total awal = " + (harga.get() * jumlah.get()));

        harga.set(6000);  // invalidasi + perubahan + hitung ulang
        jumlah.set(4);    // perubahan jumlah -> hitung ulang
        harga.set(6000);  // nilai sama: senyap sepenuhnya

        // Ikat dua arah: milimeter dan sentimeter saling mengikuti.
        Properti<Double> mm = new Properti<>(2500.0);
        Properti<Double> cm = new Properti<>(250.0);

        mm.saatBerubah((l, b) -> {
            double target = b / 10;
            if (cm.get() != target) {
                cm.set(target);
            }
        });
        cm.saatBerubah((l, b) -> {
            double target = b * 10;
            if (mm.get() != target) {
                mm.set(target);
            }
        });

        mm.set(3200.0);
        System.out.println("setelah mm=3200 : cm = " + cm.get());
        cm.set(41.0);
        System.out.println("setelah cm=41   : mm = " + mm.get());
    }
}
