package view;


import model.Line;
import presenter.AbstractPresenter;
import presenter.Presenter;
import view.listener.*;
import view.panels.*;
import view.panels.tabs.LMSPanel;
import view.panels.tabs.RMPanel;
import view.panels.tabs.TSPanel;
import view.panels.tabs.TabPanel;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

/**
 * Implementierung verschiedener Algorithmen zur Berechnung von Ausgleichsgeraden.
 *
 * @Author: Armin Wolf
 * @Email: a_wolf28@uni-muenster.de
 * @Date: 28.05.2017.
 */
public class MainFrame extends JFrame {

    private AbstractPresenter presenter;
    private Boolean lmsIsComplete = false;
    private Boolean rmIsComplete = false;
    private Boolean tsIsComplete = false;

    private JButton arrangementButton;
    private JButton importButton;
    private JButton generateButton;
    private JButton exportButton;

    private InfoPanel output;
    private MenuBar menu;
    private JToolBar toolBar;
    private TabPanel lmsPanel;
    private TabPanel rmPanel;
    private TabPanel tsPanel;

    private DualityPanel arrangement;
    private JDialog arrangementDialog;
    private PlotPanel plotLMS;
    private PlotPanel plotRM;
    private PlotPanel plotTS;
    private JDialog progressDialog;
    private JDialog evaluationDialog;

    private Container progressContent;
    private JProgressBar progressBar;


    private JSplitPane splitpane;
    private JScrollPane scrollPane;
    private JTabbedPane tabbedPane;
    private EvaluationPanel evaluationPanel;

    public MainFrame() {
        super();
        initializeComponents();
        setDimensions();
        setLayouts();
        setTitles();

        addComponents();

        setCloseOperations();
        setIcons();
        disableFunctionality();
        this.setVisible(true);
    }


    /*******************************************************************************************************************
     *                                          visualisierungs methoden
     ******************************************************************************************************************/

    public void createDualityDialog() {
        arrangement = new DualityPanel();
        arrangementDialog = new JDialog();
        arrangementDialog.setTitle("Dualraum - Dialog");
        arrangementDialog.setMinimumSize(new Dimension(1024, 768));
        arrangementDialog.setPreferredSize(new Dimension(800, 800));
        arrangementDialog.setLocationRelativeTo(this);
        arrangementDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
        arrangementButton.addActionListener(new DualityVisualizerListener((Presenter) getPresenter()));
        arrangement.clear();
        arrangement.setPrameters(getPresenter().getModel().getLines(),
                getPresenter().getModel().getNodes(),
                getPresenter().getModel().getxMinimum(),
                getPresenter().getModel().getxMaximum(),
                getPresenter().getModel().getyMinimum(),
                getPresenter().getModel().getyMaximum());
        arrangement.createArrangement();

        SwingUtilities.invokeLater(() -> {
            arrangementDialog.add(arrangement);
            arrangementDialog.setLocationRelativeTo(this);
            arrangementDialog.revalidate();
            arrangementDialog.repaint();
            arrangementDialog.setVisible(true);
        });
    }

    public void visualizeLMS(double m, double b) {
        plotLMS = new PlotPanel();
        lmsPanel.setPlotPanel(plotLMS);
        createPlot(m, b, plotLMS, lmsPanel, "LMS");
    }

    public void visualizeRM(double m, double b) {
        plotRM = new PlotPanel();
        rmPanel.setPlotPanel(plotRM);
        createPlot(m, b, plotRM, rmPanel, "RM");
    }

    public void visualizeTS(double m, double b) {
        plotTS = new PlotPanel();
        tsPanel.setPlotPanel(plotTS);
        createPlot(m, b, plotTS, tsPanel, "TS");
    }

    public void createPlot(double m, double b, PlotPanel plot, JPanel panel, String name) {
        SwingUtilities.invokeLater(() -> {
            plot.clear();
            plot.createPlot(getPresenter().getModel().getLines());
            plot.addLineToPlot(m, b, name);
            panel.revalidate();
        });
    }

    public void showImportProgress(Integer progress) {
        progressBar.setValue(progress);
        progressBar.setStringPainted(true);
        progressDialog.setVisible(true);
    }

