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