001package jmri.jmrit.operations.locations;
002
003import java.util.ArrayList;
004import java.util.List;
005
006import org.slf4j.Logger;
007import org.slf4j.LoggerFactory;
008
009import jmri.beans.Bean;
010
011/**
012 * Represents a pool of tracks that share their length.
013 *
014 * @author Daniel Boudreau Copyright (C) 2011
015 * @author Gregory Madsen Copyright (C) 2012
016 *
017 */
018public class Pool extends Bean {
019
020    public static final String LISTCHANGE_CHANGED_PROPERTY = "poolListChange"; // NOI18N
021    public static final String DISPOSE = "poolDispose"; // NOI18N
022
023    private final static Logger log = LoggerFactory.getLogger(Pool.class);
024
025    // stores tracks for this pool
026    protected List<Track> _tracks = new ArrayList<>();
027
028    protected String _id = "";
029
030    public String getId() {
031        return _id;
032    }
033
034    protected String _name = "";
035
036    public String getName() {
037        return _name;
038    }
039
040    public void setName(String name) {
041        String old = _name;
042        _name = name;
043        firePropertyChange("Name", old, name);
044    }
045
046    /**
047     * The number of tracks in this pool.
048     *
049     * @return the number of tracks in this pool.
050     */
051    public int getSize() {
052        return _tracks.size();
053    }
054
055    // for combo boxes
056    @Override
057    public String toString() {
058        return _name;
059    }
060
061    public Pool(String id, String name) {
062        super(false);
063        log.debug("New pool ({}) id: {}", name, id);
064        _name = name;
065        _id = id;
066    }
067
068    public void dispose() {
069        firePropertyChange(DISPOSE, null, DISPOSE);
070    }
071
072    /**
073     * Adds a track to this pool
074     *
075     * @param track to be added.
076     */
077    public void add(Track track) {
078        if (!_tracks.contains(track)) {
079            int oldSize = _tracks.size();
080            _tracks.add(track);
081            firePropertyChange(LISTCHANGE_CHANGED_PROPERTY, oldSize, _tracks.size());
082        }
083    }
084
085    /**
086     * Removes a track from this pool
087     *
088     * @param track to be removed.
089     */
090    public void remove(Track track) {
091        if (_tracks.contains(track)) {
092            int oldSize = _tracks.size();
093            _tracks.remove(track);
094            firePropertyChange(LISTCHANGE_CHANGED_PROPERTY, oldSize, _tracks.size());
095        }
096    }
097
098    public List<Track> getTracks() {
099        // Return a copy to protect the internal list
100        return new ArrayList<>(_tracks);
101    }
102
103    public int getTotalLengthTracks() {
104        return getTracks().stream().map(track -> track.getLength()).reduce(0, Integer::sum);
105    }
106    
107    /**
108     * Used to determine the maximum available length for a given track
109     * 
110     * @param track the track being evaluated
111     * @return maximum track length
112     */
113    public int getMaxLengthTrack(Track track) {
114        int length = getTotalLengthTracks();
115        for (Track t : getTracks()) {
116            if (t == track) {
117                continue;
118            }
119            length = length - t.getPoolMinimumLength();
120        }
121        return length;
122    }
123
124    /**
125     * Request track length from one of the other tracks in this pool. Other
126     * tracks in the same pool may have their length shortened or lengthened by
127     * this operation.
128     *
129     * @param track  the track requesting additional length
130     * @param length the length of rolling stock
131     * @return true if successful
132     */
133    public boolean requestTrackLength(Track track, int length) {
134        // is there a maximum length restriction?
135        if (track.getUsedLength() + track.getReserved() + length > track.getPoolMaximumLength()) {
136            return false;
137        }
138        // only request enough length for the rolling stock to fit
139        int additionalLength = track.getUsedLength() + track.getReserved() + length - track.getLength();
140
141        for (Track t : getTracks()) {
142            // note that the reserved track length can be either positive or negative
143            if (t != track) {
144                if (t.getUsedLength() + t.getReserved() + additionalLength <= t.getLength()
145                        && t.getLength() - additionalLength >= t.getPoolMinimumLength()) {
146                    log.debug("Pool ({}) increasing track ({}) length ({}) decreasing ({})", getName(),
147                            track.getName(), additionalLength, t.getName()); // NOI18N
148                    t.setLength(t.getLength() - additionalLength);
149                    track.setLength(track.getLength() + additionalLength);
150                    return true;
151                } else {
152                    // steal whatever isn't being used by this track
153                    int available = t.getLength() - (t.getUsedLength() + t.getReserved());
154                    int min = t.getLength() - t.getPoolMinimumLength();
155                    if (min < available) {
156                        available = min;
157                    }
158                    if (available > 0) {
159                        // adjust track lengths and reduce the additional length needed
160                        log.debug("Pool ({}) incremental increase for track ({}) length ({}) decreasing ({})",
161                                getName(), track.getName(), available, t.getName()); // NOI18N
162                        t.setLength(t.getLength() - available);
163                        track.setLength(track.getLength() + available);
164                        additionalLength = additionalLength - available;
165                    }
166                }
167            }
168        }
169        return false;
170    }
171
172    /**
173     * Used to determine if the option to use maximum track length in a pool is
174     * enabled. Enabled when there's 3 or more tracks in the pool.
175     * 
176     * @return true if maximum track length option is available
177     */
178    public boolean isMaxLengthOptionEnabled() {
179        // need 3 or more tracks in pool before allowing max track length option
180        if (getSize() > 2) {
181            return true;
182        }
183        return false;
184    }
185
186    public boolean isThereMaxLengthRestrictions() {
187        for (Track t : getTracks()) {
188            if (t.getPoolMaximumLength() != Integer.MAX_VALUE) {
189                return true;
190            }
191        }
192        return false;
193    }
194}