    public void showEvauluationDialog() {
        if (evaluationDialog == null) {
            SwingUtilities.invokeLater(() -> {
                evaluationDialog = new JDialog();
                evaluationDialog.setTitle("evaluation");
                evaluationDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
                evaluationDialog.setSize(new Dimension(1800, 800));
                evaluationDialog.setLocationRelativeTo(null);

                evaluationPanel = new EvaluationPanel(this);
                evaluationDialog.setLocationRelativeTo(this);
                evaluationDialog.add(evaluationPanel);
                evaluationDialog.setVisible(true);
            });
        } else {
            SwingUtilities.invokeLater(() -> {
                evaluationDialog.setLocationRelativeTo(this);
                evaluationDialog.setVisible(true);
            });
        }

    }

    public void appendEvalResult(Object[] res, int col, boolean isApprCol) {
        SwingUtilities.invokeLater(() -> {
            Object[] tmp = Arrays.asList(res).subList(2, res.length).toArray();
            if (isApprCol) {
                evaluationPanel.setCurrentRow(tmp.length);
                evaluationPanel.addColumn(tmp, col, true);
            } else {
                evaluationPanel.addColumn(tmp, col + 1, false);
            }
            evaluationPanel.repaint();
            evaluationPanel.revalidate();
        });
    }

    public void appendEvalResult(Object[] res) {
        SwingUtilities.invokeLater(() -> {
            Object[] tmp = Arrays.asList(res).subList(1, res.length).toArray();
            evaluationPanel.addRow(tmp);
            evaluationPanel.repaint();
            evaluationPanel.revalidate();
        });
    }

    public void drawLineResult(Object[] res, int alg) {
        SwingUtilities.invokeLater(() -> {
            Object[] result = Arrays.asList(res).subList(2, res.length).toArray();
            evaluationPanel.drawLines(result, alg);
            evaluationPanel.repaint();
            evaluationPanel.revalidate();
        });
    }

    public void drawLineResults(Object[] res) {
        SwingUtilities.invokeLater(() -> {
            Object[] result = Arrays.asList(res).subList(1, res.length).toArray();
            ArrayList<Double[]> algs = new ArrayList<>();

            for (int i = 0; i < (result.length + 1) / 3; i++) {
                String name = (String) result[(3 * i)];
                String m = (String) result[(3 * i) + 1];
                String b = (String) result[(3 * i) + 2];
                Double[] tmp = {Double.parseDouble(name), Double.parseDouble(m), Double.parseDouble(b)};
                algs.add(tmp);
            }

            evaluationPanel.drawLines(algs);
            evaluationPanel.repaint();
            evaluationPanel.revalidate();
        });
    }

    public void addEvalDataset(LinkedList<Line> lines) {
        SwingUtilities.invokeLater(() -> {
            evaluationPanel.setDualPoints(lines);
            evaluationPanel.repaint();
            evaluationPanel.revalidate();
        });
    }


    /*******************************************************************************************************************
     *                                          init GUI
     ******************************************************************************************************************/
    private void setTitles() {
        this.setTitle("Algorithmen zur Berechnung von Ausgleichgeraden");
        importButton.setText("Import");
        exportButton.setText("Export");
        generateButton.setText("Generiere");
        arrangementButton.setText("Dualraum");
    }

    private void setupTabbedPane() {
        tabbedPane.add("Least Median of Squares", lmsPanel);
        tabbedPane.add("Repeated Median", rmPanel);
        tabbedPane.add("Theil-Sen", tsPanel);
    }

