package control;

import imp.*;
import graph.*;

import javafx.fxml.*;
import javafx.scene.control.*;
import javafx.event.*;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ContextMenuEvent;
import javafx.scene.layout.*;
import javafx.scene.Node;
import javafx.scene.text.*;
import javafx.geometry.Pos;
import javafx.collections.FXCollections;
import javafx.scene.control.Alert.AlertType;

import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Optional;
import javafx.collections.ObservableList;

/**
 * Die Klasse UnterTabMitController stellt einen Tab inclusive ihres Controllers
 * zur händischen Erforschung eines Algorithmus. Es wird immer ein einzelner Knoten 
 * oder eine Kante fokussiert.
 *
 * @author Thomas Schaller
 * @version 24.06.2021 (v6.9)
 * v6.9: Context-Menü für die ToDo-Liste: Knoten löschen
 */

public class UnterTabMitController extends TabMitController {

    @FXML
    private VBox infoBox;

    @FXML
    private Button wertButton;

    @FXML
    private Button besuchtButton;

    @FXML
    private Label knotenname;

    @FXML
    private Label knotenwert;

    @FXML
    private Label knotenMarkiert;

    @FXML
    private Label knotenBesucht;

    @FXML
    private GridPane farben;

    @FXML
    Button bStatus;

    @FXML
    Button bGehezu;

    @FXML
    Button bAnfang;

    @FXML
    Button bEnde;

    @FXML
    Button bSortieren;

    @FXML
    private CheckBox cbFarbeAutomatisch;

    @FXML
    private ListView<String> lvAuswahl;

    private List<GraphElement> auswahl;
    private List<String> sicherung;

    public UnterTabMitController(Graph graph, GraphOptions options) {
        this.graph = graph;
        this.options = options;

    }

    public void initialize() {
        if(graph.getAnzahlKnoten()==0) {
            getTabPane().getTabs().remove(this);
            return;
        }

        buildAuswahl();

        this.bAnfang.managedProperty().bind(bAnfang.visibleProperty());
        this.bEnde.managedProperty().bind(bEnde.visibleProperty());
        this.bSortieren.managedProperty().bind(bSortieren.visibleProperty());

        viewer.setGraph(graph,options);
        viewer.setHvalue(0.5);
        viewer.setVvalue(0.5);
        //auswahl = viewer.getGraph().getAlleKnoten();

        for(int x = 0; x< 4; x++) {
            for(int y = 0; y <3; y++) {
                Label l = new Label();
                l.setPrefSize(20,20);
                l.setMaxSize(200, 200);
                if(options.farbenKnoten.length > y*4+x) {
                    l.setStyle("-fx-background-color:#"+options.farbenKnoten[y*4+x]);
                    l.setAlignment(Pos.CENTER);
                    l.setFont(Font.font("System", FontWeight.BOLD, 12));
                }
                else
                    l.setStyle("-fx-background-color:#FFFFFF");

                l.setOnMouseClicked(e -> knotenFarbe(e));
                farben.add(l, x, y);
            }
        }

        fillLvAuswahl();
        lvAuswahl.getSelectionModel().selectedItemProperty().addListener(
            (obs, oldValue, newValue) -> showAuswahl());

        cbFarbeAutomatisch.selectedProperty().addListener((o, oldValue, newValue) -> {
                Knoten k = viewer.getSelectedKnoten();
                if (k!=null) {            
                    k.setFarbeAutomatisch(newValue);
                    viewer.updateImage();
                    updateInfofeld();   
                }
            });

        super.initialize();
    }

    public void setGraph(Graph graph, GraphOptions options) {
        if(graph.getAnzahlKnoten()==0) {
            getTabPane().getTabs().remove(this);
            return;
        }
        options.fokusArt = this.options.fokusArt;
        options.auswahl = this.options.auswahl;
        options.bildAnzeigen = false;
        if(options.fokusArt == 0 && this.options.parent!= null) options.parent = graph.getKnoten(0);
        if(options.fokusArt == 1 && this.options.parent!= null) options.parent = graph.getKante(0,1);
        this.graph = graph;
        this.options = options;
        viewer.setRestrictTo(null);
        buildAuswahl();
        super.setGraph(graph,options);

    }

    public void buildAuswahl() {
        auswahl = new ArrayList<GraphElement>();
        if(options.auswahl == 0) {  // Alle Knoten/Kanten gewählt
            if(options.fokusArt == 0) // Knoten
                auswahl = new ArrayList<GraphElement>(graph.getAlleKnoten());
            else 
                auswahl = new ArrayList<GraphElement>(graph.getAlleKanten());
        } else {
            if(options.auswahl == 1) { // Nachbarn
                auswahl = new ArrayList<GraphElement>(graph.getNachbarknoten(((Knoten) (options.parent))));
            } else { // single
                auswahl.add(options.parent);
            }
        } 
        if(auswahl.size() == 0) getTabPane().getTabs().remove(this);
    }

