001package jmri.jmrix.dcc4pc;
002
003import java.util.function.Supplier;
004import javax.annotation.CheckForNull;
005import javax.annotation.Nonnull;
006import jmri.AddressedProgrammer;
007import jmri.AddressedProgrammerManager;
008import jmri.CommandStation;
009import jmri.GlobalProgrammerManager;
010import jmri.managers.DefaultProgrammerManager;
011
012/**
013 * DCC4PC Programmer acts as a proxy for ops mode programming. Another
014 * connection (the one chosen with the connection's "Programmer" option) sends
015 * the Programming on Main packets, and the decoder's RailCom reply comes back
016 * through the RailCom reader; see {@link Dcc4PcOpsModeProgrammer}.
017 * <p>
018 * Only ops mode is provided. Service mode stays with the other connection.
019 *
020 * @see jmri.managers.DefaultProgrammerManager
021 * @author Kevin Dickerson Copyright (C) 2012
022 *
023 */
024public class Dcc4PcProgrammerManager extends DefaultProgrammerManager {
025
026    /**
027     * Finds the other connection's programmer manager, or null if that
028     * connection isn't running (yet). Looked up when needed, so this manager
029     * can exist before the other connection has started.
030     */
031    private final Supplier<AddressedProgrammerManager> manager;
032
033    /** Finds the other connection's command station, which sends POM read packets; null if none. */
034    private final Supplier<CommandStation> commandStation;
035
036    /**
037     * @param manager        finds the other connection's programmer manager
038     * @param commandStation finds the other connection's command station
039     * @param memo           the DCC4PC connection, which gives this manager its name
040     */
041    public Dcc4PcProgrammerManager(@Nonnull Supplier<AddressedProgrammerManager> manager,
042            @Nonnull Supplier<CommandStation> commandStation, @Nonnull Dcc4PcSystemConnectionMemo memo) {
043        super(null, memo);
044        this.manager = manager;
045        this.commandStation = commandStation;
046    }
047
048    /**
049     * @param manager the other connection's programmer manager
050     * @param memo    the DCC4PC connection, which gives this manager its name
051     */
052    public Dcc4PcProgrammerManager(@Nonnull AddressedProgrammerManager manager, @Nonnull Dcc4PcSystemConnectionMemo memo) {
053        this(() -> manager, () -> null, memo);
054    }
055
056    /**
057     * @param <T>     a manager for both modes
058     * @param manager the other connection's programmer manager
059     */
060    public <T extends AddressedProgrammerManager & GlobalProgrammerManager> Dcc4PcProgrammerManager(@Nonnull T manager) {
061        super(null);
062        this.manager = () -> manager;
063        this.commandStation = () -> null;
064    }
065
066    @CheckForNull
067    private AddressedProgrammerManager realManager() {
068        AddressedProgrammerManager m = manager.get();
069        if (m == null) {
070            log.warn("{}: no connection to send Programming on Main packets; check the connection's"
071                    + " Programmer option, or the default Command Station in Preferences, Defaults", getUserName());
072        }
073        return m;
074    }
075
076    /**
077     * {@inheritDoc}
078     * <p>
079     * True until the other connection says otherwise: it may simply not have
080     * started yet.
081     */
082    @Override
083    public boolean isAddressedModePossible() {
084        AddressedProgrammerManager m = manager.get();
085        return m == null || m.isAddressedModePossible();
086    }
087
088    @Override
089    public boolean isGlobalProgrammerAvailable() {
090        return false;
091    }
092
093    @Override
094    protected AddressedProgrammer getConcreteAddressedProgrammer(boolean pLongAddress, int pAddress) {
095        AddressedProgrammerManager m = realManager();
096        return m == null ? null : new Dcc4PcOpsModeProgrammer(pLongAddress, pAddress, m, commandStation.get());
097    }
098
099    @Override
100    protected AddressedProgrammer reserveConcreteAddressedProgrammer(boolean pLongAddress, int pAddress) {
101        // a reserved programmer must read through DCC4PC too
102        return getConcreteAddressedProgrammer(pLongAddress, pAddress);
103    }
104
105    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(Dcc4PcProgrammerManager.class);
106}