001package jmri.jmrit.entryexit;
002
003import java.beans.PropertyChangeEvent;
004import java.beans.PropertyChangeListener;
005import java.beans.PropertyChangeSupport;
006import java.util.ArrayList;
007import java.util.HashMap;
008import java.util.List;
009
010import javax.annotation.CheckForNull;
011import javax.swing.JMenuItem;
012
013import jmri.NamedBean;
014import jmri.SignalMast;
015import jmri.jmrit.display.layoutEditor.LayoutBlock;
016import jmri.jmrit.display.layoutEditor.LayoutEditor;
017
018public class Source implements PropertyChangeListener {
019
020    JMenuItem clear = null;
021    JMenuItem cancel = null;
022    JMenuItem editCancel = null;
023    JMenuItem editClear = null;
024    JMenuItem editOneClick = null;
025    JMenuItem oneClick = null;
026
027    /**
028     * String constant for the active property.
029     */
030    public static final String PROPERTY_ACTIVE = "active";
031
032    NamedBean sourceObject = null;
033    NamedBean sourceSignal = null;
034    //String ref = "Empty";
035    transient PointDetails pd = null;
036
037    EntryExitPairs manager = jmri.InstanceManager.getDefault(jmri.jmrit.entryexit.EntryExitPairs.class);
038
039    //Using Object here rather than sourceSensor, working on the basis that it might
040    //one day be possible to have a signal icon selectable on a panel and
041    //generate a propertychange, so hence do not want to tie it down at this stage.
042    transient HashMap<PointDetails, DestinationPoints> pointToDest = new HashMap<PointDetails, DestinationPoints>();
043
044    public Source(PointDetails point/*, ArrayList<LayoutBlock> protectingBlock*/) {
045        if (point.getSensor() != null) {
046            addSourceObject(point.getSensor());
047        } else {
048            addSourceObject(point.getSignal());
049        }
050        //protectingBlocks = protectingBlock;
051        point.setSource(Source.this);
052        sourceSignal = point.getSignal();
053        pd = point;
054    }
055
056    public boolean isEnabled(Object dest, LayoutEditor panel) {
057        PointDetails lookingFor = manager.getPointDetails(dest, panel);
058        if (pointToDest.containsKey(lookingFor)) {
059            return pointToDest.get(lookingFor).isEnabled();
060        }
061        return true;
062    }
063
064    public void setEnabled(Object dest, LayoutEditor panel, boolean boo) {
065        PointDetails lookingFor = manager.getPointDetails(dest, panel);
066        if (pointToDest.containsKey(lookingFor)) {
067            pointToDest.get(lookingFor).setEnabled(boo);
068        }
069    }
070
071    /**
072     * Property change support for table in AddEntryExitPairPanel.
073     * Catch when paths go active.
074     * @since 4.17.4
075     */
076    private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
077
078    /**
079     * Add property change listener.
080     * @since 4.17.4
081     * @param listener the pcl to add.
082     */
083    public void addPropertyChangeListener(PropertyChangeListener listener) {
084        pcs.addPropertyChangeListener(listener);
085    }
086
087    /**
088     * Remove property change listener.
089     * @since 4.17.4
090     * @param listener the pcl to remove.
091     */
092    public void removePropertyChangeListener(PropertyChangeListener listener) {
093        pcs.removePropertyChangeListener(listener);
094    }
095
096    /**
097     * @since 4.17.4
098     */
099    @Override
100    public void propertyChange(PropertyChangeEvent evt) {
101        pcs.firePropertyChange(PROPERTY_ACTIVE, evt.getOldValue(), evt.getNewValue());
102    }
103
104
105    void cancelClearInterlockFromSource(int cancelClear) {
106        for (DestinationPoints dp : pointToDest.values()) {
107            if (dp.isActive()) {
108                dp.cancelClearInterlock(cancelClear);
109            }
110        }
111    }
112
113    void setMenuEnabled(boolean boo) {
114        if (clear != null) {
115            clear.setEnabled(boo);
116        }
117        if (cancel != null) {
118            cancel.setEnabled(boo);
119        }
120        if (editClear != null) {
121            editClear.setEnabled(boo);
122        }
123        if (editCancel != null) {
124            editCancel.setEnabled(boo);
125        }
126        if (oneClick != null) {
127            oneClick.setEnabled(!boo);
128        }
129        if (editOneClick != null) {
130            editOneClick.setEnabled(!boo);
131        }
132    }
133
134    /**
135     * @since 4.17.4
136     * Making the source object available for scripting in Jython.
137     * @return the point details.
138     */
139    public PointDetails getPoint() {
140        return pd;
141    }
142
143    LayoutBlock getStart() {
144        return pd.getFacing();
145    }
146
147    List<LayoutBlock> getSourceProtecting() {
148        return pd.getProtecting();
149    }
150
151    NamedBean getSourceSignal() {
152        if (sourceSignal == null) {
153            pd.getSignal();
154        }
155        return sourceSignal;
156    }
157
158    /**
159     * @since 4.17.4
160     * Add Property Change Listener.
161     * @param dest the points details to add.
162     * @param id the points details id.
163     */
164    public void addDestination(PointDetails dest, String id) {
165        if (pointToDest.containsKey(dest)) {
166            return;
167        }
168
169        DestinationPoints dstPoint = new DestinationPoints(dest, id, this);
170        dest.setDestination(dstPoint, this);
171        pointToDest.put(dest, dstPoint);
172        dstPoint.addPropertyChangeListener(this);
173    }
174
175    /**
176     * @since 4.17.4
177     * Remove Property Change Listener.
178     * @param dest the point details location to remove.
179     */
180    public void removeDestination(PointDetails dest) {
181        pointToDest.get(dest).dispose();
182        pointToDest.remove(dest);
183        dest.removePropertyChangeListener(this);
184        if (pointToDest.isEmpty()) {
185            getPoint().removeSource(this);
186        }
187    }
188
189    private void addSourceObject(NamedBean source) {
190        if (sourceObject == source) {
191            return;
192        }
193        sourceObject = source;
194    }
195
196    Object getSourceObject() {
197        return sourceObject;
198    }
199
200    public ArrayList<PointDetails> getDestinationPoints() {
201        return new ArrayList<>(pointToDest.keySet());
202    }
203
204    public boolean isDestinationValid(PointDetails destPoint) {
205        return pointToDest.containsKey(destPoint);
206    }
207
208    public boolean getUniDirection(Object dest, LayoutEditor panel) {
209        //Work on the principle that if the source is uniDirection, then the destination has to be.
210        PointDetails lookingFor = manager.getPointDetails(dest, panel);
211        if (pointToDest.containsKey(lookingFor)) {
212            return pointToDest.get(lookingFor).getUniDirection();
213        }
214        return true;
215    }
216
217    public void setUniDirection(Object dest, LayoutEditor panel, boolean set) {
218
219        PointDetails lookingFor = manager.getPointDetails(dest, panel);
220        if (pointToDest.containsKey(lookingFor)) {
221            pointToDest.get(lookingFor).setUniDirection(set);
222        }
223    }
224
225    public boolean canBeBiDirection(Object dest, LayoutEditor panel) {
226        if (getSourceSignal() == null) {
227            return true;
228        }
229        //Work on the pinciple that if the source is uniDirection, then the destination has to be.
230        PointDetails lookingFor = manager.getPointDetails(dest, panel);
231        if (pointToDest.containsKey(lookingFor)) {
232            return pointToDest.get(lookingFor).getSignal() == null;
233        }
234        return false;
235    }
236
237    public boolean isRouteActive(PointDetails endpoint) {
238        if (pointToDest.containsKey(endpoint)) {
239            return pointToDest.get(endpoint).isActive();
240        }
241        return false;
242    }
243
244    public void activeBean(DestinationPoints dest, boolean reverseDirection) {
245        if (dest != null) {
246            dest.activeBean(reverseDirection);
247        }
248    }
249
250    public DestinationPoints getDestForPoint(PointDetails dp) {
251        return pointToDest.get(dp);
252    }
253
254    public int getNumberOfDestinations() {
255        return pointToDest.size();
256    }
257
258    public void setEntryExitType(Object dest, LayoutEditor panel, int type) {
259        PointDetails lookingFor = manager.getPointDetails(dest, panel);
260        if (pointToDest.containsKey(lookingFor)) {
261            pointToDest.get(lookingFor).setEntryExitType(type);
262        }
263        if (type == EntryExitPairs.FULLINTERLOCK) {
264            if (sourceSignal instanceof SignalMast) {
265                if (!manager.isAbsSignalMode()) {
266                    ((SignalMast) sourceSignal).setHeld(true);
267                }
268            }
269        }
270    }
271
272    public int getEntryExitType(Object dest, LayoutEditor panel) {
273        PointDetails lookingFor = manager.getPointDetails(dest, panel);
274        if (pointToDest.containsKey(lookingFor)) {
275            return pointToDest.get(lookingFor).getEntryExitType();
276        }
277
278        return 0x00;
279    }
280
281    public void cancelInterlock(Object dest, LayoutEditor panel) {
282        PointDetails lookingFor = manager.getPointDetails(dest, panel);
283        if (pointToDest.containsKey(lookingFor)) {
284            pointToDest.get(lookingFor).cancelClearInterlock(EntryExitPairs.CANCELROUTE);
285        }
286    }
287
288    @CheckForNull
289    public String getUniqueId(Object dest, LayoutEditor panel) {
290        PointDetails lookingFor = manager.getPointDetails(dest, panel);
291        if (pointToDest.containsKey(lookingFor)) {
292            return pointToDest.get(lookingFor).getUniqueId();
293        }
294        return null;
295    }
296
297    public ArrayList<String> getDestinationUniqueId() {
298        ArrayList<String> rList = new ArrayList<>();
299        for (DestinationPoints d : pointToDest.values()) {
300            rList.add(d.getUniqueId());
301        }
302        return rList;
303    }
304
305    @CheckForNull
306    public DestinationPoints getByUniqueId(String id) {
307        for (DestinationPoints d : pointToDest.values()) {
308            if (d.getUniqueId().equals(id)) {
309                return d;
310            }
311        }
312        return null;
313    }
314
315    @CheckForNull
316    public DestinationPoints getByUserName(String id) {
317        for (DestinationPoints d : pointToDest.values()) {
318            String uname = d.getUserName();
319            if (uname != null && uname.equals(id)) {
320                return d;
321            }
322        }
323        return null;
324    }
325
326}