    private void addComponents() {
        toolBar.add(arrangementButton);
        toolBar.add(importButton);
        toolBar.add(exportButton);
        toolBar.add(generateButton);
        toolBar.setFloatable(false);

        setJMenuBar(menu.getMenuBar());
        add(toolBar);
        //northPanel.add(toolBar);

        setupSplitPane();
        setupTabbedPane();

        progressContent.add(progressBar, BorderLayout.CENTER);
        progressBar.setBorder(BorderFactory.createTitledBorder("Import..."));
        progressDialog.setLocationRelativeTo(null);
        ((JPanel) progressDialog.getContentPane()).setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY));

        this.add(toolBar, BorderLayout.NORTH);
        this.add(splitpane, BorderLayout.CENTER);

    }

    private void setupSplitPane() {
        splitpane.setOrientation(JSplitPane.HORIZONTAL_SPLIT);
        splitpane.setResizeWeight(.5d);
        splitpane.setContinuousLayout(true);
        splitpane.setLeftComponent(output);
        splitpane.setRightComponent(tabbedPane);
    }

    private void setCloseOperations() {
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        progressDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
    }

    private void setDimensions() {
        this.setExtendedState(JFrame.MAXIMIZED_BOTH);
        this.setMinimumSize(new Dimension(1024,768));
        this.setResizable(true);
        GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
        this.setMaximizedBounds(env.getMaximumWindowBounds());
        lmsPanel.setMinimumSize(new Dimension(400, 500));
        rmPanel.setMinimumSize(new Dimension(400, 500));
        tsPanel.setMinimumSize(new Dimension(400, 500));
        output.setMinimumSize(new Dimension(400, 500));
        progressDialog.setSize(300, 45);
        progressDialog.setResizable(false);
        progressDialog.setUndecorated(true);
    }

    private void setLayouts() {
        this.setLayout(new BorderLayout());
        toolBar.setLayout(new FlowLayout(FlowLayout.LEFT));
    }

    private void initializeComponents() {
        //panels
        toolBar = new JToolBar();
        lmsPanel = new LMSPanel();
        rmPanel = new RMPanel();
        tsPanel = new TSPanel();
        menu = new MenuBar(this);

        //Dialogs
        progressDialog = new JDialog();
        progressDialog.setLocationRelativeTo(null);
        progressContent = progressDialog.getContentPane();
        progressBar = new JProgressBar();

        //Panes
        tabbedPane = new JTabbedPane();
        output = new InfoPanel();
        splitpane = new JSplitPane();

        //Buttons
        arrangementButton = new JButton();
        importButton = new JButton();
        generateButton = new JButton();
        exportButton = new JButton();
    }

    private void setIcons() {
        try {
            ClassLoader classLoader = getClass().getClassLoader();
            Image imgImport = ImageIO.read(classLoader.getResource("import.png")).getScaledInstance(16, 16, Image.SCALE_SMOOTH);
            Image imgPlot = ImageIO.read(classLoader.getResource("plot.png")).getScaledInstance(16, 16, Image.SCALE_SMOOTH);
            Image imgStart = ImageIO.read(classLoader.getResource("start.png")).getScaledInstance(32, 32, Image.SCALE_SMOOTH);
            Image imgGenerate = ImageIO.read(classLoader.getResource("generate.png")).getScaledInstance(16, 16, Image.SCALE_SMOOTH);
            Image imgExport = ImageIO.read(classLoader.getResource("export.png")).getScaledInstance(16, 16, Image.SCALE_SMOOTH);
            Image imgFrame = ImageIO.read(classLoader.getResource("frame.png")).getScaledInstance(32, 23, Image.SCALE_SMOOTH);


            importButton.setIcon(new ImageIcon(imgImport));
            exportButton.setIcon(new ImageIcon(imgExport));
            generateButton.setIcon(new ImageIcon(imgGenerate));
            arrangementButton.setIcon(new ImageIcon(imgPlot));
            lmsPanel.getStartButton().setIcon(new ImageIcon(imgStart));
            rmPanel.getStartButton().setIcon(new ImageIcon(imgStart));
            tsPanel.getStartButton().setIcon(new ImageIcon(imgStart));
            this.setIconImage(imgFrame);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void setActionListeners() {

        //action listener für MenuItems
        menu.addActionListeners();

        //action listener für diese Klasse
        arrangementButton.addActionListener(new DualityVisualizerListener((Presenter) getPresenter()));
        lmsPanel.getStartButton().addActionListener(new StartAlgorithmListener(((Presenter) getPresenter()), lmsPanel));
        rmPanel.getStartButton().addActionListener(new StartAlgorithmListener(((Presenter) getPresenter()), rmPanel));
        tsPanel.getStartButton().addActionListener(new StartAlgorithmListener(((Presenter) getPresenter()), tsPanel));
        importButton.addActionListener(new ImportDataListener((Presenter) this.getPresenter() , this));
        exportButton.addActionListener(new ExportDataListener((Presenter) this.getPresenter() , this));
        generateButton.addActionListener(new GenerateDataListener((Presenter) this.getPresenter()));

    }

    public void enableFunctionality() {
        this.getLmsPanel().getStartButton().setEnabled(true);
        this.getRmPanel().getStartButton().setEnabled(true);
        this.getTsPanel().getStartButton().setEnabled(true);
        this.getArrangementButton().setEnabled(true);
        this.getExportButton().setEnabled(true);
    }

    public void disableFunctionality() {
        this.getLmsPanel().getStartButton().setEnabled(false);
        this.getRmPanel().getStartButton().setEnabled(false);
        this.getTsPanel().getStartButton().setEnabled(false);
        this.getArrangementButton().setEnabled(false);
        this.getExportButton().setEnabled(false);
    }

    /*******************************************************************************************************************
     *                                          log Methode
     ******************************************************************************************************************/
    public void log(String s) {
        SwingUtilities.invokeLater(() -> output.appendParagraph(s));
    }

    public void logError(String s) {
        SwingUtilities.invokeLater(() -> output.appendParagraphRed(s));
    }

    public void logSuccess(String s) {
        SwingUtilities.invokeLater(() -> output.appendParagraphGreen(s));
    }

    public void logHeading(String s) {
        SwingUtilities.invokeLater(() -> output.appendParagraphWithHeading(s));
    }

    public void createTable(List<String> heading, List<List<String>> rows) {
        SwingUtilities.invokeLater(() -> output.logTable(heading, rows));
    }

    /*******************************************************************************************************************
     *                                          Getter und Setter Methoden
     ******************************************************************************************************************/

    public Boolean getLmsIsComplete() {
        return lmsIsComplete;
    }

    public void setLmsIsComplete(Boolean lmsIsComplete) {
        this.lmsIsComplete = lmsIsComplete;
    }

    public Boolean getRmIsComplete() {
        return rmIsComplete;
    }

    public void setRmIsComplete(Boolean rmIsComplete) {
        this.rmIsComplete = rmIsComplete;
    }

    public Boolean getTsIsComplete() {
        return tsIsComplete;
    }

    public void setTsIsComplete(Boolean tsIsComplete) {
        this.tsIsComplete = tsIsComplete;
    }

    public JButton getArrangementButton() {
        return arrangementButton;
    }

    public void setArrangementButton(JButton arrangementButton) {
        this.arrangementButton = arrangementButton;
    }

    public JButton getImportButton() {
        return importButton;
    }

    public void setImportButton(JButton importButton) {
        this.importButton = importButton;
    }

    public JToolBar getToolBar() {
        return toolBar;
    }

    public void setToolBar(JToolBar toolBar) {
        this.toolBar = toolBar;
    }

    public JDialog getArrangementDialog() {
        return arrangementDialog;
    }

    public void setArrangementDialog(JDialog arrangementDialog) {
        this.arrangementDialog = arrangementDialog;
    }

    public InfoPanel getOutput() {
        return output;
    }

    public void setOutput(InfoPanel output) {
        this.output = output;
    }

    public MenuBar getMenu() {
        return menu;
    }

    public TabPanel getLmsPanel() {
        return lmsPanel;
    }

    public JSplitPane getSplitpane() {
        return splitpane;
    }

    public void setSplitpane(JSplitPane splitpane) {
        this.splitpane = splitpane;
    }

    public JScrollPane getScrollPane() {
        return scrollPane;
    }

    public void setScrollPane(JScrollPane scrollPane) {
        this.scrollPane = scrollPane;
    }

    public TabPanel getRmPanel() {
        return rmPanel;
    }

    public void setRmPanel(TabPanel rmPanel) {
        this.rmPanel = rmPanel;
    }

    public PlotPanel getPlotLMS() {
        return plotLMS;
    }

    public void setPlotLMS(PlotPanel plotLMS) {
        this.plotLMS = plotLMS;
    }

    public AbstractPresenter getPresenter() {
        return presenter;
    }

    public void setPresenter(AbstractPresenter presenter) {
        this.presenter = presenter;
    }

    public JDialog getProgressDialog() {
        return progressDialog;
    }

    public void setProgressDialog(JDialog progressDialog) {
        this.progressDialog = progressDialog;
    }

    public TabPanel getTsPanel() {
        return tsPanel;
    }

    public void setTsPanel(TabPanel tsPanel) {
        this.tsPanel = tsPanel;
    }

    public JButton getExportButton() {
        return exportButton;
    }

    public void setExportButton(JButton exportButton) {
        this.exportButton = exportButton;
    }
}
