// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.graphview.plugin.dialogs;

import static org.openstreetmap.josm.tools.I18n.tr;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Line2D;
import java.io.File;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JColorChooser;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTextField;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import org.openstreetmap.josm.gui.preferences.DefaultTabPreferenceSetting;
import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane;
import org.openstreetmap.josm.plugins.graphview.plugin.dialogs.AccessParameterDialog.BookmarkAction;
import org.openstreetmap.josm.plugins.graphview.plugin.layer.GraphViewLayer;
import org.openstreetmap.josm.plugins.graphview.plugin.preferences.GraphViewPreferenceDefaults;
import org.openstreetmap.josm.plugins.graphview.plugin.preferences.GraphViewPreferences;
import org.openstreetmap.josm.plugins.graphview.plugin.preferences.PreferenceAccessParameters;
import org.openstreetmap.josm.tools.GBC;

public class GraphViewPreferenceEditor extends DefaultTabPreferenceSetting {

    private File rulesetFolder;
    private Map<String, PreferenceAccessParameters> parameterBookmarks;

    private JPanel preferencePanel;

    private JCheckBox internalRulesetCheckBox;
    private JLabel rulesetFolderLabel;
    private JTextField rulesetFolderTextField;
    private JButton selectRulesetFolderButton;

    private JComboBox<String> bookmarkComboBox;
    private JButton editBookmarkButton;
    private JButton deleteBookmarkButton;

    private JCheckBox separateDirectionsCheckBox;
    private JButton segmentColorButton;
    private JPanel segmentColorField;
    private JButton nodeColorButton;
    private JPanel nodeColorField;
    private JButton arrowheadFillColorButton;
    private JPanel arrowheadFillColorField;
    private JSlider arrowheadPlacementSlider;
    private JPanel arrowPreviewPanel;

    public GraphViewPreferenceEditor() {
        super("graphview", tr("Graphview"),
                tr("Settings for the Graphview plugin that visualizes routing graphs."));
    }

    @Override
    public void addGui(PreferenceTabbedPane gui) {

        readPreferences();

        preferencePanel = gui.createPreferenceTab(this);

        JPanel mainPanel = createMainPanel();

        preferencePanel.add(mainPanel, GBC.eol().fill(GBC.BOTH));

        updateVehiclePanel(GraphViewPreferences.getInstance().getCurrentParameterBookmarkName());

    }

    /**
     * creates local versions of preference information
     * that will only be written to real preferences if the OK button is pressed
     */
    private void readPreferences() {

        GraphViewPreferences preferences = GraphViewPreferences.getInstance();

        rulesetFolder = preferences.getRulesetFolder();

        parameterBookmarks =
            new HashMap<>(preferences.getParameterBookmarks());

    }

    private JPanel createMainPanel() {

        JPanel mainPanel = new JPanel();

        GridBagLayout mainLayout = new GridBagLayout();
        mainPanel.setLayout(mainLayout);

        GridBagConstraints constraints = new GridBagConstraints();
        constraints.fill = GridBagConstraints.HORIZONTAL;
        constraints.weightx = 1;
        constraints.gridx = 0;

        {
            JPanel rulesetPanel = createRulesetPanel();
            constraints.gridy = 0;
            mainLayout.setConstraints(rulesetPanel, constraints);
            mainPanel.add(rulesetPanel);
        } {
            JPanel vehiclePanel = createVehiclePanel();
            constraints.gridy = 1;
            mainLayout.setConstraints(vehiclePanel, constraints);
            mainPanel.add(vehiclePanel);
        } {
            JPanel visualizationPanel = createVisualizationPanel();
            constraints.gridy = 2;
            mainLayout.setConstraints(visualizationPanel, constraints);
            mainPanel.add(visualizationPanel);
        }

        mainPanel.add(GBC.glue(0, 0));

        return mainPanel;

    }

