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