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 CbusReporterManager reporterManager;
151    
152    private CbusDccProgrammerManager programmerManager;
153
154    private CbusDccProgrammerManager getProgrammerManager() {
155        if (programmerManager == null && !adapterMemo.getDisabled()) {
156            programmerManager = new CbusDccProgrammerManager(
157                    new CbusDccProgrammer(adapterMemo), adapterMemo);
158        }
159        return programmerManager;
160    }
161
162    protected CbusConsistManager consistManager = null;
163
164    /**
165     * Get the ConsistManager, creating one if neccessary.
166     * 
167     * Only enable it if we definitely have a command station.
168     * 
169     * @return ConsistManager object
170     */
171    private ConsistManager getConsistManager() {
172        if ( adapterMemo.getDisabled() ) {
173            return null;
174        }
175        if (consistManager == null) {
176            consistManager = new CbusConsistManager(get(CommandStation.class));
177            if (adapterMemo.getProgModeSwitch() == ProgModeSwitch.EITHER) {
178                // Could be either programmer or command station
179                if (getProgrammerManager().isAddressedModePossible()) {
180                    // We have a command station so enable the ConsistManager
181                    consistManager.setEnabled(true);
182                } else {
183                    // Disable for now, may be enabled later if user switches modes, avoid returning a null manager
184                    consistManager.setEnabled(false);
185                }
186            } else {
187                // Command station is always avaliable
188                consistManager.setEnabled(true);
189            }
190        }
191        return consistManager;
192    }
193
194    /**
195     * Provide a new Class instance.
196     * <p>
197     * NOT for general use outside of this class, although public so that
198     * classes like CbusEventTablePane can get a CbusEventTableDataModel
199     * when started.
200     * <p>
201     * If a class is NOT auto-created by the normal get,
202     * it can be provided with this method.
203     * Adds provided class to memo class object map,
204     * AND InstanceManager.
205     * @param <T> class type.
206     * @param T class type.
207     * @return class object, or null if unavailable.
208     */
209    public <T> T provide(@Nonnull Class<?> T){
210        if (adapterMemo.getDisabled()) {
211            return null;
212        }
213        T existing = adapterMemo.getFromMap(T); // if already in object map, use it
214        if ( existing !=null ) {
215            return existing;
216        }
217        if (T.equals(CbusNodeTableDataModel.class)) {
218            storeToMemoAndInstance(new CbusNodeTableDataModel(adapterMemo,10), CbusNodeTableDataModel.class);
219        } else if (T.equals(CbusEventTableDataModel.class)) {
220            storeToMemoAndInstance(new CbusEventTableDataModel(adapterMemo,10), CbusEventTableDataModel.class);
221        } else if (T.equals(CbusPreferences.class)) {
222            storeToMemoAndInstance(new CbusPreferences(), CbusPreferences.class);
223        } else if (T.equals(PowerManager.class)) {
224            storeToMemoAndInstance(new CbusPowerManager(adapterMemo), PowerManager.class);
225        } else if (T.equals(CommandStation.class)) {
226            storeToMemoAndInstance(new CbusCommandStation(adapterMemo), CommandStation.class);
227        } else if (T.equals(ThrottleManager.class)) {
228            storeToMemoAndInstance(new CbusThrottleManager(adapterMemo), ThrottleManager.class);
229        } else if (T.equals(CabSignalManager.class)) {
230            storeToMemoAndInstanceDefault(new CbusCabSignalManager(adapterMemo), CabSignalManager.class);
231        } else if (T.equals(ClockControl.class) ) {
232            storeToMemoAndInstanceDefault(new CbusClockControl(adapterMemo), ClockControl.class);
233        } else if (T.equals(SensorManager.class) ) {
234            adapterMemo.store(new CbusSensorManager(adapterMemo), SensorManager.class);
235            InstanceManager.setSensorManager(adapterMemo.getFromMap(T));
236        } else if (T.equals(TurnoutManager.class) ) {
237            adapterMemo.store(new CbusTurnoutManager(adapterMemo), TurnoutManager.class);
238            InstanceManager.setTurnoutManager(adapterMemo.getFromMap(T));
239        } else if (T.equals(ReporterManager.class) ) {
240            adapterMemo.store(reporterManager = new CbusReporterManager(adapterMemo), ReporterManager.class);
241            InstanceManager.setReporterManager(adapterMemo.getFromMap(T));
242            
243        } else if (T.equals(LightManager.class) ) {
244            adapterMemo.store(new CbusLightManager(adapterMemo), LightManager.class);
245            InstanceManager.setLightManager(adapterMemo.getFromMap(T));
246        } else if (T.equals(CbusPredefinedMeters.class) ) {
247            InstanceManager.setMeterManager(new jmri.managers.AbstractMeterManager(adapterMemo));
248            storeToMemoAndInstance(new CbusPredefinedMeters(adapterMemo), CbusPredefinedMeters.class);
249        }
250        else if (T.equals(CbusSimulator.class)) {
251            storeToMemoAndInstance(new CbusSimulator(adapterMemo), CbusSimulator.class);
252        }
253        else if (T.equals(CbusSlotMonitorDataModel.class)) {
254            storeToMemoAndInstance(new CbusSlotMonitorDataModel(adapterMemo), CbusSlotMonitorDataModel.class);
255        }
256        return adapterMemo.getFromMap(T); // if class not in map, class not provided.
257    }
258
259    private <T> void storeToMemoAndInstance(@Nonnull T item, @Nonnull Class<T> type){
260        adapterMemo.store(item, type); // store with memo
261        InstanceManager.store(item, type); // and with InstanceManager
262    }
263
264    private <T> void storeToMemoAndInstanceDefault(@Nonnull T item, @Nonnull Class<T> type){
265        adapterMemo.store(item, type); // store with memo
266        InstanceManager.setDefault( type, item); // and with InstanceManager
267    }
268
269    public  <T> void disposeOf(@Nonnull T item, @Nonnull Class<T> type ) {
270        InstanceManager.deregister(item, type);
271        adapterMemo.deregister(item, type);
272    }
273
274    /**
275     * {@inheritDoc}
276     */
277    @Override
278    public void dispose() {
279
280        // classed stored in the memo classObjectMap will be deregisted from
281        // InstanceManager on memo disposal, and will also have their 
282        // dispose method called if they implement jmri.Disposable.
283        
284        InstanceManager.deregister(cf, jmri.jmrix.swing.ComponentFactory.class);
285
286        if (consistManager != null) {
287            InstanceManager.deregister(consistManager, ConsistManager.class);
288        }
289        if (programmerManager != null) {
290            programmerManager.dispose();
291        }
292        if (reporterManager != null) {
293            reporterManager.dispose();
294        }
295                
296        InstanceManager.deregister(this, CbusConfigurationManager.class);
297    }
298
299    /**
300     * {@inheritDoc}
301     */
302    @Override
303    protected ResourceBundle getActionModelResourceBundle() {
304        return ResourceBundle.getBundle("jmri.jmrix.can.CanActionListBundle");
305    }
306
307    // private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CbusConfigurationManager.class);
308
309}