import com.sun.javafx.collections.ObservableListWrapper; 
import javafx.collections.ListChangeListener; 
import org.junit.Assert; 
import org.junit.Ignore; 
import org.junit.Test; 

import java.util.ArrayList; 
import java.util.Arrays; 
import java.util.List; 

/** 
 * Expose JavaFX {@linkplain javafx.collections.ListChangeBuilder} bugs. 
 * @author hollisw 
 */ 
@Ignore 
public class ListChangeBuilderTest { 
    /** 
     * Expose {@linkplain ObservableListWrapper#beginChange()} and 
     * {@linkplain ObservableListWrapper#endChange()}. 
     */ 
    private static class ExposedObservableList<E> extends ObservableListWrapper<E> { 
        ExposedObservableList(List<E> list) {super(list);} 
        public void doBeginChange() {beginChange();} 
        public void doEndChange() {endChange();} 
    } 

    /** 
     * An element is added on index 0, then it is removed. A change event is not fired for 
     * these changes, because no real change was made to the list. However, this is not taken 
     * into account when calculating the indices of the removed elements on higher positions, 
     * when the removals occur between the add and remove on same index. When a removal 
     * occurs on an index for which an addition already occurred in the same commit, 
     * all removals on higher positions should be recalculated. 
     */ 
    @Test 
    public void testAddRemoveOnSameIndex() { 
        // [1, 2] 
        final ExposedObservableList<Integer> list = 
         new ExposedObservableList<>(new ArrayList<>(Arrays.asList(1, 2))); 

        list.addListener( 
            (ListChangeListener<? super Integer>) change -> { 
                change.next(); 
                Assert.assertEquals(1, change.getRemovedSize()); 
                Assert.assertEquals(1, change.getFrom()); 
                Assert.assertFalse(change.wasAdded()); 
                Assert.assertFalse(change.next()); 
            } 
        ); 

        list.doBeginChange(); 

        // [0, 1, 2] 
        list.add(0, 0); 

        // [0, 1] 
        list.remove(2); 

        // [1] 
        list.remove(0); 
        list.doEndChange(); 
    } 

    /** @see #testAddRemoveOnSameIndex() */ 
    @Test 
    public void testAddRemoveOnSameIndex2() { 
        // [1, 2] 
        final ExposedObservableList<Integer> list = 
         new ExposedObservableList<>(new ArrayList<>(Arrays.asList(1, 2))); 

        list.addListener( 
            (ListChangeListener<? super Integer>) change -> { 
                change.next(); 
                Assert.assertEquals(1, change.getAddedSize()); 
                Assert.assertEquals(2, change.getFrom()); 
                Assert.assertFalse(change.wasRemoved()); 
                Assert.assertFalse(change.next()); 
            } 
        ); 

        list.doBeginChange(); 

        // [0, 1, 2] 
        list.add(0, 0); 

        // [0, 1, 2, 3] 
        list.add(3); 

        // [1, 2, 3] 
        list.remove(0); 

        list.doEndChange(); 
    } 

    /** 
     * An element is added on index 2, elements are removed on index 0 and 1 - the removal on 
     * index 1 actually removes the element added on index 2, so a change event is not fired 
     * for the addition on index 2 and the removal on index 1, because the element was added 
     * and removed in the same commit. However this is not taken into account when 
     * calculating the indexes of the removed elements on higher positions, when the removals 
     * occur between the addition and removal of the same element in the same commit - all 
     * removals on higher positions should be recalculated. 
     */ 
    @Test 
    public void testIndirectAddRemoveOnSameIndex() { 
        // [1, 2, 3, 4] 
        final ExposedObservableList<Integer> list = 
         new ExposedObservableList<>(new ArrayList<>(Arrays.asList(1, 2, 3, 4))); 

        list.addListener( 
            (ListChangeListener.Change<? extends Integer> change) -> { 
                change.next(); 
                Assert.assertEquals(1, change.getRemovedSize()); 
                Assert.assertEquals(0, change.getFrom()); 
                Assert.assertFalse(change.wasAdded()); 

                change.next(); 
                Assert.assertEquals(1, change.getRemovedSize()); 
                Assert.assertEquals(2, change.getFrom()); 
                Assert.assertFalse(change.wasAdded()); 

                Assert.assertFalse(change.next()); 
            } 
        ); 

        list.doBeginChange(); 

        list.add(2, 6); 
        list.remove(4); 
        list.remove(0); 
        list.remove(1); 
        list.doEndChange(); 
    } 

    /** @see #testIndirectAddRemoveOnSameIndex() */ 
    @Test 
    public void testIndirectAddRemoveOnSameIndex2() { 
        final ExposedObservableList<Integer> list = 
         new ExposedObservableList<>(new ArrayList<>(Arrays.asList(1, 2, 3, 4))); 

        list.addListener( 
            (ListChangeListener.Change<? extends Integer> change) -> { 
                change.next(); 
                Assert.assertEquals(1, change.getRemovedSize()); 
                Assert.assertEquals(0, change.getFrom()); 
                Assert.assertFalse(change.wasAdded()); 

                change.next(); 
                Assert.assertEquals(1, change.getAddedSize()); 
                Assert.assertEquals(2, change.getFrom()); 
                Assert.assertFalse(change.wasRemoved()); 

                Assert.assertFalse(change.next()); 
            } 
        ); 

        list.doBeginChange(); 
        list.add(2, 6); 
        list.add(4, 5); 
        list.remove(0); 
        list.remove(1); 
        list.doEndChange(); 
    } 
} 