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        this.propertyChangeSupport.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
080            int oldSize = _tracks.size();
081            _tracks.add(track);
082
083            firePropertyChange(LISTCHANGE_CHANGED_PROPERTY, oldSize, _tracks.size());
084        }
085    }
086
087    /**
088     * Removes a track from this pool
089     *
090     * @param track to be removed.
091     */
092    public void remove(Track track) {
093        if (_tracks.contains(track)) {
094
095            int oldSize = _tracks.size();
096            _tracks.remove(track);
097
098            firePropertyChange(LISTCHANGE_CHANGED_PROPERTY, oldSize, _tracks.size());
099        }
100    }
101
102    public List<Track> getTracks() {
103        // Return a copy to protect the internal list
104        return new ArrayList<>(_tracks);
105    }
106
107    public int getTotalLengthTracks() {
108        return getTracks().stream().map(track -> track.getLength()).reduce(0, Integer::sum);
109    }
110    
111    public int getMaxLengthTrack(Track track) {
112        int length = getTotalLengthTracks();
113        for (Track t : getTracks()) {
114            if (t == track) {
115                continue;
116            }
117            length = length - t.getMinimumLength();
118        }
119        return length;
120    }
121
122    /**
123     * Request track length from one of the other tracks in this pool. Other
124     * tracks in the same pool may have their length shortened or lengthened by
125     * this operation.
126     *
127     * @param track  the track requesting additional length
128     * @param length the length of rolling stock
129     * @return true if successful
130     */
131    public boolean requestTrackLength(Track track, int length) {
132        // only request enough length for the rolling stock to fit
133        int additionalLength = track.getUsedLength() + track.getReserved() + length - track.getLength();
134
135        for (Track t : getTracks()) {
136            // note that the reserved track length can be either positive or negative
137            if (t != track) {
138                if (t.getUsedLength() + t.getReserved() + additionalLength <= t.getLength()
139                        && t.getLength() - additionalLength >= t.getMinimumLength()) {
140                    log.debug("Pool ({}) increasing track ({}) length ({}) decreasing ({})", getName(),
141                            track.getName(), additionalLength, t.getName()); // NOI18N
142                    t.setLength(t.getLength() - additionalLength);
143                    track.setLength(track.getLength() + additionalLength);
144                    return true;
145                } else {
146                    // steal whatever isn't being used by this track
147                    int available = t.getLength() - (t.getUsedLength() + t.getReserved());
148                    int min = t.getLength() - t.getMinimumLength();
149                    if (min < available) {
150                        available = min;
151                    }
152                    if (available > 0) {
153                        // adjust track lengths and reduce the additional length needed
154                        log.debug("Pool ({}) incremental increase for track ({}) length ({}) decreasing ({})",
155                                getName(), track.getName(), available, t.getName()); // NOI18N
156                        t.setLength(t.getLength() - available);
157                        track.setLength(track.getLength() + available);
158                        additionalLength = additionalLength - available;
159                    }
160                }
161            }
162        }
163        return false;
164    }
165}