    private JPanel createRulesetPanel() {

        JPanel rulesetPanel = new JPanel();
        rulesetPanel.setBorder(BorderFactory.createTitledBorder(tr("Ruleset")));
        rulesetPanel.setLayout(new BoxLayout(rulesetPanel, BoxLayout.Y_AXIS));

        internalRulesetCheckBox = new JCheckBox(tr("Use built-in rulesets"));
        internalRulesetCheckBox.setSelected(GraphViewPreferences.getInstance().getUseInternalRulesets());
        internalRulesetCheckBox.addActionListener(internalRulesetActionListener);
        rulesetPanel.add(internalRulesetCheckBox);

        rulesetFolderLabel = new JLabel(tr("External ruleset directory:"));
        rulesetPanel.add(rulesetFolderLabel);

        rulesetFolderTextField = new JTextField();
        rulesetFolderTextField.setText(rulesetFolder.getPath());
        rulesetFolderTextField.setEditable(false);
        rulesetPanel.add(rulesetFolderTextField);

        selectRulesetFolderButton = new JButton(tr("Select directory"));
        selectRulesetFolderButton.addActionListener(selectRulesetFolderActionListener);
        rulesetPanel.add(selectRulesetFolderButton);

        updateRulesetPanel();

        return rulesetPanel;
    }

