001package jmri.jmrix.can.cbus;
002
003import java.util.List;
004import java.util.ResourceBundle;
005
006import javax.annotation.Nonnull;
007
008import jmri.*;
009import jmri.jmrix.can.CanSystemConnectionMemo;
010import jmri.jmrix.can.cbus.simulator.CbusSimulator;
011import jmri.jmrix.can.cbus.node.CbusNodeTableDataModel;
012import jmri.jmrix.can.cbus.eventtable.CbusEventTableDataModel;
013import jmri.jmrix.can.cbus.swing.cbusslotmonitor.CbusSlotMonitorDataModel;
014
015/**
016 * Does configuration for MERG CBUS CAN-based communications implementations.
017 * <hr>
018 * This file is part of JMRI.
019 * <p>
020 * JMRI is free software; you can redistribute it and/or modify it under the
021 * terms of version 2 of the GNU General Public License as published by the Free
022 * Software Foundation. See the "COPYING" file for a copy of this license.
023 * <p>
024 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY
025 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
026 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
027 *
028 * @author Bob Jacobsen Copyright (C) 2009
029 * @author Steve Young Copyright (C) 2022
030 */
031public class CbusConfigurationManager extends jmri.jmrix.can.ConfigurationManager implements Disposable {
032
033    /**
034     * Create a new CbusConfigurationManager.
035     * A Supporting class to configure the {@link jmri.jmrix.can.CanSystemConnectionMemo}
036     * for the {@link jmri.jmrix.can.cbus} classes.
037     * @param memo Connection to configure.
038     */
039    public CbusConfigurationManager(@Nonnull CanSystemConnectionMemo memo) {
040        super(memo);
041        storeToMemoAndInstance(CbusConfigurationManager.this, CbusConfigurationManager.class);
042        cf = new jmri.jmrix.can.cbus.swing.CbusComponentFactory(adapterMemo);
043        InstanceManager.store(cf, jmri.jmrix.swing.ComponentFactory.class);
044    }
045
046    private final jmri.jmrix.swing.ComponentFactory cf;
047
048    // configureManagers() startup order
049    private static final List<Class<?>> DEFAULT_CLASSES = List.of(
050        CbusPreferences.class, 
051        PowerManager.class,
052        SensorManager.class,
053        // SensorManager before TurnoutManager so that listener can be added
054        CommandStation.class,
055        // CommandStation before TurnoutManager so that Raw Turnout Operator available
056        TurnoutManager.class,
057        ThrottleManager.class,
058        CbusPredefinedMeters.class,
059        ReporterManager.class,
060        LightManager.class,
061        CabSignalManager.class,
062        // Clock Control initialised last so CbusSensorManager exists, otherwise
063        // InternalSensorManager is deafult SensorManager when ISCLOCKRUNNING is provided.
064        ClockControl.class);
065
066    /**
067     * {@inheritDoc}
068     */
069    @Override
070    public void configureManagers() {
071
072        for (Class<?> listClass : DEFAULT_CLASSES ) {
073            provide(listClass);
074        }
075
076        // We register a programmer based on whether the hardware is available,
077        // not whether the functionality is available
078        CbusDccProgrammerManager pm = getProgrammerManager();
079        if ( pm !=null ) {
080            if (pm.isAddressedModeHardwareAvailable()) {
081                storeToMemoAndInstance(pm, AddressedProgrammerManager.class);
082            }
083            if (pm.isGlobalProgrammerHardwareAvailable()) {
084                storeToMemoAndInstance(pm, GlobalProgrammerManager.class);
085            }
086        }
087
088        if (getConsistManager() != null) {
089            storeToMemoAndInstance(getConsistManager(), ConsistManager.class);
090        }
091
092        // kick-start cbus sim tools ( Dummy Command Station etc. ) if using loopback connection
093        if ( adapterMemo.getTrafficController() instanceof jmri.jmrix.can.adapters.loopback.LoopbackTrafficController) {
094            adapterMemo.get( CbusSimulator.class);
095        }
096
097    }
098
099    /**
100     * Tells which managers this class provides.
101     * {@inheritDoc}
102     * @param type Class type to check
103     * @return true if supported; false if not
104     */
105    @Override
106    public boolean provides(Class<?> type) {
107        if (adapterMemo.getDisabled()) {
108            return false;
109        } else if (type.equals(AddressedProgrammerManager.class)) {
110            return getProgrammerManager().isAddressedModePossible();
111        } else if (type.equals(GlobalProgrammerManager.class)) {
112            return getProgrammerManager().isGlobalProgrammerAvailable();
113        } else if (type.equals(ConsistManager.class)) {
114            return true;
115        } else if (type.equals(CbusSimulator.class)) {
116            return true;
117        } else if (type.equals(CbusSlotMonitorDataModel.class)) {
118            return true;
119        } else {
120            return DEFAULT_CLASSES.contains(type);
121        }
122    }
123
124    /**
125     * {@inheritDoc}
126     */
127    @SuppressWarnings("unchecked")
128    @Override
129    public <T> T get(Class<?> T) {
130        if (adapterMemo.getDisabled()) {
131            return null;
132        } else if (T.equals(AddressedProgrammerManager.class)
133                && getProgrammerManager().isAddressedModePossible()) {
134            return (T) getProgrammerManager();
135        } else if (T.equals(GlobalProgrammerManager.class)
136                && getProgrammerManager().isGlobalProgrammerAvailable()) {
137            return (T) getProgrammerManager();
138        } else if (T.equals(ConsistManager.class)) {
139            return (T) getConsistManager();
140        } else if (T.equals(CbusSimulator.class)) {
141            return provide(T);
142        } else if (T.equals(CbusSlotMonitorDataModel.class)) {
143            return provide(T);
144        } else if ( DEFAULT_CLASSES.contains(T) ) {
145            return provide(T);
146        }
147        return null; // nothing, by default
148    }
149
150    private CbusDccProgrammerManager programmerManager;
151
152    private CbusDccProgrammerManager getProgrammerManager() {
153        if (programmerManager == null && !adapterMemo.getDisabled()) {
154            programmerManager = new CbusDccProgrammerManager(
155                    new CbusDccProgrammer(adapterMemo), adapterMemo);
156        }
157        return programmerManager;
158    }
159
160    protected CbusConsistManager consistManager = null;
161
162    /**
163     * Get the ConsistManager, creating one if neccessary.
164     * 
165     * Only enable it if we definitely have a command station.
166     * 
167     * @return ConsistManager object
168     */
169    private ConsistManager getConsistManager() {
170        if ( adapterMemo.getDisabled() ) {
171            return null;
172        }
173        if (consistManager == null) {
174            consistManager = new CbusConsistManager(get(CommandStation.class));
175            if (adapterMemo.getProgModeSwitch() == ProgModeSwitch.EITHER) {
176                // Could be either programmer or command station
177                if (getProgrammerManager().isAddressedModePossible()) {
178                    // We have a command station so enable the ConsistManager
179                    consistManager.setEnabled(true);
180                } else {
181                    // Disable for now, may be enabled later if user switches modes, avoid returning a null manager
182                    consistManager.setEnabled(false);
183                }
184            } else {
185                // Command station is always avaliable
186                consistManager.setEnabled(true);
187            }
188        }
189        return consistManager;
190    }
191
192    /**
193     * Provide a new Class instance.
194     * <p>
195     * NOT for general use outside of this class, although public so that
196     * classes like CbusEventTablePane can get a CbusEventTableDataModel
197     * when started.
198     * <p>
199     * If a class is NOT auto-created by the normal get,
200     * it can be provided with this method.
201     * Adds provided class to memo class object map,
202     * AND InstanceManager.
203     * @param <T> class type.
204     * @param T class type.
205     * @return class object, or null if unavailable.
206     */
207    public <T> T provide(@Nonnull Class<?> T){
208        if (adapterMemo.getDisabled()) {
209            return null;
210        }
211        T existing = adapterMemo.getFromMap(T); // if already in object map, use it
212        if ( existing !=null ) {
213            return existing;
214        }
215        if (T.equals(CbusNodeTableDataModel.class)) {
216            storeToMemoAndInstance(new CbusNodeTableDataModel(adapterMemo,10), CbusNodeTableDataModel.class);
217        } else if (T.equals(CbusEventTableDataModel.class)) {
218            storeToMemoAndInstance(new CbusEventTableDataModel(adapterMemo,10), CbusEventTableDataModel.class);
219        } else if (T.equals(CbusPreferences.class)) {
220            storeToMemoAndInstance(new CbusPreferences(), CbusPreferences.class);
221        } else if (T.equals(PowerManager.class)) {
222            storeToMemoAndInstance(new CbusPowerManager(adapterMemo), PowerManager.class);
223        } else if (T.equals(CommandStation.class)) {
224            storeToMemoAndInstance(new CbusCommandStation(adapterMemo), CommandStation.class);
225        } else if (T.equals(ThrottleManager.class)) {
226            storeToMemoAndInstance(new CbusThrottleManager(adapterMemo), ThrottleManager.class);
227        } else if (T.equals(CabSignalManager.class)) {
228            storeToMemoAndInstanceDefault(new CbusCabSignalManager(adapterMemo), CabSignalManager.class);
229        } else if (T.equals(ClockControl.class) ) {
230            storeToMemoAndInstanceDefault(new CbusClockControl(adapterMemo), ClockControl.class);
231        } else if (T.equals(SensorManager.class) ) {
232            adapterMemo.store(new CbusSensorManager(adapterMemo), SensorManager.class);
233            InstanceManager.setSensorManager(adapterMemo.getFromMap(T));
234        } else if (T.equals(TurnoutManager.class) ) {
235            adapterMemo.store(new CbusTurnoutManager(adapterMemo), TurnoutManager.class);
236            InstanceManager.setTurnoutManager(adapterMemo.getFromMap(T));
237        } else if (T.equals(ReporterManager.class) ) {
238            adapterMemo.store(new CbusReporterManager(adapterMemo), ReporterManager.class);
239            InstanceManager.setReporterManager(adapterMemo.getFromMap(T));
240        } else if (T.equals(LightManager.class) ) {
241            adapterMemo.store(new CbusLightManager(adapterMemo), LightManager.class);
242            InstanceManager.setLightManager(adapterMemo.getFromMap(T));
243        } else if (T.equals(CbusPredefinedMeters.class) ) {
244            InstanceManager.setMeterManager(new jmri.managers.AbstractMeterManager(adapterMemo));
245            storeToMemoAndInstance(new CbusPredefinedMeters(adapterMemo), CbusPredefinedMeters.class);
246        }
247        else if (T.equals(CbusSimulator.class)) {
248            storeToMemoAndInstance(new CbusSimulator(adapterMemo), CbusSimulator.class);
249        }
250        else if (T.equals(CbusSlotMonitorDataModel.class)) {
251            storeToMemoAndInstance(new CbusSlotMonitorDataModel(adapterMemo), CbusSlotMonitorDataModel.class);
252        }
253        return adapterMemo.getFromMap(T); // if class not in map, class not provided.
254    }
255
256    private <T> void storeToMemoAndInstance(@Nonnull T item, @Nonnull Class<T> type){
257        adapterMemo.store(item, type); // store with memo
258        InstanceManager.store(item, type); // and with InstanceManager
259    }
260
261    private <T> void storeToMemoAndInstanceDefault(@Nonnull T item, @Nonnull Class<T> type){
262        adapterMemo.store(item, type); // store with memo
263        InstanceManager.setDefault( type, item); // and with InstanceManager
264    }
265
266    public  <T> void disposeOf(@Nonnull T item, @Nonnull Class<T> type ) {
267        InstanceManager.deregister(item, type);
268        adapterMemo.deregister(item, type);
269    }
270
271    /**
272     * {@inheritDoc}
273     */
274    @Override
275    public void dispose() {
276
277        // classed stored in the memo classObjectMap will be deregisted from
278        // InstanceManager on memo disposal, and will also have their 
279        // dispose method called if they implement jmri.Disposable.
280        
281        InstanceManager.deregister(cf, jmri.jmrix.swing.ComponentFactory.class);
282
283        if (consistManager != null) {
284            InstanceManager.deregister(consistManager, ConsistManager.class);
285        }
286        if (programmerManager != null) {
287            programmerManager.dispose();
288        }
289        InstanceManager.deregister(this, CbusConfigurationManager.class);
290    }
291
292    /**
293     * {@inheritDoc}
294     */
295    @Override
296    protected ResourceBundle getActionModelResourceBundle() {
297        return ResourceBundle.getBundle("jmri.jmrix.can.CanActionListBundle");
298    }
299
300    // private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CbusConfigurationManager.class);
301
302}