package be.iminds.ilabt.jfed.experimenter_gui.slice.scp;

import be.iminds.ilabt.jfed.ui.javafx.GlyphUtils;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.concurrent.Worker;
import javafx.scene.control.*;
import javafx.scene.layout.HBox;
import org.controlsfx.glyphfont.FontAwesome;

import java.text.DecimalFormat;

/**
 * User: twalcari
 * Date: 8/25/2016
 * Time: 10:15
 */
public class SCPTab extends Tab {

    private final TableView<SCPTask> tableView;

    public SCPTab() {
        setText("SCP");
        setGraphic(GlyphUtils.createGlyph(FontAwesome.Glyph.UPLOAD));

        this.tableView = new TableView<>();

        {
            final TableColumn<SCPTask, String> actionTypeTableColumn = new TableColumn<>("Operation");
            //actionTypeTableColumn.setPrefWidth(75);
            actionTypeTableColumn.setMaxWidth(1f * Integer.MAX_VALUE * 10);
            actionTypeTableColumn.setCellValueFactory(param -> {
                if (param.getValue() instanceof SCPUploadTask)
                    return new ReadOnlyStringWrapper("Upload");
                else if (param.getValue() instanceof SCPDownloadTask)
                    return new ReadOnlyStringWrapper("Download");
                else
                    return new ReadOnlyStringWrapper("");
            });
            tableView.getColumns().add(actionTypeTableColumn);
        }

        {
            final TableColumn<SCPTask, String> localTableColumn = new TableColumn<>("Local");
            //localTableColumn.setPrefWidth(275);
            localTableColumn.setMaxWidth(1f * Integer.MAX_VALUE * 30);
            localTableColumn.setCellValueFactory(param -> new ReadOnlyStringWrapper(param.getValue().getLocalPath()));
            tableView.getColumns().add(localTableColumn);
        }
        {
            final TableColumn<SCPTask, String> remoteTableColumn = new TableColumn<>("Remote");
            //remoteTableColumn.setPrefWidth(275);
            remoteTableColumn.setMaxWidth(1f * Integer.MAX_VALUE * 30);
            remoteTableColumn.setCellValueFactory(param -> new ReadOnlyStringWrapper(param.getValue().getRemotePath()));
            tableView.getColumns().add(remoteTableColumn);
        }
        {
            final TableColumn<SCPTask, Long> sizeTableColumn = new TableColumn<>("Size");
            //sizeTableColumn.setPrefWidth(100);
            sizeTableColumn.setMaxWidth(1f * Integer.MAX_VALUE * 13);
            sizeTableColumn.setCellValueFactory(param -> param.getValue().sizeProperty().asObject());
            sizeTableColumn.setCellFactory(param -> new FilesizeTableCell());
            tableView.getColumns().add(sizeTableColumn);
        }
        {
            final TableColumn<SCPTask, SCPTask> progressTableColumn = new TableColumn<>("Status");
            progressTableColumn.setMinWidth(75);
            //progressTableColumn.setPrefWidth(100);
            progressTableColumn.setMaxWidth(1f * Integer.MAX_VALUE * 15);
            progressTableColumn.setCellValueFactory(param -> new ReadOnlyObjectWrapper<>(param.getValue()));
            progressTableColumn.setCellFactory(param -> new StatusTableCell());
            tableView.getColumns().add(progressTableColumn);
        }

        tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
        setContent(tableView);

        setClosable(false);
    }

    public void registerSCPTask(SCPTask task) {
        tableView.getItems().add(task);
    }

    private static class FilesizeTableCell extends TableCell<SCPTask, Long> {

        protected static final String[] UNITS = new String[]{"B", "kB", "MB", "GB", "TB", "EB"};

        @Override
        protected void updateItem(Long item, boolean empty) {
            super.updateItem(item, empty);
            if (!empty) {
                setText(readableFileSize(item));
            } else {
                setText("");
            }
        }

        public static String readableFileSize(long size) {
            if (size <= 0) return "0";
            int digitGroups = (int) (Math.log10(size) / Math.log10(1024));
            return new DecimalFormat("#,##0.##").format(size / Math.pow(1024, digitGroups)) + " " + UNITS[digitGroups];
        }
    }

    private static class StatusTableCell extends TableCell<SCPTask, SCPTask> {

        private final HBox content = new HBox(5);
        private final ProgressBar progressBar = new ProgressBar();
        private final Button cancelButton = new Button("Cancel");
        private final Label resultLabel = new Label();

        {
            progressBar.managedProperty().bind(progressBar.visibleProperty());
            cancelButton.managedProperty().bind(cancelButton.visibleProperty());
            resultLabel.managedProperty().bind(resultLabel.visibleProperty());
            content.getChildren().addAll(progressBar, cancelButton, resultLabel);

            cancelButton.setOnAction(event -> {
                ((SCPTask) getTableRow().getItem()).cancel(true);
                ((SCPTask) getTableRow().getItem()).stateProperty().isEqualTo(Worker.State.CANCELLED);
            });
        }

        @Override
        public void updateItem(SCPTask item, boolean empty) {
            super.updateItem(item, empty);

            if (empty) {
                setGraphic(null);
            } else {
                progressBar.progressProperty().unbind();
                progressBar.progressProperty().bind(item.progressProperty());

                progressBar.visibleProperty().unbind();
                progressBar.visibleProperty().bind(item.runningProperty());

                cancelButton.visibleProperty().unbind();
                cancelButton.visibleProperty().bind(item.runningProperty());

                cancelButton.disableProperty().unbind();
                cancelButton.disableProperty().bind(item.stateProperty().isNotEqualTo(Worker.State.RUNNING));

                resultLabel.visibleProperty().unbind();
                resultLabel.visibleProperty().bind(item.runningProperty().not());

                resultLabel.textProperty().unbind();
                resultLabel.textProperty().bind(Bindings.createStringBinding(() -> {
                    switch (item.getState()) {
                        case SUCCEEDED:
                            return "Success";
                        case CANCELLED:
                            return "Cancelled";
                        case FAILED:
                            return "Failed";
                        case READY:
                        case SCHEDULED:
                            return "Pending";
                    }

                    return "Unknown";

                }, item.stateProperty()));

                setGraphic(content);
            }
        }
    }
}