    private JPanel createVehiclePanel() {

        JPanel vehiclePanel = new JPanel();
        vehiclePanel.setBorder(BorderFactory.createTitledBorder(tr("Vehicle")));
        vehiclePanel.setLayout(new BoxLayout(vehiclePanel, BoxLayout.Y_AXIS));

        bookmarkComboBox = new JComboBox<>();
        vehiclePanel.add(bookmarkComboBox);

        JPanel buttonPanel = new JPanel();
        buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));

        JButton createButton = new JButton(tr("Create"));
        createButton.addActionListener(createVehicleActionListener);
        buttonPanel.add(createButton);

        editBookmarkButton = new JButton(tr("Edit"));
        editBookmarkButton.addActionListener(editVehicleActionListener);
        buttonPanel.add(editBookmarkButton);

        deleteBookmarkButton = new JButton(tr("Delete"));
        deleteBookmarkButton.addActionListener(deleteVehicleActionListener);
        buttonPanel.add(deleteBookmarkButton);

        JButton restoreDefaultsButton = new JButton(tr("Restore defaults"));
        restoreDefaultsButton.addActionListener(restoreVehicleDefaultsActionListener);
        buttonPanel.add(restoreDefaultsButton);

        vehiclePanel.add(buttonPanel);

        return vehiclePanel;
    }

    private JPanel createVisualizationPanel() {

        JPanel visualizationPanel = new JPanel();
        visualizationPanel.setBorder(BorderFactory.createTitledBorder(tr("Visualization")));
        visualizationPanel.setLayout(new BoxLayout(visualizationPanel, BoxLayout.Y_AXIS));

        separateDirectionsCheckBox = new JCheckBox(tr("Draw directions separately"));
        separateDirectionsCheckBox.setSelected(GraphViewPreferences.getInstance().getSeparateDirections());
        visualizationPanel.add(separateDirectionsCheckBox);

        { // create color chooser panel

            JPanel colorPanel = new JPanel();
            colorPanel.setLayout(new GridLayout(3, 2));

            Color nodeColor = GraphViewPreferences.getInstance().getNodeColor();

            nodeColorButton = new JButton(tr("Node color"));
            nodeColorButton.addActionListener(chooseNodeColorActionListener);
            colorPanel.add(nodeColorButton);
            nodeColorField = new JPanel();
            nodeColorField.setBackground(nodeColor);
            colorPanel.add(nodeColorField);

            Color segmentColor = GraphViewPreferences.getInstance().getSegmentColor();

            segmentColorButton = new JButton(tr("Arrow color"));
            segmentColorButton.addActionListener(chooseSegmentColorActionListener);
            colorPanel.add(segmentColorButton);
            segmentColorField = new JPanel();
            segmentColorField.setBackground(segmentColor);
            colorPanel.add(segmentColorField);

            Color arrowheadFillColor = GraphViewPreferences.getInstance().getArrowheadFillColor();

            arrowheadFillColorButton = new JButton(tr("Arrowhead fill color"));
            arrowheadFillColorButton.addActionListener(chooseArrowheadFillColorActionListener);
            colorPanel.add(arrowheadFillColorButton);
            arrowheadFillColorField = new JPanel();
            arrowheadFillColorField.setBackground(arrowheadFillColor);
            colorPanel.add(arrowheadFillColorField);

            visualizationPanel.add(colorPanel);

        }

        arrowheadPlacementSlider = new JSlider(0, 100);
        arrowheadPlacementSlider.setToolTipText(tr("Arrowhead placement"));
        arrowheadPlacementSlider.setMajorTickSpacing(10);
        arrowheadPlacementSlider.setPaintTicks(true);
        arrowheadPlacementSlider.setName("name");
        arrowheadPlacementSlider.setLabelTable(null);
        arrowheadPlacementSlider.setValue((int) Math.round(
                100 * GraphViewPreferences.getInstance().getArrowheadPlacement()));
        arrowheadPlacementSlider.addChangeListener(arrowheadPlacementChangeListener);
        visualizationPanel.add(arrowheadPlacementSlider);

        arrowPreviewPanel = new ArrowPreviewPanel();
        visualizationPanel.add(arrowPreviewPanel);

        return visualizationPanel;
    }

    @Override
    public boolean ok() {

        GraphViewPreferences preferences = GraphViewPreferences.getInstance();

        preferences.setUseInternalRulesets(internalRulesetCheckBox.isSelected());
        preferences.setRulesetFolder(rulesetFolder);

        preferences.setParameterBookmarks(parameterBookmarks);

        String selectedBookmarkName = (String) bookmarkComboBox.getSelectedItem();
        preferences.setCurrentParameterBookmarkName(selectedBookmarkName);

        preferences.setSeparateDirections(separateDirectionsCheckBox.isSelected());

        preferences.setNodeColor(nodeColorField.getBackground());
        preferences.setSegmentColor(segmentColorField.getBackground());
        preferences.setArrowheadFillColor(arrowheadFillColorField.getBackground());

        preferences.setArrowheadPlacement(
                arrowheadPlacementSlider.getValue() / 100f);

        preferences.distributeChanges();

        return false;
    }

    private final ActionListener internalRulesetActionListener = new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            updateRulesetPanel();
        }
    };

    private final ActionListener selectRulesetFolderActionListener = new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {

            File initialFCDirectory = rulesetFolder;
            if (rulesetFolder.getParentFile() != null) {
                initialFCDirectory = rulesetFolder.getParentFile();
            }

            final JFileChooser fc = new JFileChooser();
            fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
            fc.setCurrentDirectory(initialFCDirectory);

            int returnVal = fc.showOpenDialog(preferencePanel);

            if (returnVal == JFileChooser.APPROVE_OPTION) {
                rulesetFolder = fc.getSelectedFile();
                rulesetFolderTextField.setText(rulesetFolder.getPath());
            }

        }
    };

    private final ActionListener createVehicleActionListener = new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {

            PreferenceAccessParameters defaultBookmarkParameters =
                GraphViewPreferenceDefaults.createDefaultBookmarkAccessParameters();

            AccessParameterDialog apd = new AccessParameterDialog(
                    null,
                    false,
                    tr("New bookmark"),
                    parameterBookmarks.keySet(),
                    defaultBookmarkParameters,
                    new BookmarkAction() {
                        @Override
                        public void execute(String name, PreferenceAccessParameters parameters) {
                            parameterBookmarks.put(name, parameters);
                            updateVehiclePanel(name);
                        }
                    });

            apd.setVisible(true);
        }
    };

    private final ActionListener editVehicleActionListener = new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            if (bookmarkComboBox.getSelectedItem() != null) {

                final String selectedBookmarkName = (String) bookmarkComboBox.getSelectedItem();
                PreferenceAccessParameters parameters =
                    parameterBookmarks.get(selectedBookmarkName);

                Collection<String> otherBookmarkNames = new LinkedList<>();
                for (String bookmarkName : parameterBookmarks.keySet()) {
                    if (!bookmarkName.equals(selectedBookmarkName)) {
                        otherBookmarkNames.add(bookmarkName);
                    }
                }

                AccessParameterDialog apd = new AccessParameterDialog(
                        null,
                        true,
                        selectedBookmarkName,
                        otherBookmarkNames,
                        parameters,
                        new BookmarkAction() {
                            @Override
                            public void execute(String name, PreferenceAccessParameters parameters) {
                                parameterBookmarks.remove(selectedBookmarkName);
                                parameterBookmarks.put(name, parameters);
                                updateVehiclePanel(name);
                            }
                        });

                apd.setVisible(true);
            }
        }
    };

    private final ActionListener deleteVehicleActionListener = new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            if (bookmarkComboBox.getSelectedItem() != null) {

                String selectedBookmarkName = (String) bookmarkComboBox.getSelectedItem();

                int userChoice = JOptionPane.showConfirmDialog(
                        preferencePanel,
                        tr("Really delete \"{0}\"?", selectedBookmarkName),
                        tr("Bookmark deletion"),
                        JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);

                if (userChoice == JOptionPane.YES_OPTION) {
                    parameterBookmarks.remove(selectedBookmarkName);
                    updateVehiclePanel(null);
                }
            }
        }
    };

    private final ActionListener restoreVehicleDefaultsActionListener = new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {

            int userChoice = JOptionPane.showConfirmDialog(
                    preferencePanel,
                    tr("Really restore default bookmarks?\n"
                    + "All manually added or edited bookmarks will be lost!"),
                    tr("Bookmark reset"),
                    JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);

            if (userChoice == JOptionPane.YES_OPTION) {
                parameterBookmarks.clear();
                parameterBookmarks.putAll(
                        GraphViewPreferenceDefaults.createDefaultAccessParameterBookmarks());
                updateVehiclePanel(null);
            }

        }
    };

    private final ActionListener chooseNodeColorActionListener = new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {

            Color selectedColor = JColorChooser.showDialog(
                    preferencePanel, tr("Choose node color"), nodeColorField.getBackground());

            if (selectedColor != null) {
                nodeColorField.setBackground(selectedColor);
            }

            arrowPreviewPanel.repaint();

        }
    };

    private final ActionListener chooseSegmentColorActionListener = new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {

            Color selectedColor = JColorChooser.showDialog(
                    preferencePanel, tr("Choose arrow color"), segmentColorField.getBackground());

            if (selectedColor != null) {
                segmentColorField.setBackground(selectedColor);
            }

            arrowPreviewPanel.repaint();

        }
    };

    private final ActionListener chooseArrowheadFillColorActionListener = new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {

            Color selectedColor = JColorChooser.showDialog(
                    preferencePanel, tr("Choose arrowhead fill color"), segmentColorField.getBackground());

            if (selectedColor != null) {
                arrowheadFillColorField.setBackground(selectedColor);
            }

            arrowPreviewPanel.repaint();

        }
    };

    private final ChangeListener arrowheadPlacementChangeListener = new ChangeListener() {
        @Override
        public void stateChanged(ChangeEvent e) {
            arrowPreviewPanel.repaint();
        }
    };

    private void updateRulesetPanel() {

        rulesetFolderLabel.setEnabled(!internalRulesetCheckBox.isSelected());
        rulesetFolderTextField.setEnabled(!internalRulesetCheckBox.isSelected());
        selectRulesetFolderButton.setEnabled(!internalRulesetCheckBox.isSelected());

    }

    private void updateVehiclePanel(String selectedBookmarkName) {

        bookmarkComboBox.removeAllItems();
        for (String bookmarkName : parameterBookmarks.keySet()) {
            bookmarkComboBox.addItem(bookmarkName);
        }

        if (selectedBookmarkName == null) {
            if (bookmarkComboBox.getItemCount() > 0) {
                bookmarkComboBox.setSelectedIndex(0);
            }
        } else {
            bookmarkComboBox.setSelectedItem(selectedBookmarkName);
        }

        editBookmarkButton.setEnabled(parameterBookmarks.size() > 0);
        deleteBookmarkButton.setEnabled(parameterBookmarks.size() > 0);
    }

    private class ArrowPreviewPanel extends JPanel {

        ArrowPreviewPanel() {
            setPreferredSize(new Dimension(100, 50));
            setBackground(Color.DARK_GRAY);
        }

        @Override
        public void paint(Graphics g) {

            super.paint(g);

            Graphics2D g2D = (Graphics2D) g;

            Point p1 = new Point(15, this.getHeight() / 2);
            Point p2 = new Point(this.getWidth()-15, this.getHeight() / 2);

            g2D.setStroke(new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
            g2D.setColor(segmentColorField.getBackground());
            g2D.draw(new Line2D.Float(p1.x, p1.y, p2.x, p2.y));

            GraphViewLayer.paintNode(g, p1, nodeColorField.getBackground());
            GraphViewLayer.paintNode(g, p2, nodeColorField.getBackground());

            GraphViewLayer.paintArrowhead(g2D, p1, p2,
                    arrowheadPlacementSlider.getValue() / 100.0,
                    segmentColorField.getBackground(),
                    arrowheadFillColorField.getBackground());
        }
    }
}
