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