package io.twasyl.jstackfx.ui.cells;

import io.twasyl.jstackfx.beans.ThreadElement;
import javafx.event.EventHandler;
import javafx.scene.Cursor;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import javafx.util.Callback;

import java.util.Iterator;
import java.util.Set;

/**
 * Class responsible of creating cells that lists a collection of {@link ThreadElement}.
 *
 * @author Thierry Wasylczenko
 * @since JStackFX 1.0
 */
public class ThreadListCellFactory implements Callback<TableColumn<ThreadElement, Set<ThreadElement>>, TableCell<ThreadElement, Set<ThreadElement>>> {
    @Override
    public TableCell<ThreadElement, Set<ThreadElement>> call(TableColumn<ThreadElement, Set<ThreadElement>> param) {
        final TableCell<ThreadElement, Set<ThreadElement>> cell = new TableCell<ThreadElement, Set<ThreadElement>>() {
            @Override
            protected void updateItem(Set<ThreadElement> item, boolean empty) {
                super.updateItem(item, empty);

                if (item != null && !item.isEmpty() && !empty) {
                    final TextFlow threadList = buildThreadListGraphic(this, item);

                    if (this.prefHeightProperty().isBound()) {
                        this.prefHeightProperty().unbind();
                    }

                    this.prefHeightProperty().bind(threadList.heightProperty());
                    threadList.prefWidthProperty().bind(this.widthProperty().subtract(-5));

                    this.setGraphic(threadList);
                    this.layout();
                } else {
                    this.setGraphic(null);
                }
            }
        };

        return cell;
    }

    protected TextFlow buildThreadListGraphic(final TableCell<ThreadElement, Set<ThreadElement>> cell, final Set<ThreadElement> threads) {
        final TextFlow threadsGraphic = new TextFlow();
        threadsGraphic.setPrefHeight(20);

        final Iterator<ThreadElement> threadIterator = threads.iterator();
        while (threadIterator.hasNext()) {
            final ThreadElement thread = threadIterator.next();

            threadsGraphic.getChildren().add(buildThreadLink(cell, thread));

            if (threadIterator.hasNext()) {
                threadsGraphic.getChildren().add(buildThreadSeparator());
            }
        }
        return threadsGraphic;
    }

    protected Text buildThreadLink(final TableCell<ThreadElement, Set<ThreadElement>> cell, final ThreadElement thread) {
        final Text threadText = new Text(thread.getName());
        threadText.getStyleClass().add("text");

        threadText.setOnMouseClicked(ThreadListCellFactory.this.buildMouseClickedEventHandler(cell, thread));
        threadText.setOnMouseEntered(event -> threadText.setCursor(Cursor.HAND));

        return threadText;
    }

    protected Text buildThreadSeparator() {
        final Text separator = new Text(", ");
        separator.getStyleClass().add("text");
        return separator;
    }

    protected EventHandler<MouseEvent> buildMouseClickedEventHandler(final TableCell<ThreadElement, Set<ThreadElement>> cell, final ThreadElement thread) {
        final EventHandler<MouseEvent> handler = event -> {
            if (event.getButton().equals(MouseButton.PRIMARY) && event.getClickCount() == 1) {
                cell.getTableView().getSelectionModel().select(thread);
            }
        };

        return handler;
    }
}