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

import be.iminds.ilabt.jfed.experimenter_gui.canvas.rspec.CanvasNodeFactory;
import be.iminds.ilabt.jfed.experimenter_gui.config.JFedGuiConfig;
import be.iminds.ilabt.jfed.experimenter_gui.editor.ModelRspecEditor;
import be.iminds.ilabt.jfed.experimenter_gui.editor.ToolboxItem;
import be.iminds.ilabt.jfed.experimenter_gui.editor.addresspool_properties.AddressPoolPropertiesDialogFactory;
import be.iminds.ilabt.jfed.experimenter_gui.editor.channel_properties.ChannelPropertiesDialogFactory;
import be.iminds.ilabt.jfed.experimenter_gui.editor.impl.AddressPoolToolboxItem;
import be.iminds.ilabt.jfed.experimenter_gui.editor.impl.ChannelToolboxItem;
import be.iminds.ilabt.jfed.experimenter_gui.editor.impl.GeantTestbedToolboxItem;
import be.iminds.ilabt.jfed.experimenter_gui.editor.impl.NodeResourceClassToolboxItem;
import be.iminds.ilabt.jfed.fedmon.webapi.service.json.ResourceClass;
import be.iminds.ilabt.jfed.lowlevel.TestbedInfoSource;
import be.iminds.ilabt.jfed.lowlevel.authority.AuthorityFinder;
import be.iminds.ilabt.jfed.rspec.rspec_source.RequestRspecSource;
import com.google.common.eventbus.EventBus;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.event.EventHandler;
import javafx.scene.control.ScrollPane;
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;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

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

    private static final String EDITABLE_CANVAS_EDITOR_VIEW_FXML = "EditableCanvasEditorView.fxml";

    private final ModelRspecEditor modelRspecEditor;

    private final JFedGuiConfig conf;

    private ToolboxItem draggedItem = null;

    private final EditableExperimentCanvas 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");
                    EditableCanvasEditorView.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();
    };


    EditableCanvasEditorView(ModelRspecEditor modelRspecEditor, JFedGuiConfig conf, CanvasNodeFactory canvasNodeFactory,
                             TestbedInfoSource testbedInfoSource, AuthorityFinder authorityFinder, ChannelPropertiesDialogFactory channelPropertiesDialogFactory,
                             AddressPoolPropertiesDialogFactory addressPoolPropertiesDialogFactory,
                             EventBus eventBus) {

        this.modelRspecEditor = modelRspecEditor;
        this.conf = conf;

        getStylesheets().add(getClass().getResource("ExperimenterEditor.css").toExternalForm());

        this.canvas =
                new EditableExperimentCanvas(this, modelRspecEditor, conf, canvasNodeFactory, testbedInfoSource, authorityFinder,
                        channelPropertiesDialogFactory, addressPoolPropertiesDialogFactory, eventBus);
        this.canvas.statusProperty().addListener(observable -> setStatus(canvas.getStatus()));

        setCenter(canvas);

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

        ScrollPane resourcesScrollPane = new ScrollPane();
        resourcesScrollPane.setContent(resourcesPane);
        resourcesScrollPane.setPrefWidth(210);
        resourcesScrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
        resourcesScrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED);

        TitledPane resourcesTitledPane = new TitledPane();
        resourcesTitledPane.setText("Resources");
        resourcesTitledPane.setContent(resourcesScrollPane);
        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();


        modelRspecEditor.setEditableExperimentCanvas(canvas);
    }

    private void loadToolboxContent() {
        List<ToolboxItem> items = new ArrayList<>();

        for (ResourceClass resourceClass : conf.getResourceClasses()) {
            NodeResourceClassToolboxItem ndti = new NodeResourceClassToolboxItem(resourceClass);
            registerToolboxDragHandlers(ndti);
            if (Objects.equals(ndti.getResourceClass().getVisibleInToolbox(), Boolean.TRUE)) {
                items.add(ndti);
            }
        }

        items.sort((o1, o2) -> {
            int result = o2.getWeight() - o1.getWeight();

            if(result == 0)
                result = o2.hashCode() - o1.hashCode();

            return result;
        });

        items.forEach(resourcesPane.getChildren()::add);

        ChannelToolboxItem channelToolboxItem = new ChannelToolboxItem();
        registerToolboxDragHandlers(channelToolboxItem);
        resourcesPane.getChildren().add(channelToolboxItem);

        GeantTestbedToolboxItem gtsToolboxItem = new GeantTestbedToolboxItem();
        registerToolboxDragHandlers(gtsToolboxItem);
        resourcesPane.getChildren().add(gtsToolboxItem);

        AddressPoolToolboxItem addressPoolToolboxItem = new AddressPoolToolboxItem();
        registerToolboxDragHandlers(addressPoolToolboxItem);
        resourcesPane.getChildren().add(addressPoolToolboxItem);

    }


    private void registerToolboxDragHandlers(final ToolboxItem toolboxItem) {
        toolboxItem.setOnDragDetected(toolboxOnDragDetectedHandler);
    }


    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 RequestRspecSource getRequestRspecSource() {
        return new RequestRspecSource(modelRspecEditor.getModelRspec());
    }

    @Override
    public void dispose() {
        modelRspecEditor.setEditableExperimentCanvas(null);
    }

    public EditableExperimentCanvas getCanvas() {
        return canvas;
    }

    public ModelRspecEditor getModelRspecEditor() {
        return modelRspecEditor;
    }

    public void autoLayout() {
        canvas.autoLayout();
    }
}
