/*******************************************************************************
 * Copyright (c) 2015-2016 Oak Ridge National Laboratory.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *******************************************************************************/
package org.csstudio.display.builder.model;

import static org.csstudio.display.builder.model.properties.CommonWidgetProperties.positionX;
import static org.csstudio.display.builder.model.properties.CommonWidgetProperties.positionY;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

import org.csstudio.display.builder.model.macros.Macros;
import org.csstudio.display.builder.model.widgets.GroupWidget;
import org.junit.Test;

/** JUnit test of widget property subscriptions
 *  @author Kay Kasemir
 */
@SuppressWarnings("nls")
public class WidgetPropertySubscriptionUnitTest
{
    /** Check subscription updates */
    @Test
    public void testBasicSubscription()
    {
        final AtomicInteger updates = new AtomicInteger(0);
        final Widget widget = new Widget("generic");
        final WidgetPropertyListener<Integer> listener = (property, old_value, new_value) ->
        {
            updates.incrementAndGet();
            System.out.println(property.getName() + " changed to " + new_value);
        };
        widget.positionX().addPropertyListener(listener);

        // Noting, yet
        assertThat(updates.get(), equalTo(0));

        // Change once
        widget.positionX().setValue(21);
        assertThat(updates.get(), equalTo(1));

        // Change again
        widget.getProperty(positionX).setValue(22);
        assertThat(updates.get(), equalTo(2));

        // No change, same value
        widget.getProperty(positionX).setValue(22);
        assertThat(updates.get(), equalTo(2));
    }

    /** Check subscription updates */
    @Test
    public void testSpecificSubscription()
    {
        final Widget widget = new Widget("generic");

        final AtomicInteger x_updates = new AtomicInteger(0);
        final AtomicInteger y_updates = new AtomicInteger(0);

        widget.positionX().addPropertyListener((p, o, n) ->
        {
            x_updates.incrementAndGet();
            System.out.println(p.getName() + " = " + n);
        });
        widget.positionY().addUntypedPropertyListener((p, o, n) ->
        {
            y_updates.incrementAndGet();
            System.out.println(p.getName() + " = " + n);
        });

        // Noting, yet
        assertThat(x_updates.get(), equalTo(0));
        assertThat(y_updates.get(), equalTo(0));

        // Change one
        widget.getProperty(positionX).setValue(21);
        assertThat(x_updates.get(), equalTo(1));
        assertThat(y_updates.get(), equalTo(0));

        // Change other
        widget.getProperty(positionY).setValue(21);
        assertThat(x_updates.get(), equalTo(1));
        assertThat(y_updates.get(), equalTo(1));
    }

    @Test
    public void testMacroizedValueChanges()
    {
        // Group widget supports macros
        final GroupWidget widget = new GroupWidget();

        final Macros macros = new Macros();
        macros.add("NAME", "Fred");
        widget.widgetMacros().setValue(macros);

        final MacroizedWidgetProperty<String> name_prop = (MacroizedWidgetProperty<String>) widget.widgetName();
        final AtomicInteger updates = new AtomicInteger();
        final AtomicReference<String> received_value = new AtomicReference<String>(null);
        name_prop.addPropertyListener((prop, old_value, new_value) ->
        {
            System.out.println(prop.getName() + " changes from " + old_value + " to " + new_value);
            updates.incrementAndGet();
            received_value.set(new_value);
        });

        assertThat(updates.get(), equalTo(0));

        // Setting the specification triggers a notification
        name_prop.setSpecification("$(NAME)");
        assertThat(updates.get(), equalTo(1));

        // The listener received null
        assertThat(received_value.get(), nullValue());

        // _IF_ the listener called name_prop.getValue(), that
        // would resolve macros, but also trigger another listener invocation.

        // Fetching the value will resolve macros
        // and that triggers another update
        assertThat(name_prop.getValue(), equalTo("Fred"));
        assertThat(updates.get(), equalTo(2));
        assertThat(received_value.get(), equalTo("Fred"));
    }
}

@@ -4481,16 +4481,105 @@
 ved null
+, because only the specification%0D%0A        // was set, and the value has not been resolved
 %0D%0A      
@@ -4658,16 +4658,68 @@
 listener
+, triggered by the specification update,%0D%0A        //
  called 
@@ -4744,28 +4744,16 @@
 (), that
-%0D%0A        //
  would r
@@ -4770,17 +4770,24 @@
 ros,
- but also
+%0D%0A        // and
  tri
@@ -5082,23 +5082,523 @@
 o(%22Fred%22));%0D
+%0A%0D%0A        // Setting the value (not the description) to something%0D%0A        // that doesn't contain macros will just set the value.%0D%0A        name_prop.setValue(%22New Name%22);%0D%0A        assertThat(updates.get(), equalTo(3));%0D%0A        assertThat(received_value.get(), equalTo(%22New Name%22));%0D%0A        // Fetching that value does not trigger macro resolution%0D%0A        // and another value update%0D%0A        assertThat(name_prop.getValue(), equalTo(%22New Name%22));%0D%0A        assertThat(updates.get(), equalTo(3));%0D
 %0A    %7D%0D%0A%7D%0D%0A

Add detail to macroized prop test