package leigh.elements.javafx.type1;

import javafx.collections.ObservableList;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.SplitPane;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import leigh.elements.ElementsSort;
import leigh.elements.ParentElement;
import leigh.elements.sdk.AppScreenInstance;
import leigh.elements.sdk.type1.Element;
import leigh.elements.sdk.type1.ScreenElement;
import leigh.eo.EO;
import leigh.eo.EOLoop;
import leigh.eo.EOLoopListener;
import leigh.eo.EditEventGenerator;
import leigh.mecha.log.MechaLogger;
import leigh.mecha.log.MechaLoggerFactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;

/**
 * Implementation of {@ScreenElement} for JavaFX.
 * <p>
 * Limitations: The content for this element is expected to consist entirely of
 * {@link leigh.elements.sdk.type1.PanelGroupElement}.
 *
 * @author Alex Leigh
 */
public class ScreenElementJFX extends ParentElement implements ScreenElement, ElementJFX {
    private static final MechaLogger logger = MechaLoggerFactory.getLogger(ScreenElementJFX.class);
    private final EOLoop contentCfg;
    private final HashMap<Integer, ArrayList<Double>> widths = new HashMap<>();
    private final SplitPane splitPane;
    private BorderPane root;
    private String imageUrl = "file:qody-vision-app/vsimg/brand_logo.png";
    private Image image;

    protected ScreenElementJFX(EO cfg, AppScreenInstance factory) {
        super(cfg, factory);
        contentCfg = cfg.getValueLoop(ScreenElement.KEY_CONTENT);

        buildChildren(ScreenElement.KEY_CONTENT);

        root = new BorderPane();

        // This is JavaFX-specific, so we can't push it up to a parent.
        logger.info("Logo is {}", imageUrl);
        if (imageUrl != null) {
            image = new Image(imageUrl);
        }

        // Our superclass should have populated the panes from the configuration, so by this point in the
        // constructor, we can iterate them.

        splitPane = new SplitPane();

        ArrayList<Element> children = ElementsSort.sort(
                getChildren().get(ScreenElement.KEY_CONTENT),
                "uiIndex", Locale.ENGLISH);

        for (Element group : children) {
            logger.info("Adding panel group to content: {}", group.getCfg());
            VBox westNode = (VBox) ((PanelGroupElementJFX) group).getJavaFXNode();
            westNode.setMinWidth(200);

            splitPane.getItems().add(westNode);
            SplitPane.setResizableWithParent(splitPane.getItems().get(0), false);
        }

        // Setup the initial JFX state.
        root.setTop(buildHeader());
        root.setCenter(splitPane);

        EditEventGenerator peg = (EditEventGenerator) contentCfg.getEditTracker();

        // FIXME: Splitpane positions should be retained

        peg.addLoopListener(contentCfg, new EOLoopListener() {
            @Override
            public void removed(EOLoop loop, EO obj, int index) {
                logger.info("Panel content removing. [index: {}] [paneSz: {}] [obj: {}]",
                        index, splitPane.getItems().size(), obj);

                // obj has been removed from the content. Remove the corresponding JavaFX.

                savePositions();
                ObservableList<Node> panes = splitPane.getItems();
                // FIXME - This sometimes calls with index=2 to=1 which throws a IAE
                panes.subList(index, panes.size()).clear();
                restorePositions();
            }

            @Override
            public void added(EOLoop loop, EO obj) {
                logger.info("Panel content adding: {}", obj);

                // Index of the panel being added
                int idx = loop.indexOf(obj);
                ObservableList<Node> panes = splitPane.getItems();
                panes.subList(idx, panes.size()).clear();
                Element group = factory.getFactory().build(obj, factory);
                VBox child = (VBox) ((PanelGroupElementJFX) group).getJavaFXNode();
                child.setMinWidth(200);
                //   child.setMinWidth(Region.USE_PREF_SIZE);
                //   child.setMinWidth(400);
                SplitPane.setResizableWithParent(child, false);
                savePositions();

                logger.info("New child. [minSize: {}]", child.getMinWidth());

                panes.add(child);

                restorePositions();
            }
        });

    }

    /**
     * Save the current positions in the splitPane into the positions list. If there were more positions in the stored
     * list than there are dividers presently, the old positions are retained. This allows the user preference
     * to survive times when the split pane has fewer dividers.
     */
    private void savePositions() {
        List<Node> nodes = splitPane.getItems();
        int sz = nodes.size();

        ArrayList<Double> w = widths.get(sz);
        if (w == null) {
            w = new ArrayList<>();
            widths.put(sz, w);
        } else {
            w.clear();
        }

        // TODO Why is Collections.addAll() not working here?
        for (Double d : splitPane.getDividerPositions()) {
            w.add(d);
        }
    }

    public void restorePositions() {
        List<Node> nodes = splitPane.getItems();
        int sz = nodes.size();
        ArrayList<Double> w = widths.get(sz);
        logger.info("restorePositions() called. [w: {}]", w);

        if (w != null) {
            logger.info("Restoring positions. [sz: {}]", sz);
            double pos[] = new double[w.size()];
            for (int i = 0; i < w.size(); i++) {
                pos[i] = w.get(i);
            }
            splitPane.setDividerPositions(pos);
        } else {
            ObservableList<SplitPane.Divider> dividers = splitPane.getDividers();
            double each = 1.0 / (dividers.size() + 1);
            for (int i = 0; i < dividers.size(); i++) {
                double myPos = (i + 1) * each;
                dividers.get(i).setPosition(myPos);
            }
        }
    }

    private Pane buildHeader() {
        BorderPane header = new BorderPane();
        header.getStyleClass().add("leigh-elements-screen-header");

        if (image != null) {
            ImageView logoView = new ImageView(image);
            logoView.setPreserveRatio(true);
            logoView.setFitHeight(35);
            header.setRight(logoView);
        }

        Label title = new Label(getCfg().getValueString(ScreenElement.KEY_LABEL));
        title.getStyleClass().add("leigh-elements-screen-title");
        header.setLeft(title);
        BorderPane.setAlignment(title, Pos.CENTER);

        return header;
    }

    public String getImageUrl() {
        return imageUrl;
    }

    public Node getJavaFXNode() {
        return root;
    }
}
