package com.revolsys.swing.map.layer.record.component;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.WindowConstants;
import org.jdesktop.swingx.VerticalLayout;
import com.revolsys.collection.CollectionUtil;
import com.revolsys.datatype.DataType;
import com.revolsys.datatype.DataTypes;
import com.revolsys.geometry.graph.Edge;
import com.revolsys.geometry.graph.Node;
import com.revolsys.geometry.graph.RecordGraph;
import com.revolsys.geometry.model.Geometry;
import com.revolsys.geometry.model.LineString;
import com.revolsys.logging.Logs;
import com.revolsys.record.ArrayRecord;
import com.revolsys.record.Record;
import com.revolsys.swing.Panels;
import com.revolsys.swing.SwingUtil;
import com.revolsys.swing.action.RunnableAction;
import com.revolsys.swing.component.BasePanel;
import com.revolsys.swing.map.layer.record.AbstractRecordLayer;
import com.revolsys.swing.map.layer.record.LayerRecord;
import com.revolsys.swing.map.layer.record.table.model.MergedRecordsTableModel;
import com.revolsys.swing.parallel.Invoke;
import com.revolsys.swing.table.TablePanel;
import com.revolsys.swing.table.record.model.RecordListTableModel;
import com.revolsys.swing.undo.CreateRecordUndo;
import com.revolsys.swing.undo.DeleteLayerRecordUndo;
import com.revolsys.swing.undo.MultipleUndo;
import com.revolsys.swing.undo.UndoManager;
import com.revolsys.util.Property;
public class MergeRecordsDialog extends JDialog implements WindowListener {
private static final long serialVersionUID = 1L;
public static void showDialog(final AbstractRecordLayer layer) {
final UndoManager undoManager = layer.getMapPanel().getUndoManager();
final MergeRecordsDialog dialog = new MergeRecordsDialog(undoManager, layer);
dialog.showDialog();
}
private final AbstractRecordLayer layer;
private final Map<Record, LayerRecord> mergeableToOiginalRecordMap = new HashMap<>();
private JPanel mergedRecordsPanel;
private Map<Record, Set<LayerRecord>> mergedRecords = Collections.emptyMap();
private JButton okButton;
private final Set<LayerRecord> replacedOriginalRecords = new LinkedHashSet<>();
private final UndoManager undoManager;
public MergeRecordsDialog(final UndoManager undoManager, final AbstractRecordLayer layer) {
super(SwingUtil.getActiveWindow(), "Merge " + layer.getName(), ModalityType.APPLICATION_MODAL);
this.undoManager = undoManager;
this.layer = layer;
initDialog();
}
public void cancel() {
if (isVisible()) {
SwingUtil.dispose(this);
}
}
public void finish() {
final MultipleUndo multipleUndo = new MultipleUndo();
for (final Record mergedRecord : this.mergedRecords.keySet()) {
final CreateRecordUndo createRecordUndo = new CreateRecordUndo(this.layer, mergedRecord);
multipleUndo.addEdit(createRecordUndo);
}
for (final LayerRecord record : this.replacedOriginalRecords) {
final DeleteLayerRecordUndo deleteRecordUndo = new DeleteLayerRecordUndo(record);
multipleUndo.addEdit(deleteRecordUndo);
}
if (this.undoManager == null) {
multipleUndo.redo();
} else {
this.undoManager.addEdit(multipleUndo);
}
setVisible(false);
}
protected void initDialog() {
setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
setMinimumSize(new Dimension(600, 100));
addWindowListener(this);
final BasePanel panel = new BasePanel(new BorderLayout());
add(new JScrollPane(panel, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED));
this.mergedRecordsPanel = new JPanel(new VerticalLayout());
panel.add(this.mergedRecordsPanel, BorderLayout.CENTER);
final JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
add(buttonsPanel, BorderLayout.SOUTH);
final JButton cancelButton = RunnableAction.newButton("Cancel", this::cancel);
buttonsPanel.add(cancelButton);
this.okButton = RunnableAction.newButton("OK", this::finish);
this.okButton.setEnabled(false);
buttonsPanel.add(this.okButton);
pack();
SwingUtil.autoAdjustPosition(this);
}
protected void replaceRecord(final Record mergedRecord, final Record record) {
if (mergedRecord != record) {
final LayerRecord originalRecord = this.mergeableToOiginalRecordMap.remove(record);
if (originalRecord != null) {
this.replacedOriginalRecords.add(originalRecord);
}
}
}
public void run() {
try {
final List<LayerRecord> originalRecords = this.layer.getMergeableSelectedRecords();
String errorMessage = "";
final DataType geometryType = this.layer.getGeometryType();
this.mergedRecords = new HashMap<>();
if (originalRecords.size() < 2) {
errorMessage = " at least two records must be selected to merge.";
} else if (!DataTypes.LINE_STRING.equals(geometryType)
&& !DataTypes.MULTI_LINE_STRING.equals(geometryType)) {
errorMessage = "Merging " + geometryType + " not currently supported";
} else {
final RecordGraph graph = new RecordGraph();
for (final LayerRecord originalRecord : originalRecords) {
Geometry geometry = originalRecord.getGeometry();
if (geometry != null && !geometry.isEmpty()) {
geometry = this.layer.getGeometryFactory().geometry(LineString.class, geometry);
if (geometry instanceof LineString) {
final Record mergeableRecord = new ArrayRecord(originalRecord);
mergeableRecord.setGeometryValue(geometry);
this.mergeableToOiginalRecordMap.put(mergeableRecord, originalRecord);
graph.addEdge(mergeableRecord);
}
}
}
for (final Node<Record> node : graph.nodes()) {
if (node != null) {
final List<Edge<Record>> edges = node.getEdges();
if (edges.size() == 2) {
final Edge<Record> edge1 = edges.get(0);
final Record record1 = edge1.getObject();
final Edge<Record> edge2 = edges.get(1);
final Record record2 = edge2.getObject();
if (record1 != record2) {
final Record mergedRecord = this.layer.getMergedRecord(node, record1, record2);
graph.addEdge(mergedRecord);
edge1.remove();
edge2.remove();
final Set<LayerRecord> sourceRecords = new LinkedHashSet<>();
// TODO verify orientation to ensure they are in the correct
// order
// and see if they are reversed
CollectionUtil.addIfNotNull(sourceRecords,
this.mergeableToOiginalRecordMap.get(record1));
CollectionUtil.addAllIfNotNull(sourceRecords, this.mergedRecords.remove(record1));
CollectionUtil.addIfNotNull(sourceRecords,
this.mergeableToOiginalRecordMap.get(record2));
CollectionUtil.addAllIfNotNull(sourceRecords, this.mergedRecords.remove(record2));
this.mergedRecords.put(mergedRecord, sourceRecords);
replaceRecord(mergedRecord, record1);
replaceRecord(mergedRecord, record2);
}
}
}
}
}
final String message = errorMessage;
Invoke.later(() -> setMergedRecords(message, this.mergedRecords));
} catch (final Throwable e) {
Logs.error(this, "Error " + this, e);
}
}
public void setMergedRecord(final int i, final Record mergedObject,
final Collection<LayerRecord> objects) {
this.okButton.setEnabled(true);
final TablePanel tablePanel = MergedRecordsTableModel.newPanel(this.layer, mergedObject,
objects);
final JPanel panel = Panels
.titledTransparentVerticalLayout("Merged " + objects.size() + " Records");
panel.add(tablePanel);
this.mergedRecordsPanel.add(panel);
}
public void setMergedRecords(String errorMessage,
final Map<Record, Set<LayerRecord>> mergedRecords) {
final Set<Record> unMergeableRecords = new HashSet<>(this.mergeableToOiginalRecordMap.keySet());
unMergeableRecords.removeAll(mergedRecords.keySet());
if (!mergedRecords.isEmpty()) {
int i = 0;
for (final Entry<Record, Set<LayerRecord>> mergedEntry : mergedRecords.entrySet()) {
final Record mergedObject = mergedEntry.getKey();
final Set<LayerRecord> originalObjects = mergedEntry.getValue();
setMergedRecord(i, mergedObject, originalObjects);
i++;
}
}
if (!unMergeableRecords.isEmpty() || Property.hasValue(errorMessage)) {
final Set<LayerRecord> records = new LinkedHashSet<>();
for (final Record record : unMergeableRecords) {
final LayerRecord originalRecord = this.mergeableToOiginalRecordMap.get(record);
if (originalRecord != null) {
records.add(originalRecord);
}
}
final TablePanel tablePanel = RecordListTableModel.newPanel(this.layer, records);
final RecordListTableModel tableModel = tablePanel.getTableModel();
tableModel.setEditable(false);
tablePanel.setPreferredSize(new Dimension(100, 50 + unMergeableRecords.size() * 22));
final JPanel panel = Panels
.titledTransparentBorderLayout(unMergeableRecords.size() + " Un-Mergeable Records");
if (!Property.hasValue(errorMessage)) {
errorMessage = "The following records could not be merged and will not be modified.";
}
final JLabel unMergeLabel = new JLabel(
"<html><p style=\"color:red\">" + errorMessage + "</p></html>");
panel.add(unMergeLabel, BorderLayout.NORTH);
panel.add(tablePanel, BorderLayout.SOUTH);
this.mergedRecordsPanel.add(panel);
}
SwingUtil.autoAdjustPosition(this);
setVisible(true);
}
private void showDialog() {
Invoke.background(toString(), this::run);
}
@Override
public String toString() {
return getTitle();
}
@Override
public void windowActivated(final WindowEvent e) {
}
@Override
public void windowClosed(final WindowEvent e) {
}
@Override
public void windowClosing(final WindowEvent e) {
cancel();
}
@Override
public void windowDeactivated(final WindowEvent e) {
}
@Override
public void windowDeiconified(final WindowEvent e) {
}
@Override
public void windowIconified(final WindowEvent e) {
}
@Override
public void windowOpened(final WindowEvent e) {
}
}