001package jmri.jmrit.etcs;
002
003import java.util.*;
004
005import javax.annotation.Nonnull;
006
007import org.apiguardian.api.API;
008
009/**
010 * Class to represent a Movement Authority which is passed to the DMI
011 * to authorise a route.
012 * @author Steve Young Copyright (C) 2024
013 */
014@API(status=API.Status.EXPERIMENTAL)
015public class MovementAuthority {
016
017    private final List<TrackSection> list;
018
019    /**
020     * Create a new MovementAuthority from a List of TrackSections.
021     * @param sectionList the list of TrackSections.
022     */
023    public MovementAuthority(List<TrackSection> sectionList){
024        list = sectionList;
025    }
026
027    /**
028     * Get the list of TrackSections.
029     * @return Unmodifiable list of TrackSections.
030     */
031    public List<TrackSection> getTrackSections() {
032        return Collections.unmodifiableList(list);
033    }
034
035    /**
036     * Remove a TrackSection from the Movement Authority.
037     * @param ts the TrackSection to remove.
038     */
039    private void removeTrackSection(TrackSection ts){
040        list.remove(ts);
041    }
042
043    /**
044     * Aggregates a list of DmiMovementAuthorities into a list of
045     * DmiTrackSections, based on either speed changes or gradient changes.
046     *
047     * @param completeList The list of MovementAuthority objects to process.
048     * @param isSpeed      True to aggregate based on speed changes,
049     *                     False to aggregate based on gradient changes.
050     * @return A list of aggregated DmiTrackSection77 objects.
051     */
052    @Nonnull
053    public static List<TrackSection> getTrackSectionList(
054        @Nonnull List<MovementAuthority> completeList, boolean isSpeed) {
055
056        List<TrackSection> trackSectionList = new ArrayList<>();
057        TrackSection lastTs = null;
058        List<TrackSection> tempTrackList;
059        for (MovementAuthority ma : completeList) {
060            tempTrackList = ma.getTrackSections();
061            for (TrackSection ts : tempTrackList) {
062                if (lastTs == null || checkAddToList(lastTs, ts, isSpeed)) {
063                    trackSectionList.add(new TrackSection(ts.getLength(), ts.getSpeed(), ts.getGradient()));
064                    lastTs = trackSectionList.get(trackSectionList.size() - 1);
065                } else {
066                    lastTs.setLength(ts.getLength() + lastTs.getLength());
067                }
068            }
069        }
070        return trackSectionList;
071    }
072
073    private static boolean checkAddToList(
074        @Nonnull TrackSection lastTs, @Nonnull TrackSection ts, boolean isSpeed ) {
075        if (isSpeed) {
076            return lastTs.getSpeed() != ts.getSpeed();
077        } else {
078            return lastTs.getGradient() != ts.getGradient();
079        }
080    }
081
082    /**
083     * Advance forward a List of Movement Authorities.
084     * The length of the nearest TrackSection(s) are reduced by the distance.
085     * Unused Track Sections are removed from the List.
086     * Track Section announcements are also advanced.
087     * @param list the list to advance.
088     * @param distance the distance to advance.
089     * @return the modified List of Movement Authorities.
090     */
091    @Nonnull
092    public static List<MovementAuthority> advanceForward(
093        @Nonnull List<MovementAuthority> list, int distance) {
094    
095        if (list.isEmpty()){
096            return list;
097        }
098
099        MovementAuthority ma = list.get(0);
100        if (ma.getTrackSections().isEmpty()) {
101            list.remove(ma);
102            return advanceForward(list, distance);
103        }
104        TrackSection ts = ma.getTrackSections().get(0);
105
106        int newLength = ts.getLength() - distance;
107        if ( newLength < 0 ) {
108            ma.removeTrackSection(ts);
109            return advanceForward(list, distance-ts.getLength());
110        }
111        ts.setLength(newLength);
112        ts.advanceAnnouncements(distance);
113        return list;    
114    }
115    
116}