package control;

import imp.*;
import graph.*;
import algorithmen.*;
import java.io.FileReader;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.FileWriter;

import javafx.fxml.*;
import javafx.scene.control.*;
import javafx.scene.control.Alert.AlertType;
import javafx.event.*;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.scene.Node;
import javafx.scene.text.*;
import javafx.geometry.Pos;
import javafx.stage.*;          // Dateiöffnen / Speichern-Dialog
import java.io.File;
import java.nio.file.*; 
import javafx.stage.FileChooser.ExtensionFilter;
import javafx.scene.image.Image;
import javafx.geometry.Rectangle2D;

import java.util.List;
import java.util.ArrayList;
import javafx.collections.ObservableList;
/**
 * Die Klasse Controller stellt den Controller des Hauptfensters / Menu dar.
 *
 * @author Thomas Schaller
 * @version 03.03.2023 (v7.1)
 * v7.0: Die aktuelle Bildschirmposition und der angezeigte Graph werden in config.csv abgelegt.
 * v7.1: Verzeichnisauswahl für Laden/Speichern verbessert
 */

public class Controller {
    private String version = "7.0 (Februar 2023)";
    private String pfad;  // Pfad der aktuell angezeigten Datei

    @FXML
    private TabPane tpRekursionen;

    @FXML
    private CheckMenuItem mOptionKnotenwerte;

    @FXML
    private CheckMenuItem mOptionKnotenname;

    @FXML
    private CheckMenuItem mOptionKantengewichte;

    @FXML
    private CheckMenuItem mOptionKnoteninfo;

    @FXML
    private CheckMenuItem mOptionBild;

    @FXML
    private CheckMenuItem mHilfefenster;

    @FXML
    private MenuItem mmSpeichern;

    @FXML
    private Menu mmBearbeiten;

    @FXML
    private Menu mmExperimentieren;

    @FXML
    private Menu mmSimulieren;

    @FXML
    private Menu mmZuruecksetzen;

    @FXML
    private Menu mmAnsicht;

    private FileChooser dateidialog;
    private Graph graph;
    private GraphOptions options;
    private Stage stage;

    public void initialize() {

        dateidialog = new FileChooser();
        dateidialog.setInitialDirectory(new File("beispielgraphen"));

        oeffneHauptTab();
        mNeuerGraph(null);

        tpRekursionen.getSelectionModel().selectedItemProperty().
        addListener((value, tabOld, tabNew) -> changeTab(tabOld, tabNew));

        BufferedReader in =null;
        try{
            in = new BufferedReader(new FileReader("config.csv"));
            String fullScreen = in.readLine();
            String posSize = in.readLine();
            String[] ps = posSize.split(",");
            Rectangle2D ss = Screen.getPrimary().getBounds();
            
            stage.setX(Double.parseDouble(ps[0]));
            stage.setY(Double.parseDouble(ps[1]));
            stage.setWidth(Math.min(Double.parseDouble(ps[2]), ss.getWidth()-Double.parseDouble(ps[0])));
            stage.setHeight(Math.min(Double.parseDouble(ps[3]), ss.getHeight()-Double.parseDouble(ps[1])));
            String[] fs = fullScreen.split(",");
            if(fs[0].equals("true")) stage.setFullScreen(true);
            if(fs[1].equals("true")) stage.setMaximized(true);
            pfad = in.readLine();
            File f = new File(pfad);
            f.getCanonicalPath();
            if(!pfad.isBlank() && f.exists()){
                graphLaden(pfad);
                dateidialog.setInitialDirectory((f.getAbsoluteFile()).getParentFile());
            } else {
                pfad = "";
            }
        }
        catch(Exception e) {
            pfad = "";
            dateidialog.setInitialDirectory(new File("beispielgraphen"));
        }
        finally{
            try{if(in != null) in.close();} catch(IOException e) {}
            showTitle();
        }

    }

    public void saveAktConfig() {
        PrintWriter pWriter = null;
        String s = "config.csv";
        try {
            pWriter = new PrintWriter(new FileWriter(s));
            pWriter.println(stage.isFullScreen()+","+stage.isMaximized());
            stage.setFullScreen(false);
            stage.setMaximized(false);
            pWriter.println(stage.getX()+","+stage.getY()+","+stage.getWidth()+","+ stage.getHeight());
            pWriter.println(pfad);
        } catch (IOException ioe) {
            ioe.printStackTrace();
        } finally {
            if (pWriter != null) {
                pWriter.flush();
                pWriter.close();
            }
        }
    }

