001package jmri.jmrix.bidib.netbidib;
002
003import java.awt.event.WindowAdapter;
004import java.awt.event.WindowEvent;
005import java.awt.event.ActionListener;
006import java.awt.event.ActionEvent;
007
008import javax.swing.JOptionPane;
009import javax.swing.JDialog;
010//import javax.swing.Timer;
011import java.beans.PropertyChangeListener;
012import java.beans.PropertyChangeEvent;
013import jmri.jmrix.bidib.BiDiBPortController;
014
015import org.bidib.jbidibc.messages.helpers.Context;
016import org.bidib.jbidibc.messages.utils.ByteUtils;
017import org.bidib.jbidibc.netbidib.NetBidibContextKeys;
018import org.slf4j.Logger;
019import org.slf4j.LoggerFactory;
020
021/**
022 * This dialog is presented to the user if netBiDiB pairing is required.
023 * It informs the user about the remote device and waits until the pairing is
024 * accepted on the remote side. In this case, the dialog should be removed by the
025 * calling program. If the request timed out, is is closed an the calling program
026 * is informed by an ActionListener.
027 * 
028 * @author Eckart Meyer Copyright (C) 2024
029 */
030
031public class NetBiDiBPairingRequestDialog {
032    
033    //private final java.util.ResourceBundle rb = java.util.ResourceBundle.getBundle("jmri.jmrix.bidib.BiDiBBundle"); // NOI18N
034
035    private final Context context;
036    private final BiDiBPortController portController;
037    private final Long uniqueId;
038    private Integer remainingTimeout;
039    private JOptionPane optionPane;
040    private final JDialog dialog = new JDialog();
041    private final ActionListener actionListener;
042
043    private final javax.swing.Timer updateTimer = new javax.swing.Timer(1000, e -> updateDialog());
044    
045    /**
046     * Constructor.
047     * 
048     * @param context to transfer several parameters to show on the dialog
049     * @param portController to transfer the port hostname and port number
050     * @param listener listing to the cancel event
051     */
052    public NetBiDiBPairingRequestDialog(Context context, BiDiBPortController portController, ActionListener listener) {
053        this.context = context;
054        this.portController = portController;
055        actionListener = listener;
056        uniqueId = context.get(NetBidibContextKeys.KEY_DESCRIPTOR_UID, Long.class, null);
057        remainingTimeout = context.get(NetBidibContextKeys.KEY_PAIRING_TIMEOUT, Integer.class, Integer.valueOf(30));
058    }
059    
060
061    /**
062     * Show the dialog, setup listeners for the button and for the window close event.
063     * Immediately return, this is not a modal dialog.
064     */
065    public void show() {
066        
067
068        String[] options = new String[1];
069        options[0] = Bundle.getMessage("netBiDiBPairingDialogCancel"); // NOI18N
070        String title = Bundle.getMessage("netBiDiBPairingDialogTitle") ;  // NOI18N
071
072        optionPane = new JOptionPane(makeMessageText(), JOptionPane.DEFAULT_OPTION,JOptionPane.INFORMATION_MESSAGE, null, options, null);
073        
074        optionPane.addPropertyChangeListener(new PropertyChangeListener() {
075                
076            @Override
077            public void propertyChange(PropertyChangeEvent e) {
078                String prop = e.getPropertyName();
079                //log.trace("property change event: {}", e);
080                //log.trace("propertychange: name: {}, old value: {}, new value: {}", prop, e.getOldValue(), e.getNewValue());
081
082                if (dialog.isVisible() 
083                 && (e.getSource() == optionPane)
084                 && (prop.equals(JOptionPane.VALUE_PROPERTY))) {
085                    log.trace("propertychange new value: {}", e.getNewValue());
086                    onCancel();
087                }
088            }
089        });
090        
091        
092        
093        dialog.setTitle(title);
094        dialog.setModal(false); //true: setVisible() would not return until dispose()
095        dialog.setContentPane(optionPane);
096        dialog.setLocationRelativeTo(null); // Centers on the screen
097        dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); //would inhibit closing the window from the user
098        dialog.addWindowListener(new WindowAdapter() {
099
100            @Override
101            public void windowClosing(WindowEvent we) {
102                log.debug("user closes window.");
103                onCancel();
104            }
105        });
106
107        updateTimer.setRepeats(true);  // 1s tick
108
109        //  timer removes dialog after iDelayInSeconds
110        updateTimer.start();
111
112
113        dialog.pack();
114        dialog.setVisible(true);
115    }
116    
117    /**
118     * Stop timer and remove dialog
119     */
120    public void dispose() {
121        updateTimer.stop();
122        dialog.dispose();
123    }
124    
125    /**
126     * Stop timer and hide dialog
127     */
128    public void hide() {
129        updateTimer.stop();
130        dialog.setVisible(false);
131    }
132    
133    /**
134     * Call the listener on the cancel event
135     */
136    private void onCancel() {
137        log.info("User cancelled netBiDiB pairing");
138        dispose();
139        if (actionListener != null) {
140            actionListener.actionPerformed(new ActionEvent(this, 0, "Cancel"));
141        }
142    }
143    
144    /**
145     * Update the dialog message.
146     * The text changes every second to display the remaining time.
147     */
148    private void updateDialog() {
149        remainingTimeout -= 1;
150        if (remainingTimeout > 0) {
151            optionPane.setMessage(makeMessageText());
152        }
153        else {
154            dispose();
155        }
156    }
157
158    /**
159     * Setup the text to be presented to the user.
160     * The data is taken from the Context given by the calling program.
161     * 
162     * @return the complete string
163     */
164    private String makeMessageText() {
165        String remoteLinkData = Bundle.getMessage("netBiDiBPairingDialogText") + "\n";
166        remoteLinkData += "\n" + Bundle.getMessage("netBiDiBPairingDialogTime") + ": " + remainingTimeout.toString() + Bundle.getMessage("netBiDiBPairingDialogSecondsShort");
167        remoteLinkData += "\n" + Bundle.getMessage("netBiDiBPairingDialogProdName") + ": " + context.get(NetBidibContextKeys.KEY_DESCRIPTOR_PROD_STRING, String.class, "");
168        remoteLinkData += "\n" + Bundle.getMessage("netBiDiBPairingDialogUserName") + ": " + context.get(NetBidibContextKeys.KEY_DESCRIPTOR_USER_STRING, String.class, "");
169        remoteLinkData += "\n" + Bundle.getMessage("netBiDiBPairingDialogPortName") + ": " + portController.getRealPortName();
170        remoteLinkData += "\n" + Bundle.getMessage("netBiDiBPairingDialogUniqueID") + ": " + ByteUtils.getUniqueIdAsString(uniqueId);
171        
172        return remoteLinkData;
173    }
174    
175    private final static Logger log = LoggerFactory.getLogger(NetBiDiBPairingRequestDialog.class);
176
177}