//License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.actions;

import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
import static org.openstreetmap.josm.tools.I18n.tr;
import static org.openstreetmap.josm.tools.I18n.trn;

import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import javax.swing.JOptionPane;

import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.command.RemoveNodesCommand;
import org.openstreetmap.josm.data.osm.Node;
import org.openstreetmap.josm.data.osm.OsmPrimitive;
import org.openstreetmap.josm.data.osm.Way;
import org.openstreetmap.josm.tools.Shortcut;

/**
 * Disconnect nodes from a way they currently belong to.
 * @since 6253
 */
public class UnJoinNodeWayAction extends JosmAction {
    
    /**
     * Constructs a new {@code UnJoinNodeWayAction}.
     */
    public UnJoinNodeWayAction() {
        super(tr("Disconnect Node from Way"), "unjoinnodeway",
                tr("Disconnect nodes from a way they currently belong to"),
                Shortcut.registerShortcut("tools:unjoinnodeway",
                    tr("Tool: {0}", tr("Disconnect Node from Way")), KeyEvent.VK_J, Shortcut.ALT), true);
        putValue("help", ht("/Action/UnJoinNodeWay"));
    }

    @Override
    public void actionPerformed(ActionEvent e) {

        Collection<OsmPrimitive> selection = getCurrentDataSet().getSelected();

        List<Node> selectedNodes = OsmPrimitive.getFilteredList(selection, Node.class);
        List<Way> selectedWays = OsmPrimitive.getFilteredList(selection, Way.class);
        List<Way> applicableWays = getApplicableWays(selectedWays, selectedNodes);

        if (applicableWays == null) {
            JOptionPane.showMessageDialog(
                    Main.parent,
                    tr("Select at least one node to be disconnected."),
                    tr("Warning"),
                    JOptionPane.WARNING_MESSAGE);
            return;
        } else if (applicableWays.isEmpty()) {
            JOptionPane.showMessageDialog(Main.parent,
                    trn("Selected node cannot be disconnected from anything.",
                        "Selected nodes cannot be disconnected from anything.",
                        selectedNodes.size()),
                    tr("Warning"),
                    JOptionPane.WARNING_MESSAGE);
            return;
        } else if (applicableWays.size() > 1) {
            JOptionPane.showMessageDialog(Main.parent,
                    trn("There is more than one way using the node you selected. Please select the way also.",
                        "There is more than one way using the nodes you selected. Please select the way also.",
                        selectedNodes.size()),
                    tr("Warning"),
                    JOptionPane.WARNING_MESSAGE);
            return;
        } else if (applicableWays.get(0).getRealNodesCount() < selectedNodes.size() + 2) {
            // there is only one affected way, but removing the selected nodes would only leave it
            // with less than 2 nodes
            JOptionPane.showMessageDialog(Main.parent,
                    trn("The affected way would disappear after disconnecting the selected node.",
                        "The affected way would disappear after disconnecting the selected nodes.",
                        selectedNodes.size()),
                    tr("Warning"),
                    JOptionPane.WARNING_MESSAGE);
            return;
        }


        // Finally, applicableWays contains only one perfect way
        Way selectedWay = applicableWays.get(0);

        // I'm sure there's a better way to handle this
        Main.main.undoRedo.add(new RemoveNodesCommand(selectedWay, selectedNodes));
        Main.map.repaint();
    }

    // Find ways to which the disconnect can be applied. This is the list of ways with more
    // than two nodes which pass through all the given nodes, intersected with the selected ways (if any)
    private List<Way> getApplicableWays(List<Way> selectedWays, List<Node> selectedNodes) {
        if (selectedNodes.isEmpty())
            return null;

        // List of ways shared by all nodes
        List<Way> result = new ArrayList<>(OsmPrimitive.getFilteredList(selectedNodes.get(0).getReferrers(), Way.class));
        for (int i=1; i<selectedNodes.size(); i++) {
            List<OsmPrimitive> ref = selectedNodes.get(i).getReferrers();
            for (Iterator<Way> it = result.iterator(); it.hasNext(); ) {
                if (!ref.contains(it.next())) {
                    it.remove();
                }
            }
        }

        // Remove broken ways
        for (Iterator<Way> it = result.iterator(); it.hasNext(); ) {
            if (it.next().getNodesCount() <= 2) {
                it.remove();
            }
        }

        if (selectedWays.isEmpty())
            return result;
        else {
            // Return only selected ways
            for (Iterator<Way> it = result.iterator(); it.hasNext(); ) {
                if (!selectedWays.contains(it.next())) {
                    it.remove();
                }
            }
            return result;
        }
    }

    @Override
    protected void updateEnabledState() {
        if (getCurrentDataSet() == null) {
            setEnabled(false);
        } else {
            updateEnabledState(getCurrentDataSet().getSelected());
        }
    }

    @Override
    protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) {
        setEnabled(selection != null && !selection.isEmpty());
    }
}