    public void update() {
        super.update();
        updateInfofeld();
        fillLvAuswahl();
    }

    private void fillLvAuswahl() {
        if(auswahl.size()==0 ) {
            getTabPane().getTabs().remove(this);
        } else {
            ObservableList<String> items = FXCollections.observableArrayList ();
            for(GraphElement ge : auswahl) {
                if(ge instanceof Knoten) {
                    Knoten k = (Knoten) ge;
                    String beschreibung="";
                    if(options.showVertexInfo && !k.getInfotext().isEmpty())
                        beschreibung = k.getInfotext();
                    else 
                        beschreibung = "Knoten"+ graph.getNummer(k);

                    if(options.showVertexValue) {
                        if(k.getDoubleWert() == k.getIntWert()) 
                            beschreibung += "  ("+k.getIntWert()+")";
                        else
                            beschreibung += "  ("+k.getDoubleWert()+")";
                    }
                    items.add(beschreibung);
                }
                if(ge instanceof Kante) {
                    Kante k = (Kante) ge;
                    if(options.showEdgeWeights) {
                        if(k.getGewicht() == (int) k.getGewicht()) 
                            items.add("Kante ("+((int)k.getGewicht())+")");
                        else
                            items.add("Kante ("+k.getGewicht()+")");
                    }
                    else
                        items.add("Kante Nr."+graph.getNummer(k));
                }

            }
            lvAuswahl.setItems(items);
            showAuswahl();
        }
    }

    private void showAuswahl() {
        if(lvAuswahl.getSelectionModel().getSelectedIndex()==-1) {
            if(auswahl.contains(viewer.getRestrictTo())) {
                lvAuswahl.getSelectionModel().select(auswahl.indexOf(viewer.getRestrictTo()));
            } else {
                lvAuswahl.getSelectionModel().selectFirst();
            }
        }
        viewer.setRestrictTo(auswahl.get(lvAuswahl.getSelectionModel().getSelectedIndex()));
        updateInfofeld();
    }

    public void setAuswahl(List<GraphElement> auswahl) {
        this.auswahl = auswahl;
        fillLvAuswahl();
    }

    @FXML
    void bAnfang(ActionEvent event) {
        if(lvAuswahl.getSelectionModel().getSelectedIndex() > 0) {

            lvAuswahl.getSelectionModel().selectFirst();
            showAuswahl();
        }
    }

    @FXML
    void bBesucht(ActionEvent event) {
        Knoten k = viewer.getSelectedKnoten();
        if (k!=null) {
            k.setBesucht(!k.isBesucht());
            viewer.updateImage();
            updateInfofeld();        
        }else {
            Kante ka = viewer.getSelectedKante();
            if(ka!=null) {
                ka.setGeloescht(!ka.isGeloescht());
                viewer.updateImage();
                updateInfofeld();
            }
        }
    }

    @FXML
    void bEnde(ActionEvent event) {
        if(lvAuswahl.getSelectionModel().getSelectedIndex() < lvAuswahl.getItems().size()-1) {
            lvAuswahl.getSelectionModel().selectLast();
            showAuswahl();
        }
    }

    @FXML
    void knotenFarbe(MouseEvent event) {

        Node source = (Node) event.getSource();
        Integer colIndex = farben.getColumnIndex(source);
        Integer rowIndex = farben.getRowIndex(source);
        Knoten k = viewer.getSelectedKnoten();
        if (k!=null) {
            k.setFarbe(rowIndex*4+colIndex);
            ObservableList<Node> ln = farben.getChildren();
            for(Node n : ln) {
                if(n instanceof Label)
                    if(n == source){
                        ((Label) n).setText("X");
                    }
                    else
                        ((Label) n).setText(" ");
            } 
            viewer.updateImage();
            updateInfofeld();        
        }

    }

    @FXML
    void bMarkieren(ActionEvent event) {
        Knoten k = viewer.getSelectedKnoten();
        if (k!=null) {
            k.setMarkiert(!k.isMarkiert());
            viewer.updateImage();
            updateInfofeld();        
        } else {
            Kante ka = viewer.getSelectedKante();
            if(ka!=null) {
                ka.setMarkiert(!ka.isMarkiert());
                viewer.updateImage();
                updateInfofeld();
            }
        }

    }

