001package jmri.jmrit.throttle;
002
003import java.util.*;
004
005import javax.annotation.CheckForNull;
006import javax.swing.JFrame;
007
008import jmri.DccLocoAddress;
009import jmri.InstanceManagerAutoDefault;
010import jmri.util.JmriJFrame;
011
012import org.jdom2.Element;
013
014/**
015 * Interface for allocating and deallocating throttles frames. Not to be
016 * confused with ThrottleManager.
017 *
018 * @author Glen Oberhauser
019 */
020public class ThrottleFrameManager implements InstanceManagerAutoDefault, ThrottleControllersUIContainersManager {
021
022    private int activeFrame;
023    private int frameCounterID = 0; // to generate unique names for each card    
024
025    private ArrayList<ThrottleControllersUIContainer> throttleWindows; // synchronized access
026
027    private ThrottlesPreferencesWindow throttlePreferencesFrame;
028    private JmriJFrame throttlesListFrame;
029    private ThrottlesListPanel throttlesListPanel;
030
031    /**
032     * Constructor for the ThrottleFrameManager object.
033     */
034    public ThrottleFrameManager() {
035        throttleWindows = new ArrayList<>(0);
036    }
037
038    /**
039     * Tell this manager that a new ThrottleWindow was created.
040     *
041     * @return The newly created ThrottleWindow
042     */
043    public ThrottleWindow createThrottleWindow() {
044        return createThrottleWindow((jmri.jmrix.ConnectionConfig) null);
045    }
046
047    /**
048     * Tell this manager that a new ThrottleWindow was created.
049     *
050     * @param connectionConfig the connection config
051     * @return The newly created ThrottleWindow
052     */
053    public ThrottleWindow createThrottleWindow(jmri.jmrix.ConnectionConfig connectionConfig) {
054        ThrottleWindow tw = new ThrottleWindow(connectionConfig);
055        tw.pack();
056        synchronized (this) {
057            throttleWindows.add(tw);
058            activeFrame = throttleWindows.indexOf(tw);
059        }
060        getThrottlesListPanel().getTableModel().fireTableStructureChanged();
061        return tw;
062    }
063
064    /**
065     * Tell this manager that a new ThrottleWindow was created.
066     *
067     * @param e the xml element for the throttle window
068     * @return The newly created ThrottleWindow
069     */
070    public ThrottleWindow createThrottleWindow(Element e) {
071        ThrottleWindow tw = ThrottleWindow.createThrottleWindow(e);
072        tw.pack();
073        synchronized (this) {
074            throttleWindows.add(tw);
075            activeFrame = throttleWindows.indexOf(tw);
076        }
077        getThrottlesListPanel().getTableModel().fireTableStructureChanged();
078        return tw;
079    }
080
081    /**
082     * Tell this manager that a new ThrottleFrame was created.
083     *
084     * @return The newly created ThrottleFrame
085     */
086    public ThrottleFrame createThrottleFrame() {
087        return createThrottleFrame(null);
088    }
089    
090    @Override
091    public ThrottleControllerUI createThrottleController() {
092        return createThrottleFrame();
093    }
094
095    /**
096     * Tell this manager that a new ThrottleFrame was created.
097     *
098     * @param connectionConfig the connection config
099     * @return The newly created ThrottleFrame
100     */
101    public ThrottleFrame createThrottleFrame(jmri.jmrix.ConnectionConfig connectionConfig) {
102        return createThrottleWindow(connectionConfig).getCurrentThrottleFrame();
103    }
104
105    /**
106     * Request that this manager destroy a throttle frame.
107     *
108     * @param frame The to-be-destroyed ThrottleFrame
109     */
110    public void requestThrottleWindowDestruction(ThrottleWindow frame) {
111        if (frame != null) {
112            destroyThrottleWindow(frame);
113            synchronized (this) {
114                throttleWindows.remove(frame);
115                if (!throttleWindows.isEmpty()) {
116                    requestFocusForNextThrottleWindow();
117                }
118            }
119        }
120        getThrottlesListPanel().getTableModel().fireTableStructureChanged();        
121    }
122
123    public synchronized void requestAllThrottleWindowsDestroyed() {
124        for (ThrottleControllersUIContainer frame : throttleWindows) {
125            destroyThrottleWindow((ThrottleWindow)frame);
126        }
127        throttleWindows = new ArrayList<>(0);
128        getThrottlesListPanel().getTableModel().fireTableStructureChanged();        
129    }
130    
131    public int generateUniqueFrameID() {
132         return frameCounterID++;
133    }
134
135    /**
136     * Perform the destruction of a ThrottleFrame. This method will not affect
137     * the throttleFrames list, thus ensuring no synchronozation problems.
138     *
139     * @param window The ThrottleFrame to be destroyed.
140     */
141    private void destroyThrottleWindow(ThrottleWindow window) {
142        throttleWindows.remove(window);
143        window.dispose();        
144        getThrottlesListPanel().getTableModel().fireTableStructureChanged();        
145    }
146
147    @Override
148    public Iterator<ThrottleControllersUIContainer> iterator() {
149        return throttleWindows.iterator();
150    }
151       
152    /**
153     * Return the number of active thottle windows.
154     *
155     * @return the number of active thottle window.
156     */
157    @Override
158    public synchronized int getNbThrottleControllersContainers() {
159        return throttleWindows.size();
160    }
161    
162    @Override
163    public synchronized ThrottleControllersUIContainer getThrottleControllersContainerAt(int n) {
164        if (! (n < throttleWindows.size())) {
165            return null;
166        }
167        return throttleWindows.get(n);
168    }
169
170    public synchronized void requestFocusForNextThrottleWindow() {
171        activeFrame = (activeFrame + 1) % throttleWindows.size();
172        ThrottleWindow tw = (ThrottleWindow) throttleWindows.get(activeFrame);
173        tw.requestFocus();
174        tw.toFront();
175    }
176
177    public synchronized void requestFocusForPreviousThrottleWindow() {
178        activeFrame--;
179        if (activeFrame < 0) {
180            activeFrame = throttleWindows.size() - 1;
181        }
182        ThrottleWindow tw =(ThrottleWindow) throttleWindows.get(activeFrame);
183        tw.requestFocus();
184        tw.toFront();
185    }
186
187    public synchronized ThrottleWindow getCurrentThrottleFrame() {
188        if (throttleWindows == null) {
189            return null;
190        }
191        if (throttleWindows.isEmpty()) {
192            return null;
193        }
194        return (ThrottleWindow) throttleWindows.get(activeFrame);
195    }
196
197    public ThrottlesListPanel getThrottlesListPanel() {
198        if (throttlesListPanel == null) {
199            throttlesListPanel = new ThrottlesListPanel();
200        }
201        return throttlesListPanel;
202    }
203
204    /*
205     * Show JMRI native throttle list window
206     *
207     */
208    public void showThrottlesList() {
209        if (throttlesListFrame == null) {            
210            throttlesListFrame = new JmriJFrame(Bundle.getMessage("ThrottleListFrameTile"));        
211            throttlesListFrame.setContentPane(getThrottlesListPanel());
212            throttlesListFrame.pack();            
213        }
214        throttlesListFrame.setVisible(true);
215    }
216
217    /*
218     * Show throttle preferences window
219     *
220     */
221    public void showThrottlesPreferences() {
222        if (throttlePreferencesFrame == null) {
223            throttlePreferencesFrame = new ThrottlesPreferencesWindow(Bundle.getMessage("ThrottlePreferencesFrameTitle"));
224            throttlePreferencesFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
225            throttlePreferencesFrame.pack();
226        } else {
227            throttlePreferencesFrame.resetComponents();
228            throttlePreferencesFrame.revalidate();
229        }
230        throttlePreferencesFrame.setVisible(true);
231        throttlePreferencesFrame.requestFocus();
232    }
233
234    /*
235     * Apply curent throttle preferences to all throttle windows
236     *
237     */
238    public void applyPreferences() {
239        throttleWindows.forEach(tw -> {
240            ((ThrottleWindow)tw).applyPreferences();
241        });
242        getThrottlesListPanel().applyPreferences();
243    }
244
245    /**
246     * Force emergency stop of all managed throttles windows
247     *
248     */   
249    public void emergencyStopAll() {
250        throttleWindows.forEach(tw -> {
251            tw.emergencyStopAll();
252        });
253    }
254    
255    /**
256     * Get the number of usages of a particular Loco Address.
257     * @param la the Loco Address, can be null.
258     * @return 0 if no usages, else number of AddressPanel usages.
259     */
260    @Override
261    public int getNumberOfEntriesFor(@CheckForNull DccLocoAddress la) {
262        if (la == null) { 
263            return 0; 
264        }
265        int ret = 0;
266        for (ThrottleControllersUIContainer tw : throttleWindows) {        
267            ret += tw.getNumberOfEntriesFor(la);
268        }
269        return ret;
270    }
271    
272    // private final static Logger log = LoggerFactory.getLogger(ThrottleFrameManager.class);
273}