001package jmri.jmrix.bidib;
002
003import java.util.ArrayList;
004import java.util.Locale;
005import javax.annotation.Nonnull;
006import jmri.JmriException;
007import jmri.Turnout;
008import org.slf4j.Logger;
009import org.slf4j.LoggerFactory;
010
011/**
012 * Implement turnout manager for BiDiB systems.
013 * <p>
014 * System names are "BTnnn", where B is the user configurable system prefix,
015 * nnn is the turnout number without padding.
016 *
017 * @author Bob Jacobsen Copyright (C) 2001
018 * @author Eckart Meyer Copyright (C) 2019-2023
019 */
020public class BiDiBTurnoutManager extends jmri.managers.AbstractTurnoutManager {// implements EasyDccListener {
021
022    // Whether we accumulate partially loaded turnouts in pendingTurnouts.
023    private boolean isLoading = false;
024    // Turnouts that are being loaded from XML.
025    private final ArrayList<BiDiBTurnout> pendingTurnouts = new ArrayList<>();
026
027
028    /**
029     * Create an new BiDiB TurnoutManager.
030     *
031     * @param memo the SystemConnectionMemo for this connection (contains the prefix string needed to parse names)
032     */
033    public BiDiBTurnoutManager(BiDiBSystemConnectionMemo memo) {
034        super(memo);
035    }
036
037    /**
038     * {@inheritDoc}
039     */
040    @Override
041    public BiDiBSystemConnectionMemo getMemo() {
042        return (BiDiBSystemConnectionMemo) memo;
043    }
044
045    /**
046     * Create a new Turnout based on the system name.
047     * Assumes calling method has checked that a Turnout with this
048     * system name does not already exist.
049     *
050     * @return null if the system name is not in a valid format.
051     */
052    @Override
053    public Turnout createNewTurnout(String systemName, String userName) {
054        log.trace("createNewTurnout {} - {}", systemName, userName);
055        //String addr = systemName.substring(getSystemPrefix().length() + 1);
056        // first, check validity
057        try {
058            validateSystemNameFormat(systemName);
059        } catch (IllegalArgumentException e) {
060            log.error("failed to validate:", e);
061            throw e;
062        }
063        
064        
065        BiDiBTurnout t;
066        t = new BiDiBTurnout(systemName, this);
067        t.setUserName(userName);
068
069        synchronized (pendingTurnouts) {
070            if (isLoading) {
071                pendingTurnouts.add(t);
072            } else {
073                t.finishLoad();
074            }
075        }
076
077        return t;
078        //return null;
079    }
080
081    /**
082     * This function is invoked before an XML load is started. We defer initialization of the
083     * newly created turnouts until finishLoad because the type might be changing as we
084     * are parsing the XML.
085     */
086    public void startLoad() {
087        synchronized (pendingTurnouts) {
088            isLoading = true;
089        }
090    }
091
092    /**
093     * This function is invoked after the XML load is complete and all turnouts are instantiated
094     * and their config is read in. We use this hook to finalize the construction of the
095     * objects whose instantiation was deferred until the feedback type was known.
096     */
097    public void finishLoad() {
098        log.info("Turnout manager : finish load");
099        synchronized (pendingTurnouts) {
100            pendingTurnouts.forEach((t) -> {
101                t.finishLoad();
102            });
103            pendingTurnouts.clear();
104            isLoading = false;
105        }
106    }
107
108    /** {@inheritDoc} */
109    @Override
110    public String createSystemName(@Nonnull String curAddress, @Nonnull String prefix) throws JmriException { //TODO some validation? Throw exception then? - see parent class
111        log.trace("createSystemName from {} - {}", curAddress, prefix);
112        try {
113            int i = 1;
114            int curNum = Integer.parseInt(curAddress);
115            for (Turnout t : getNamedBeanSet()) {
116                //log.trace("turnout: {}/{} {}", i, curNum, t.getSystemName());
117                if (i++ == curNum) {
118                    return t.getSystemName();
119                }
120            }
121        } catch (java.lang.NumberFormatException ex) {
122            throw new JmriException("Hardware Address passed "+curAddress+" should be a number");
123        }
124        return prefix + typeLetter() + curAddress;
125    }
126
127/* obsolete
128    /** {@inheritDoc} * /
129    @Override
130    public String getNextValidAddress(@Nonnull String curAddress, @Nonnull String prefix, boolean ignoreInitialExisting) {
131        log.trace("getNextValidAddress from {} - {}", curAddress, prefix);
132        // If the hardware address passed does not already exist then this can
133        // be considered the next valid address.
134        String tmpSName = "";
135        try {
136            tmpSName = createSystemName(curAddress, prefix);
137        } catch (JmriException ex) {
138            jmri.InstanceManager.getDefault(jmri.UserPreferencesManager.class).
139                    showErrorMessage(Bundle.getMessage("WarningTitle"), Bundle.getMessage("ErrorConvertNumberX", curAddress), null, "", true, false);
140            return null;
141        }
142
143        Turnout t = getBySystemName(tmpSName);
144        if (t == null && !ignoreInitialExisting) {
145            return curAddress;
146        }
147        return null;
148    }
149 */
150    
151    /**
152     * Public method to validate system name format.
153     *
154     * @param systemName system name
155     * @return VALID if system name has a valid format, else return INVALID
156     */
157    @Override
158    public NameValidity validSystemNameFormat(String systemName) {
159        log.trace("validSystemNameFormat");
160        // TODO!!
161        //return (getBitFromSystemName(systemName) != 0) ? NameValidity.VALID : NameValidity.INVALID;
162        return NameValidity.VALID; //TODO
163    }    
164    
165    /**
166     * {@inheritDoc}
167     */
168    @Override
169    public String validateSystemNameFormat(String name, Locale locale) {
170        log.trace("validateSystemNameFormat: name: {}, typeLetter: {}", name, typeLetter());
171        validateSystemNamePrefix(name, locale);
172        //validateAddressFormat(name.substring(getSystemNamePrefix().length()));
173        if (!BiDiBAddress.isValidSystemNameFormat(name, typeLetter(), getMemo())) {
174            throw new jmri.NamedBean.BadSystemNameException(Locale.getDefault(), "InvalidSystemName",name);
175        }
176        return name;
177    }
178
179    /**
180     * {@inheritDoc}
181     */
182    @Override
183    public String getEntryToolTip() {
184        String entryToolTip = Bundle.getMessage("AddOutputEntryToolTip");
185        return entryToolTip;
186    }
187
188    private final static Logger log = LoggerFactory.getLogger(BiDiBTurnoutManager.class);
189
190}