
package uk.co.wingpath.util;

import uk.co.wingpath.event.*;

/**
* Instances of this class are used to "couple" two {@link Variable}s, so
* that if the value of one variable is changed the value of the other
* variable will be changed to the same value.
*/

public class Coupler<T>
{
    private final Variable<T> variable1;
    private final Variable<T> variable2;
    private final ValueListener listener1;
    private final ValueListener listener2;

    /**
    * Constructs a Coupler between the supplied variables.
    * @param variable1 one variable.
    * @param variable2 the other variable.
    */
    private Coupler (final Variable<T> variable1, final Variable<T> variable2)
    {
        this.variable1 = variable1;
        this.variable2 = variable2;

        listener1 =
            new ValueListener ()
            {
                public void valueChanged (ValueEvent e)
                {
                    if (!e.isChanging ())
                        variable2.setValue (variable1.getValue ());
                }
            };

        listener2 =
            new ValueListener ()
            {
                public void valueChanged (ValueEvent e)
                {
                    if (!e.isChanging ())
                        variable1.setValue (variable2.getValue ());
                }
            };
    }

    /**
    * Creates a Coupler between the supplied variables.
    * <p>A {@link ValueListener} is added to each variable in order to
    * implement the coupling. The {@link #uncouple} method may be called
    * to remove these listeners.
    * @param variable1 one variable.
    * @param variable2 the other variable.
    * @return the created {@code Coupler}.
    */
    public static <T> Coupler<T> couple (Variable<T> variable1,
        Variable<T> variable2)
    {
        Coupler<T> coupler = new Coupler<T> (variable1, variable2);
        coupler.couple ();
        return coupler;
    }

    /**
    * Adds {@link ValueListener}s to each variable in order to
    * implement the coupling. The {@link #uncouple} method may be called
    * to remove these listeners.
    */
    public void couple ()
    {
        uncouple ();    // Ensure listeners aren't added twice.
        variable1.addValueListener (listener1);
        variable2.addValueListener (listener2);
    }

    /**
    * Removes the coupling listeners from the variables.
    * The listeners may be put back again using the {@link #couple} method.
    */
    public void uncouple ()
    {
        variable1.removeValueListener (listener1);
        variable2.removeValueListener (listener2);
    }
}

