package be.iminds.ilabt.jfed.experimenter_gui.gts_editor.views;

import be.iminds.ilabt.jfed.experimenter_gui.config.JFedGuiConfig;
import be.iminds.ilabt.jfed.experimenter_gui.canvas.rspec.CanvasNodeFactory;
import be.iminds.ilabt.jfed.experimenter_gui.editor.ToolboxItem;
import be.iminds.ilabt.jfed.experimenter_gui.editor.views.DraggedItemProvider;
import be.iminds.ilabt.jfed.experimenter_gui.editor.views.EditableCanvasEditorView;
import be.iminds.ilabt.jfed.experimenter_gui.editor.views.EditorViewType;
import be.iminds.ilabt.jfed.experimenter_gui.gts_editor.GTSModelEditor;
import be.iminds.ilabt.jfed.experimenter_gui.gts_editor.toolbox.GTSExternalNetworkToolboxItem;
import be.iminds.ilabt.jfed.experimenter_gui.gts_editor.toolbox.GTSHostToolboxItem;
import be.iminds.ilabt.jfed.experimenter_gui.gts_editor.toolbox.GTSOFXToolboxItem;
import be.iminds.ilabt.jfed.experimenter_gui.gts_editor.toolbox.GTSPortToolboxItem;
import be.iminds.ilabt.jfed.groovy_dsl.DSLStreamException;
import be.iminds.ilabt.jfed.gts.GtsDslWriter;

import be.iminds.ilabt.jfed.lowlevel.TestbedInfoSource;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.ListChangeListener;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.control.TitledPane;
import javafx.scene.input.DragEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Region;
import javafx.scene.layout.TilePane;
import javafx.scene.layout.VBox;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * User: twalcari
 * Date: 5/19/2015
 * Time: 16:39
 */
public class EditableGTSCanvasEditorView extends BorderPane implements EditableGtsDslView, DraggedItemProvider {
    private static final Logger LOG = LoggerFactory.getLogger(EditableGTSCanvasEditorView.class);

    private final GTSModelEditor modelEditor;
    private ToolboxItem draggedItem = null;

    private final EditableGTSCanvas canvas;
    private final VBox toolbox;
    private final TilePane resourcesPane;

    private final StringProperty statusProperty = new SimpleStringProperty();

    //TODO find a way to get this handler to work
    private final EventHandler<DragEvent> onMouseDragReleasedHandlerOutsideCanvas =
            mouseDragEvent -> {
                if (draggedItem != null) {
                    draggedItem = null;
                    setStatus("Item can only be dropped on the canvas");
                    EditableGTSCanvasEditorView.this.setCursor(null);

                    mouseDragEvent.consume();
                }
            };

    private final EventHandler<MouseEvent> toolboxOnDragDetectedHandler = mouseEvent -> {
        //set selected toolboxitem as draggedItem
        ToolboxItem originalItem = (ToolboxItem) mouseEvent.getSource();
        draggedItem = originalItem;

        setStatus("Drop the item somewhere in the canvas.");

        //make JavaFX fire drag-events
        originalItem.startFullDrag();

        mouseEvent.consume();

    };


    EditableGTSCanvasEditorView(GTSModelEditor modelEditor, JFedGuiConfig config, CanvasNodeFactory canvasNodeFactory,
                                TestbedInfoSource testbedInfoSource) {
        this.modelEditor = modelEditor;

        getStylesheets().add(EditableCanvasEditorView.class.getResource("ExperimenterEditor.css").toExternalForm());

        this.canvas = new EditableGTSCanvas(modelEditor, config, canvasNodeFactory, this, testbedInfoSource);

        this.canvas.statusProperty().addListener(observable -> setStatus(canvas.getStatus()));
        setCenter(canvas);

        this.resourcesPane = new TilePane();
        resourcesPane.getStyleClass().add("toolbox-pane");
        resourcesPane.setPrefWidth(200);

        //automatically register drag handlers on all items in the resources pane
        resourcesPane.getChildren().addListener((ListChangeListener<Node>) c -> {
            while (c.next()) {
                if (c.wasAdded()) {
                    for (Node n : c.getAddedSubList()) {
                        n.setOnDragDetected(toolboxOnDragDetectedHandler);
                    }
                }
            }
        });


        TitledPane resourcesTitledPane = new TitledPane();
        resourcesTitledPane.setText("Resources");
        resourcesTitledPane.setContent(resourcesPane);
        resourcesTitledPane.setCollapsible(false);

        this.toolbox = new VBox();
        toolbox.getChildren().add(resourcesTitledPane);


        resourcesPane.prefHeightProperty().bind(toolbox.heightProperty());

        setLeft(toolbox);

        //initialize event handlers
        setOnDragDone(onMouseDragReleasedHandlerOutsideCanvas);

        //load toolbox content
        loadToolboxContent();
    }

    private void loadToolboxContent() {
        resourcesPane.getChildren().addAll(new GTSHostToolboxItem(), new GTSPortToolboxItem(),
                new GTSOFXToolboxItem(), new GTSExternalNetworkToolboxItem());
    }


    public void setStatus(String status) {
        statusProperty.set(status);
    }

    public StringProperty statusProperty() {
        return statusProperty;
    }

    @Override
    public ToolboxItem getDraggedItem() {
        return draggedItem;
    }

    @Override
    public void clearDraggedItem() {
        draggedItem = null;
    }

    @Override
    public Region getView() {
        return this;
    }

    @Override
    public EditorViewType getType() {
        return EditorViewType.CANVAS;
    }

    @Override
    public String getResultDsl() {
        GtsDslWriter writer = new GtsDslWriter(modelEditor.getModel());

        try {
            return writer.call();
        } catch (DSLStreamException e) {
            LOG.error("Error while writing GTS DSL", e);
            throw new RuntimeException("Error while writing GTS DSL",e);
        }
    }


    @Override
    public void dispose() {

    }


    public EditableGTSCanvas getCanvas() {
        return canvas;
    }

    public GTSModelEditor getModelEditor() {
        return modelEditor;
    }
}