    private void changeTab(Tab tabOld, Tab tabNew) {
        if(tpRekursionen.getTabs().indexOf(tabNew)<tpRekursionen.getTabs().size()-2) {
            tpRekursionen.getSelectionModel().select(tabOld);
        } else {

            if(tpRekursionen.getTabs().indexOf(tabNew)==tpRekursionen.getTabs().size()-2) {
                tpRekursionen.getTabs().remove(tpRekursionen.getTabs().size()-1);
            }
            ((TabMitController) tabNew).update();
            this.menuChangeAnsicht();
        }
    }

    private void oeffneHauptTab() {
        try {  // try-catch ist notwendig, um Fehler durch fehlende Dateien abzufangen.

            HauptTabMitController newtab = new HauptTabMitController(graph, options);
            newtab.setText("Graph");
            tpRekursionen.getTabs().add(newtab);
            FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/haupttab.fxml"));
            loader.setController(newtab);
            newtab.setContent((Node) loader.load());
            tpRekursionen.getSelectionModel().select(newtab);

        } 
        catch(Exception e)    {
          //  System.out.println(e);
        }   
    }

    public void setStage(Stage s){
        stage = s;
    }

    @FXML
    void mNeuerGraph(ActionEvent event) {
        while(tpRekursionen.getTabs().size()>1) tpRekursionen.getTabs().remove(1);

        TabMitController tc = (TabMitController) (tpRekursionen.getTabs().get(0));

        graph = new Graph();
        options = new GraphOptions(graph);
        tc.setGraph(graph, options);
        pfad = "";

        showTitle();
        menuChangeAnsicht();
    } 

    void schliesseTabs() {
        while(tpRekursionen.getTabs().size()>1) {
            TabMitController tc = (TabMitController) (tpRekursionen.getTabs().get(1));
            if(tc instanceof SimulationTabMitController) {
                ((SimulationTabMitController) tc).showHilfe(false);
            }
            tpRekursionen.getTabs().remove(1);
        }
    }

    @FXML
    void mBearbeiten(MouseEvent event) {

        try {  // try-catch ist notwendig, um Fehler durch fehlende Dateien abzufangen.
            schliesseTabs();
            EditTabMitController newtab = new EditTabMitController(graph, options);
            newtab.setText("Bearbeiten");
            tpRekursionen.getTabs().add(newtab);
            FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/edittab.fxml"));
            loader.setController(newtab);
            newtab.setContent((Node) loader.load());
            tpRekursionen.getSelectionModel().select(newtab);
        } 
        catch(Exception e)    {
            //System.out.println(e);
        }   
    }

    @FXML
    void mSimuliere(MouseEvent event) {

        try {  // try-catch ist notwendig, um Fehler durch fehlende Dateien abzufangen.
            schliesseTabs();

            SimulationTabMitController newtab = new SimulationTabMitController(graph, options);
            newtab.setText("Algorithmen-Simulation");
            tpRekursionen.getTabs().add(newtab);
            FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/simulationstab.fxml"));
            loader.setController(newtab);
            newtab.setContent((Node) loader.load());
            tpRekursionen.getSelectionModel().select(newtab);
        } 
        catch(Exception e)    {
         //   System.out.println(e);
        }   
    }    