    public void updateInfofeld() {
        GraphElement f = viewer.getRestrictTo();
        if( f instanceof Knoten) {
            this.setText(viewer.getGraph().getKnoteninfo((Knoten) f, false));
        }
        Knoten k = viewer.getSelectedKnoten();
        Kante ka = viewer.getSelectedKante();

        if(k!=null) {
            infoBox.setVisible(true);
            knotenname.setText("Knoten: "+viewer.getGraph().getNummer(k));
            knotenwert.setText("Wert: "+k.getDoubleWert());
            knotenMarkiert.setText(k.isMarkiert() ? "markiert" : "unmarkiert");
            knotenBesucht.setText(k.isBesucht() ? "besucht" : "nicht besucht");
            besuchtButton.setText("Besuchen");
            wertButton.setVisible(true);
            farben.setVisible(true);
            bGehezu.setVisible(k != viewer.getRestrictTo());
            bAnfang.setVisible(k != viewer.getRestrictTo());
            bEnde.setVisible(k != viewer.getRestrictTo());
            bSortieren.setVisible((options.showEdgeWeights && auswahl.get(0) instanceof Kante) || (options.showVertexValue && auswahl.get(0) instanceof Knoten));
            cbFarbeAutomatisch.setSelected(k.isFarbeAutomatisch());
            ObservableList<Node> ln = farben.getChildren();
            for(Node n : ln) {
                if(n instanceof Label){
                    Integer colIndex = farben.getColumnIndex(n);
                    Integer rowIndex = farben.getRowIndex(n);
                    if(rowIndex*4+colIndex == k.getFarbe()){
                        ((Label) n).setText("X");
                    }
                    else
                        ((Label) n).setText(" ");
                } 

            }
        } else {
            if(ka != null) {
                infoBox.setVisible(true);
                knotenname.setText("Kante:");
                knotenwert.setText("Gewicht: "+ka.getGewicht());
                knotenMarkiert.setText(ka.isMarkiert() ? "markiert" : "unmarkiert");
                knotenBesucht.setText(ka.isGeloescht() ? "gelöscht" : "nicht gelöscht");
                besuchtButton.setText("Löschen");
                wertButton.setVisible(false);
                farben.setVisible(false);
            }
            else
                infoBox.setVisible(false);
        }

    }

    @FXML
    void graphClicked(MouseEvent event) {
        viewer.mouseClicked(event);
        updateInfofeld();        
        if(viewer.getSelectedKnoten() != null && event.isSecondaryButtonDown()) { // Contextmenu
            ContextMenu contextMenu = new ContextMenu();
            CheckMenuItem item1 = new CheckMenuItem("Markiert");
            item1.setSelected(viewer.getSelectedKnoten().isMarkiert());
            item1.setOnAction((e) -> this.bMarkieren(e));
            CheckMenuItem item2 = new CheckMenuItem("Besucht");
            item2.setOnAction((e) -> this.bBesucht(e));
            item2.setSelected(viewer.getSelectedKnoten().isBesucht());
            MenuItem item3 = new MenuItem("Wert ändern");
            item3.setOnAction((e) -> this.bWertAendern(e));
            MenuItem item5 = new MenuItem("Füge am Anfang der ToDo-Liste hinzu");
            item5.setOnAction((e) -> this.bHinzufuegenAnfang(e));
            MenuItem item6 = new MenuItem("Füge am Ende der ToDo-Liste hinzu");
            item6.setOnAction((e) -> this.bHinzufuegenEnde(e));
            MenuItem item7 = new MenuItem("Loesche aus Liste");
            item7.setOnAction((e) -> this.bLoeschenAusListe(e));
            SeparatorMenuItem sep = new SeparatorMenuItem();
            SeparatorMenuItem sep2 = new SeparatorMenuItem();
            MenuItem item4 = new MenuItem("Gehe zu ...");
            item4.setOnAction((e) -> this.bGeheZu(e));

            // Add MenuItem to ContextMenu
            contextMenu.getItems().addAll(item3, item1, item2, sep, item5, item6, item7, sep2, item4);
            contextMenu.show(viewer, event.getScreenX(), event.getScreenY());
        }
        if(viewer.getSelectedKante() != null && event.isSecondaryButtonDown()) { // Contextmenu
            ContextMenu contextMenu = new ContextMenu();
            CheckMenuItem item1 = new CheckMenuItem("Markiert");
            item1.setSelected(viewer.getSelectedKante().isMarkiert());
            item1.setOnAction((e) -> this.bMarkieren(e));
            CheckMenuItem item2 = new CheckMenuItem("Gelöscht");
            item2.setSelected(viewer.getSelectedKante().isGeloescht());
            item2.setOnAction((e) -> this.bBesucht(e)); 
            SeparatorMenuItem sep = new SeparatorMenuItem();
            MenuItem item4 = new MenuItem("Gehe zu ...");
            item4.setOnAction((e) -> this.bGeheZu(e));

            // Add MenuItem to ContextMenu
            contextMenu.getItems().addAll( item1, item2, sep, item4);
            contextMenu.show(viewer, event.getScreenX(), event.getScreenY());
        }
    }

