001package jmri.jmrit.operations.rollingstock.engines;
002
003import java.beans.PropertyChangeEvent;
004import java.util.*;
005
006import org.jdom2.Element;
007import org.slf4j.Logger;
008import org.slf4j.LoggerFactory;
009
010import jmri.*;
011import jmri.jmrit.operations.rollingstock.RollingStock;
012import jmri.jmrit.operations.rollingstock.RollingStockManager;
013import jmri.jmrit.operations.setup.OperationsSetupXml;
014import jmri.jmrit.operations.trains.Train;
015import jmri.jmrit.operations.trains.TrainManifestHeaderText;
016
017/**
018 * Manages the engines.
019 *
020 * @author Daniel Boudreau Copyright (C) 2008
021 */
022public class EngineManager extends RollingStockManager<Engine>
023        implements InstanceManagerAutoDefault, InstanceManagerAutoInitialize {
024
025    public EngineManager() {
026    }
027
028    /**
029     * Finds an existing engine or creates a new engine if needed requires engine's
030     * road and number
031     *
032     * @param engineRoad   The engine's road initials
033     * @param engineNumber The engine's road number
034     *
035     * @return new engine or existing engine
036     */
037    @Override
038    public Engine newRS(String engineRoad, String engineNumber) {
039        Engine engine = getByRoadAndNumber(engineRoad, engineNumber);
040        if (engine == null) {
041            engine = new Engine(engineRoad, engineNumber);
042            register(engine);
043        }
044        return engine;
045    }
046
047    @Override
048    public void deregister(Engine engine) {
049        super.deregister(engine);
050        InstanceManager.getDefault(EngineManagerXml.class).setDirty(true);
051    }
052
053    /**
054     * Sort by engine model
055     *
056     * @return list of engines ordered by engine model
057     */
058    public List<Engine> getByModelList() {
059        return getByList(getByRoadNameList(), BY_MODEL);
060    }
061
062    /**
063     * Sort by engine consist
064     *
065     * @return list of engines ordered by engine consist
066     */
067    public List<Engine> getByConsistList() {
068        return getByList(getByRoadNameList(), BY_CONSIST);
069    }
070
071    public List<Engine> getByHpList() {
072        return getByList(getByModelList(), BY_HP);
073    }
074
075    // The special sort options for engines
076    private static final int BY_MODEL = 30;
077    private static final int BY_CONSIST = 31;
078    private static final int BY_HP = 32;
079
080    // add engine options to sort comparator
081    @Override
082    protected java.util.Comparator<Engine> getComparator(int attribute) {
083        switch (attribute) {
084            case BY_MODEL:
085                return (e1, e2) -> (e1.getModel().compareToIgnoreCase(e2.getModel()));
086            case BY_CONSIST:
087                return (e1, e2) -> (e1.getConsistName().compareToIgnoreCase(e2.getConsistName()));
088            case BY_HP:
089                return (e1, e2) -> (e1.getHpInteger() - e2.getHpInteger());
090            default:
091                return super.getComparator(attribute);
092        }
093    }
094
095    /**
096     * return a list available engines (no assigned train) engines are ordered least
097     * recently moved to most recently moved.
098     *
099     * @param train The Train requesting this list.
100     *
101     * @return Ordered list of engines not assigned to a train
102     */
103    public List<Engine> getAvailableTrainList(Train train) {
104        // now build list of available engines for this route
105        List<Engine> out = new ArrayList<>();
106        // get engines by moves list
107        for (RollingStock rs : getByMovesList()) {
108            Engine engine = (Engine) rs;
109            if (engine.getTrack() != null && (engine.getTrain() == null || engine.getTrain() == train)) {
110                out.add(engine);
111            }
112        }
113        return out;
114    }
115
116    /**
117     * Returns a list of locos sorted by blocking number for a train. This returns a
118     * list of consisted locos in the order that they were entered in.
119     *
120     * @param train The Train requesting this list.
121     * @return A list of sorted locos.
122     */
123    public List<Engine> getByTrainBlockingList(Train train) {
124        return getByList(super.getByTrainList(train), BY_BLOCKING);
125    }
126
127    /**
128     * Get a list of engine road names.
129     *
130     * @param model The string model name, can be NONE.
131     *
132     * @return List of engine road names.
133     */
134    public List<String> getEngineRoadNames(String model) {
135        List<String> names = new ArrayList<>();
136        Enumeration<String> en = _hashTable.keys();
137        while (en.hasMoreElements()) {
138            Engine engine = getById(en.nextElement());
139            if ((engine.getModel().equals(model) || model.equals(NONE)) && !names.contains(engine.getRoadName())) {
140                names.add(engine.getRoadName());
141            }
142        }
143        java.util.Collections.sort(names);
144        return names;
145    }
146
147    int _commentLength = 0;
148
149    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings( value="SLF4J_FORMAT_SHOULD_BE_CONST",
150            justification="I18N of Info Message")
151    public int getMaxCommentLength() {
152        if (_commentLength == 0) {
153            _commentLength = TrainManifestHeaderText.getStringHeader_Comment().length();
154            String comment = "";
155            Engine engineMax = null;
156            for (Engine engine : getList()) {
157                if (engine.getComment().length() > _commentLength) {
158                    _commentLength = engine.getComment().length();
159                    comment = engine.getComment();
160                    engineMax = engine;
161                }
162            }
163            if (engineMax != null) {
164                log.info(Bundle.getMessage("InfoMaxComment", engineMax.toString(), comment, _commentLength));
165            }
166        }
167        return _commentLength;
168    }
169
170    public void load(Element root) {
171        if (root.getChild(Xml.ENGINES) != null) {
172            List<Element> engines = root.getChild(Xml.ENGINES).getChildren(Xml.ENGINE);
173            log.debug("readFile sees {} engines", engines.size());
174            for (Element e : engines) {
175                register(new Engine(e));
176            }
177        }
178    }
179
180    /**
181     * Create an XML element to represent this Entry. This member has to remain
182     * synchronized with the detailed DTD in operations-engines.dtd.
183     *
184     * @param root The common Element for operations-engines.dtd.
185     *
186     */
187    public void store(Element root) {
188        Element values;
189        root.addContent(values = new Element(Xml.ENGINES));
190        // add entries
191        for (RollingStock rs : getByRoadNameList()) {
192            Engine eng = (Engine) rs;
193            values.addContent(eng.store());
194        }
195    }
196
197    protected void setDirtyAndFirePropertyChange(String p, Object old, Object n) {
198        // Set dirty
199        InstanceManager.getDefault(EngineManagerXml.class).setDirty(true);
200        super.firePropertyChange(p, old, n);
201    }
202
203    @Override
204    public void propertyChange(PropertyChangeEvent evt) {
205        if (evt.getPropertyName().equals(Engine.COMMENT_CHANGED_PROPERTY)) {
206            _commentLength = 0;
207        }
208        super.propertyChange(evt);
209    }
210
211    private final static Logger log = LoggerFactory.getLogger(EngineManager.class);
212
213    @Override
214    public void initialize() {
215        InstanceManager.getDefault(OperationsSetupXml.class); // load setup
216        // create manager to load engines and their attributes
217        InstanceManager.getDefault(EngineManagerXml.class);
218    }
219}