/*
 * Copyright (c) 2013, Tomas Mikula. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package codearea.skin;

import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;

import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.control.IndexRange;
import javafx.scene.control.IndexedCell;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;

import com.sun.javafx.scene.control.skin.VirtualFlow;

class MyListView<T, C extends ListCell<T>> extends ListView<T> {

    @SuppressWarnings("unchecked")
    public MyListView(ObservableList<T> items, Function<? super MyListView<T, C>, C> cellFactory) {
        super(items);
        super.setCellFactory(lv -> cellFactory.apply((MyListView<T, C>) lv));
    }

    public void show(int index) {
        getFlow().ifPresent(flow -> flow.show(index));
    }

    public void show(int index, Consumer<C> whenShown) {
        getFlow().ifPresent(flow -> {
            flow.show(index);
            Platform.runLater(() -> { // runLater to allow layout after show()
                C cell = flow.getCell(index);
                if(cell.getIndex() == index) { // true only if cell was cached
                    whenShown.accept(cell);
                }
            });
        });
    }

    public void showAsFirst(int index) {
        getFlow().ifPresent(flow -> showAsFirst(flow, index));
    }

    public void showAsLast(int index) {
        getFlow().ifPresent(flow -> showAsLast(flow, index));
    }

    public int getFirstVisibleIndex() {
        Optional<Integer> index = getFlow().map(f -> {
            ListCell<T> cell = f.getFirstVisibleCell();
            return cell != null ? cell.getIndex() : -1;
        });
        return index.orElse(-1);
    }

    public int getLastVisibleIndex() {
        Optional<Integer> index = getFlow().map(f -> {
            ListCell<T> cell = f.getLastVisibleCell();
            return cell != null ? cell.getIndex() : -1;
        });
        return index.orElse(-1);
    }

    public IndexRange getVisibleRange() {
        Optional<IndexRange> range = getFlow().map(f -> {
            ListCell<T> first = f.getFirstVisibleCell();
            ListCell<T> last = f.getLastVisibleCell();
            if(first != null && last != null) {
                return new IndexRange(first.getIndex(), last.getIndex() + 1);
            } else {
                return new IndexRange(0, 0);
            }
        });
        return range.orElse(new IndexRange(0, 0));
    }

    /**
     * Returns the cell for the given index.
     * The returned cell shall be used read-only
     * (for measurement purposes) and shall not be stored.
     */
    public Optional<C> getCell(int index) {
        return getFlow().map(flow -> flow.getCell(index));
    }

    private static <C extends IndexedCell<?>> void showAsFirst(VirtualFlow<C> flow, int index) {
        flow.show(index);
        C cell = flow.getVisibleCell(index);
        assert cell != null;
        flow.showAsFirst(cell);
    }

    private static <C extends IndexedCell<?>> void showAsLast(VirtualFlow<C> flow, int index) {
        flow.show(index);
        C cell = flow.getVisibleCell(index);
        assert cell != null;
        flow.showAsLast(cell);
    }

    @SuppressWarnings("unchecked")
    private Optional<VirtualFlow<C>> getFlow() {
        for(Node child: getChildren()) {
            if(child instanceof VirtualFlow)
                return Optional.of((VirtualFlow<C>) child);
        }
        return Optional.empty();
    }
}

@@ -1236,16 +1236,54 @@
 tional;%0A
+import java.util.function.BiConsumer;%0A
 import j
@@ -1495,49 +1495,8 @@
 ge;%0A
-import javafx.scene.control.IndexedCell;%0A
 impo
@@ -2080,24 +2080,167 @@
 henShown) %7B%0A
+        show(index, (flow, cell) -%3E whenShown.accept(cell));%0A    %7D%0A%0A    public void show(int index, BiConsumer%3CVirtualFlow%3CC%3E, C%3E whenShown) %7B%0A
         getF
@@ -2532,24 +2532,30 @@
 hown.accept(
+flow, 
 cell);%0A     
@@ -2655,36 +2655,41 @@
     
-getFlow().ifPresent(flow -%3E 
+show(index, (flow, cell) -%3E flow.
 show
@@ -2696,27 +2696,20 @@
 AsFirst(
-flow, index
+cell
 ));%0A    
@@ -2763,58 +2763,56 @@
     
-getFlow().ifPresent(flow -%3E showAsLast(flow, index
+show(index, (flow, cell) -%3E flow.showAsLast(cell
 ));%0A
@@ -4047,397 +4047,202 @@
     
-return getFlow().map(flow -%3E flow.getCell(index));%0A    %7D%0A%0A    private static %3CC extends IndexedCell%3C?%3E%3E void showAsFirst(VirtualFlow%3CC%3E flow, int index) %7B%0A        flow.show(index);%0A        C cell = flow.getVisibleCell(index);%0A        assert cell != null;%0A        flow.showAsFirst(cell);
+// If the cell for the given index is not available (~visible),%0A        // VirtualFlow may return some arbitrary cell. Therefore we
 %0A    
-%7D%0A%0A
     
-private static %3CC extends IndexedCell%3C?%3E%3E void showAsLast(VirtualFlow%3CC%3E flow, int
+// check that the returned cell has the desired
  index
-) %7B
+.
 %0A   
@@ -4250,42 +4250,53 @@
     
-flow.show(index);%0A        C cell =
+return getFlow()%0A                .map(flow -%3E
  flo
@@ -4300,23 +4300,16 @@
 flow.get
-Visible
 Cell(ind
@@ -4303,33 +4303,33 @@
 w.getCell(index)
-;
+)
 %0A        assert 
@@ -4325,57 +4325,56 @@
     
-assert cell != null;%0A        flow.showAsLast(cell
+        .filter(cell -%3E cell.getIndex() == index
 );%0A 

Make getCell() fail faster.