/**
 * Copyright (c) 2016 Chiral Behaviors, LLC, all rights reserved.
 * 
 
 *  This file is part of Ultrastructure.
 *
 *  Ultrastructure is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  ULtrastructure 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 Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with Ultrastructure.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.chiralbehaviors.graphql.layout.controls;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

import com.fasterxml.jackson.databind.JsonNode;
import com.sun.javafx.scene.control.skin.VirtualScrollBar;

import javafx.beans.value.ChangeListener;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.control.Control;
import javafx.scene.control.ListView;
import javafx.scene.control.ScrollBar;
import javafx.scene.control.TableRow;

public class NestedTableRow<T> extends TableRow<T> {

    public static class Nested {
        public final Control control;
        public final double  height;

        public Nested(Control control, double height) {
            this.control = control;
            this.height = height;
        }

        @Override
        public String toString() {
            return String.format("Nested [height=%s, control=%s]", height,
                                 control);
        }
    }

    public static class Nesting {
        private final Nested[] nested;
        private final int      cardinality;

        public Nesting(int count, int cardinality) {
            nested = new Nested[count];
            this.cardinality = cardinality;
        }

        public boolean layout(String id, int column, Nested child) {
            //            if (nested[column] != null
            //                && nested[column].control != child.control) {
            //                System.out.println(String.format("%s[%s] already exists", id,
            //                                                 column));
            //            }
            nested[column] = child;
            for (Nested n : nested) {
                if (n == null) {
                    return false;
                }
            }
            layout();
            return true;
        }

        private void layout() {
            List<Nested> link = Arrays.asList(nested);
            double max = link.stream()
                             .mapToDouble(p -> p.height)
                             .max()
                             .orElse(-1);
            link.forEach(p1 -> {
                double height = cardinality * max;
                if (p1.control instanceof ListView) {
                    ((ListView<?>) p1.control).setFixedCellSize(max);
                }
                p1.control.setPrefHeight(height);
            });
        }
    }

    private final Map<String, Nesting>              layouts = new HashMap<>(3);
    private final Map<String, ListView<JsonNode>[]> links   = new HashMap<>(3);

    @Override
    protected void layoutChildren() {
        super.layoutChildren();
    }

    public void layout(String id, Integer column, Control child,
                       int cardinality, double height, int count) {
        Nesting nesting = layouts.computeIfAbsent(id,
                                                  k -> new Nesting(count,
                                                                   cardinality));
        if (nesting.layout(id, column, new Nested(child, height))) {
            layouts.remove(id);
        }
    }

    public void link(String id, Integer column, ListView<JsonNode> child,
                     int count) {
        @SuppressWarnings("unchecked")
        ListView<JsonNode>[] link = links.computeIfAbsent(id,
                                                          k -> new ListView[count]);
        //        if (link[column] != null && link[column] != child) {
        //            System.out.println(String.format("%s[%s]:%s != %s", id, column,
        //                                             child, link[column]));
        //        }

        link[column] = child;
        for (Object v : link) {
            if (v == null) {
                return;
            }
        }
        link(Arrays.asList(link));
        links.remove(id);
    }

    private void link(List<ListView<JsonNode>> link) {
        Stack<ScrollBar> scrolls = new Stack<>();
        link.forEach(p -> {
            Set<Node> deadSeaScrolls = p.lookupAll(".scroll-bar:vertical");
            VirtualScrollBar scrollbar = deadSeaScrolls.stream()
                                                       .filter(n -> n instanceof VirtualScrollBar)
                                                       .map(n -> (VirtualScrollBar) n)
                                                       .filter(n -> n.getOrientation()
                                                                     .equals(Orientation.VERTICAL))
                                                       .reduce((a, b) -> b)
                                                       .orElse(null);
            scrolls.push(scrollbar);
            p.getSelectionModel()
             .selectedIndexProperty()
             .addListener((o, pr, c) -> {
                 link.forEach(sibling -> {
                     if (sibling != p) {
                         sibling.getSelectionModel()
                                .select(c.intValue());
                     }
                 });
             });
        });
        scrolls.forEach(scrollbar -> {
            scrollbar.setDisable(scrollbar != scrolls.lastElement());
            scrollbar.valueProperty()
                     .addListener((ChangeListener<? super Number>) (o, p,
                                                                    c) -> {
                         scrolls.forEach(sibling -> {
                             if (sibling != scrollbar) {
                                 sibling.setValue(c.doubleValue());
                             }
                         });
                     });
        });
    }
}

@@ -1214,45 +1214,8 @@
 de;%0A
-import javafx.scene.control.Control;%0A
 impo
@@ -1389,2600 +1389,167 @@
    p
-ublic static class Nested %7B%0A        public final Control control;%0A        public final double  height;%0A%0A        public Nested(Control control, double height) %7B%0A            this.control = control;%0A            this.height = height;%0A        %7D%0A%0A        @Override%0A        public String toString() %7B%0A            return String.format(%22Nested %5Bheight=%25s, control=%25s%5D%22, height,%0A                                 control);%0A        %7D%0A    %7D%0A%0A    public static class Nesting %7B%0A        private final Nested%5B%5D nested;%0A        private final int      cardinality;%0A%0A        public Nesting(int count, int cardinality) %7B%0A            nested = new Nested%5Bcount%5D;%0A            this.cardinality = cardinality;%0A        %7D%0A%0A        public boolean layout(String id, int column, Nested child) %7B%0A            //            if (nested%5Bcolumn%5D != null%0A            //                && nested%5Bcolumn%5D.control != child.control) %7B%0A            //                System.out.println(String.format(%22%25s%5B%25s%5D already exists%22, id,%0A            //                                                 column));%0A            //            %7D%0A            nested%5Bcolumn%5D = child;%0A            for (Nested n : nested) %7B%0A                if (n == null) %7B%0A                    return false;%0A                %7D%0A            %7D%0A            layout();%0A            return true;%0A        %7D%0A%0A        private void layout() %7B%0A            List%3CNested%3E link = Arrays.asList(nested);%0A            double max = link.stream()%0A                             .mapToDouble(p -%3E p.height)%0A                             .max()%0A                             .orElse(-1);%0A            link.forEach(p1 -%3E %7B%0A                double height = cardinality * max;%0A                if (p1.control instanceof ListView) %7B%0A                    ((ListView%3C?%3E) p1.control).setFixedCellSize(max);%0A                %7D%0A                p1.control.setPrefHeight(height);%0A            %7D);%0A        %7D%0A    %7D%0A%0A    private final Map%3CString, Nesting%3E              layouts = new HashMap%3C%3E(3);%0A    private final Map%3CString, ListView%3CJsonNode%3E%5B%5D%3E links   = new HashMap%3C%3E(3);%0A%0A    @Override%0A    protected void layoutChildren() %7B%0A        super.layoutChildren();%0A    %7D%0A%0A    public void layout(String id, Integer column, Control child,%0A                       int cardinality, double height, int count) %7B%0A        Nesting nesting = layouts.computeIfAbsent(id,%0A                                                  k -%3E new Nesting(count,%0A                                                                   cardinality));%0A        if (nesting.layout(id, column, new Nested(child, height))) %7B%0A            layouts.remove(id);%0A        %7D
+rivate final Map%3CString, ListView%3CJsonNode%3E%5B%5D%3E links   = new HashMap%3C%3E(3);%0A%0A    @Override%0A    protected void layoutChildren() %7B%0A        super.layoutChildren();
 %0A   

clean up cruft