    @FXML
    void bHinzufuegenAnfang(ActionEvent event) {
        Knoten k = viewer.getSelectedKnoten();
        if(k!=null) {
            auswahl.add(0,k);
            fillLvAuswahl();
        }
    }

    @FXML
    void bHinzufuegenEnde(ActionEvent event) {
        Knoten k = viewer.getSelectedKnoten();
        if(k!=null) {
            auswahl.add(k);
            fillLvAuswahl();
        }
    }

    @FXML
    void bLoeschenAusListe(ActionEvent event) {
        Knoten k = viewer.getSelectedKnoten();
        if(k!=null && auswahl.contains(k)) {
            auswahl.remove(k);
            fillLvAuswahl();
        }
    }    

    @FXML
    void toDoContextMenu(ContextMenuEvent event) {
        if(lvAuswahl.getSelectionModel().getSelectedIndex() >= 0) {

            Alert alert = 
                new Alert(AlertType.NONE, 
                    "Soll der Knoten aus der ToDo-Liste gelöscht werden?",
                    ButtonType.OK, 
                    ButtonType.CANCEL);
            alert.setTitle("ToDo-Liste");
            Optional<ButtonType> result = alert.showAndWait();

            if (result.get() == ButtonType.OK) {
                auswahl.remove(lvAuswahl.getSelectionModel().getSelectedIndex());
                fillLvAuswahl();
            }
        }
    }

    @FXML
    void bNaechster(ActionEvent event) {

        if(lvAuswahl.getSelectionModel().isSelected(auswahl.size()-1))
            lvAuswahl.getSelectionModel().selectFirst();
        else
            lvAuswahl.getSelectionModel().selectNext();
        showAuswahl();
    }

    // @FXML
    // void bListeAnpassen(ActionEvent event) {
    // buildAuswahl();
    // fillLvAuswahl();

    // }

    // @FXML
    // void bNeuerTab(ActionEvent event) {
    // GraphOptions neu = options.copy();
    // neu.parent = viewer.getRestrictTo();
    // if(neu.parent == null) neu.parent = viewer.getSelectedKnoten();
    // neu.bildAnzeigen = false;
    // tabOeffnen(neu);
    // }

    @FXML
    void bSort(ActionEvent event) {
        Collections.sort(auswahl);
        fillLvAuswahl();
    }

    @FXML
    void bVoheriger(ActionEvent event) {
        if(lvAuswahl.getSelectionModel().isSelected(0))
            lvAuswahl.getSelectionModel().selectLast();
        else
            lvAuswahl.getSelectionModel().selectPrevious();

        showAuswahl();
    }

    @FXML
    void bWertAendern(ActionEvent event) {
        Knoten k = viewer.getSelectedKnoten();
        if (k!=null) {
            TextInputDialog dialog = new TextInputDialog(""+k.getDoubleWert());
            dialog.setTitle("Wert ändern");
            dialog.setContentText("Bitte geben Sie den neuen Wert ein:");

            Optional<String> result = dialog.showAndWait();
            if (result.isPresent()){
                try{
                    double v = Double.parseDouble(result.get());
                    k.setWert(v);          
                    this.fillLvAuswahl();

                    viewer.updateImage();
                    updateInfofeld();        
                } catch (Exception e) {
                    System.out.println("Keine Zahl eingegeben");
                }
            }

        } 
    }

    @FXML
    void bGeheZu(ActionEvent event) {
        Knoten k = viewer.getSelectedKnoten();
        if (k!=null) {
            GraphOptions neu = options.copy();
            neu.auswahl = 2; // single
            neu.parent = k;
            neu.bildAnzeigen = false;
            this.tabOeffnen(neu);
        }
    }

    @FXML
    void mBeenden(ActionEvent event) {

    }

    @FXML
    void mBesucheKnoten(ActionEvent event) {

    }

    @FXML
    void mBesuchtLoeschen(ActionEvent event) {

    }

    @FXML
    void mFaerbeKnoten(ActionEvent event) {

    }

    @FXML
    void mMarkiereKnoten(ActionEvent event) {

    }

    @FXML
    void mMarkierungenLoeschen(ActionEvent event) {

    }

    @FXML
    void mOeffnen(ActionEvent event) {

    }

    @FXML
    void mSchliessen(ActionEvent event) {

    }

    @FXML
    void mSpeichern(ActionEvent event) {

    }

    @FXML
    void mUeber(ActionEvent event) {

    }

    @FXML
    void mWertSetzen(ActionEvent event) {

    }

    @FXML
    void mZurueck(ActionEvent event) {

    }

    @FXML
    void bStatusRestore(ActionEvent event) {
        if(sicherung != null) { graph.setStatus(sicherung); update(); }
    }

    @FXML
    void bStatusSave(ActionEvent event) {
        sicherung = graph.getStatus();
        bStatus.setDisable(false);
    }
}