    @FXML
    void mExperimentiereKanten(ActionEvent event) {
        schliesseTabs();

        GraphOptions neu = options.copy();
        neu.bildAnzeigen = false;
        neu.auswahl = 0;
        neu.fokusArt  = 1;
        neu.parent    = null;
        // neu.markiert  = Auswahl.BELIEBIG;
        // neu.geloescht = Auswahl.BELIEBIG;
        // neu.besucht   = Auswahl.BELIEBIG;
        TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());
        tc.tabOeffnen(neu);
    }

    @FXML
    void mExperimentiereKnoten(ActionEvent event) {
        schliesseTabs();

        GraphOptions neu = options.copy();
        neu.bildAnzeigen = false;
        neu.auswahl = 0;
        neu.fokusArt  = 0;
        neu.parent    = null;
        if(neu.farbenKanten[0].equals("invisible")) neu.farbenKanten[0]="808080";
        TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());
        tc.tabOeffnen(neu);
    }

    @FXML
    void mExperimentiereAuswahl(ActionEvent event) {
        schliesseTabs();

        GraphOptions neu = options.copy();
        neu.bildAnzeigen = false;
        neu.auswahl = 2;
        TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());

        if(tc.viewer.getSelectedKnoten() != null) {
            neu.fokusArt  = 0; // Knoten
            neu.parent    = tc.viewer.getSelectedKnoten();
            tc.tabOeffnen(neu);

        } else {
            if(tc.viewer.getSelectedKante() != null) {
                neu.fokusArt = 1; //Kante
                neu.parent = tc.viewer.getSelectedKante();
                tc.tabOeffnen(neu);

            }
        }
    }

    @FXML
    public void mBeenden(ActionEvent event) {
        saveAktConfig();
        schliesseTabs();
        ((Stage)tpRekursionen.getScene().getWindow()).close();
        System.exit(0);
    }

    void menuChangeAnsicht() {
        TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());
        GraphOptions options = tc.getGraphOptions();
        mOptionBild.setSelected(options.bildAnzeigen);
        mOptionKantengewichte.setSelected(options.showEdgeWeights);
        mOptionKnotenname.setSelected(options.showVertexText);
        mOptionKnotenwerte.setSelected(options.showVertexValue);
        mOptionKnoteninfo.setSelected(options.showVertexInfo);

        mmSpeichern.setDisable(!tc.getText().equals("Bearbeiten"));
        /*        mmBearbeiten.setDisabled(!tc.getText().equals("Graph"));
        mmExperimentieren.setVisible(tc.getText().equals("Graph"));
        mmSimulieren.setVisible(tc.getText().equals("Graph"));
        mmZuruecksetzen.setVisible(!tc.getText().equals("Bearbeiten"));
         */
        mmAnsicht.setDisable(tc.getText().equals("Bearbeiten"));
        if(tc.getText().equals("Algorithmen-Simulation")) {
            mHilfefenster.setDisable(false);
        } else 
        {
            mHilfefenster.setSelected(false);
            mHilfefenster.setDisable(true);
        }

    } 

    @FXML
    void mChangeOptionBild(ActionEvent event) {
        TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());
        GraphOptions options = tc.getGraphOptions();
        options.bildAnzeigen = mOptionBild.isSelected();
        tc.update();
    }

    @FXML
    void mChangeOptionKantengewichte(ActionEvent event) {
        TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());
        GraphOptions options = tc.getGraphOptions();
        options.showEdgeWeights = mOptionKantengewichte.isSelected();
        tc.update();
    }

    @FXML
    void mChangeOptionKnoteninfo(ActionEvent event) {
        TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());
        GraphOptions options = tc.getGraphOptions();
        options.showVertexInfo = mOptionKnoteninfo.isSelected();
        tc.update();
    }

    @FXML
    void mChangeOptionKnotenname(ActionEvent event) {
        TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());
        GraphOptions options = tc.getGraphOptions();
        options.showVertexText = mOptionKnotenname.isSelected();
        if(options.showVertexText) {
            options.showVertexValue = false;
            mOptionKnotenwerte.setSelected(false);
        }
        tc.update();
    }

    @FXML
    void mChangeOptionKnotenwerte(ActionEvent event) {
        TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());
        GraphOptions options = tc.getGraphOptions();
        options.showVertexValue = mOptionKnotenwerte.isSelected();
        if(options.showVertexValue) {
            options.showVertexText = false;
            mOptionKnotenname.setSelected(false);
        }
        tc.update();
    }

    @FXML
    void mChangeHilfefenster(ActionEvent event) {
        TabMitController tc = (TabMitController) (tpRekursionen.getTabs().get(tpRekursionen.getTabs().size()-1));
        if(tc instanceof SimulationTabMitController) {
            ((SimulationTabMitController) tc).showHilfe(mHilfefenster.isSelected());
        }
    }

    @FXML
    void mOeffnen(ActionEvent event) {
        dateidialog.getExtensionFilters().clear();
        dateidialog.getExtensionFilters().add(new ExtensionFilter("Graph-Datei (*.csv)", "*.csv"));                                          

        File file = dateidialog.showOpenDialog(null);
        if (file != null) {
            graphLaden(file.getAbsolutePath());
            dateidialog.setInitialDirectory(file.getAbsoluteFile().getParentFile());
        }
    }

    void graphLaden(String p) {

        while(tpRekursionen.getTabs().size()>2) tpRekursionen.getTabs().remove(1);
        TabMitController tc = (TabMitController) (tpRekursionen.getTabs().get(0));

        File f = new File(p); 
        if(f.exists() ){
            pfad = p;

            Table csvParser = new Table(pfad,"",',','"');

            graph = new Graph();
            graph.ladeGraph(csvParser);

            options = new GraphOptions(graph);
            options.ladeGraph(csvParser);

            tc.setGraph(graph, options);

            if(tpRekursionen.getTabs().size()>1){
                tc = (TabMitController) (tpRekursionen.getTabs().get(1));
                tc.setGraph(graph, options);
            }
        } 

        menuChangeAnsicht();
        showTitle();

    }

    public void showTitle() {

        if(stage!=null) {
            if(pfad == null || pfad.equals("")) {
                stage.setTitle("GraphTester by Thomas Schaller - Version "+version);
            } else {
                String[] arr = pfad.split("[/\\\\]");
                String dateiname = arr[arr.length-1];
                stage.setTitle("GraphTester by Thomas Schaller - Version "+version+" - "+dateiname);
            }
        }

    }

    @FXML
    void mSchliessen(ActionEvent event) {
        mNeuerGraph(event);
    }

    @FXML
    void mSpeichern(ActionEvent event) {
        dateidialog.getExtensionFilters().clear();
        dateidialog.getExtensionFilters().add(new ExtensionFilter("Graph-Datei (*.csv)", "*.csv"));
        if(!pfad.isBlank())
            dateidialog.setInitialFileName(new File(pfad).getName());
        else 
            dateidialog.setInitialFileName("");

        File file = dateidialog.showSaveDialog(null);
        if (file != null) {
            try{
                TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());
                GraphOptions options = tc.getGraphOptions();
                String text = options.getText();
                text += "#\n# Graph:\n";
                text +=graph.toCSVString(options.saveAsMatrix);
                String dateiName = file.getAbsolutePath();
                String name = dateiName.substring(dateiName.lastIndexOf("\\")+1);
                if(name.contains(".")) dateiName = dateiName.substring(0, dateiName.lastIndexOf("."));
                Files.write(Paths.get(file.getAbsolutePath()), text.getBytes());
                pfad = file.getAbsolutePath();
                dateidialog.setInitialDirectory(file.getAbsoluteFile().getParentFile());
                showTitle();
            } catch(Exception e) {

            }
        }

    }

    @FXML
    void mBildExportieren(ActionEvent event) {
        dateidialog.getExtensionFilters().clear();
        dateidialog.getExtensionFilters().add(new ExtensionFilter("Bild des Graphen", "*.png","*.gif"));
        File file = dateidialog.showSaveDialog(null);
        if (file != null) {
            try{
                TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());
                Picture p = tc.getViewer().updateImage();
                String dateiName = file.getAbsolutePath();
                p.save(dateiName);

            } catch(Exception e) {

            }
        }
    }       

    @FXML
    void mUeber(ActionEvent event) {
        Alert alert = new Alert(AlertType.INFORMATION);
        alert.setTitle("Graphtester");
        alert.setHeaderText(null);
        alert.setContentText("Mit diesem Programm können Sie Graphen modellieren, Graphenalgorithmen von Hand durchspielen oder implementierte Algorithmen schrittweise ausführen.\n\n"+
            "Version: "+version+"\nThomas Schaller\nLandesfachgruppe Informatik (ZSL BW)\n"+
            "Lizenz: CC BY-NC 4.0 (https://creativecommons.org/licenses/by-nc/4.0/deed.de)\n\n"+
            "3rd Party:\n CommonsIO.jar, csv.jar, JDom.jar\n Lizenzinfo: siehe Ordner '+libs'" );
        Image icon = new Image("view/icon.png");
        Stage stage = (Stage) alert.getDialogPane().getScene().getWindow();
        stage.getIcons().add(icon);
        alert.showAndWait();
    }

    @FXML
    void mResetAlles(ActionEvent event) {
        graph.initialisiereAlleKanten();
        graph.initialisiereAlleKnoten();
        TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());
        tc.update();
    }

    @FXML
    void mResetBesucht(ActionEvent event) {
        for(Knoten k : graph.getAlleKnoten()) {
            k.setBesucht(false);
        }
        TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());
        tc.update();
    }

    @FXML
    void mResetFarbe(ActionEvent event) {
        for(Knoten k : graph.getAlleKnoten()) {
            k.setFarbeAutomatisch(true);
        }
        TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());
        tc.update();
    }

    @FXML
    void mResetGeloescht(ActionEvent event) {
        for(Kante k : graph.getAlleKanten()) {
            k.setGeloescht(false);
        }
        TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());
        tc.update();

    }

    @FXML
    void mResetKantenmarkierung(ActionEvent event) {
        for(Kante k : graph.getAlleKanten()) {
            k.setMarkiert(false);
        }
        TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());
        tc.update();

    }

    @FXML
    void mResetMarkierung(ActionEvent event) {
        for(Knoten k : graph.getAlleKnoten()) {
            k.setMarkiert(false);
        }
        TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());
        tc.update();

    }

    @FXML
    void mResetWert(ActionEvent event) {
        for(Knoten k : graph.getAlleKnoten()) {
            k.setWert(0.0);
        }
        TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());
        tc.update();

    }

}
