001package jmri.jmrit.throttle.panels;
002
003import java.awt.BorderLayout;
004import java.awt.Component;
005import java.util.ArrayList;
006import java.util.List;
007
008import javax.swing.Icon;
009import javax.swing.JLabel;
010import javax.swing.JPanel;
011import javax.swing.JTabbedPane;
012
013import org.jdom2.Element;
014
015import jmri.Consist;
016import jmri.DccLocoAddress;
017import jmri.DccThrottle;
018import jmri.InstanceManager;
019import jmri.LocoAddress;
020import jmri.ThrottleManager;
021import jmri.jmrit.roster.Roster;
022import jmri.jmrit.roster.RosterEntry;
023import jmri.jmrit.roster.RosterIconFactory;
024import jmri.jmrit.throttle.implementation.SimpleThrottlePanel;
025import jmri.jmrit.throttle.interfaces.AddressListener;
026
027/**
028 * A panel do be used withing a throttle UI, allows for independantly 
029 * controling functions of each locomotive in a consist
030 * 
031 * <hr>
032 * This file is part of JMRI.
033 * <p>
034 * JMRI is free software; you can redistribute it and/or modify it under the
035 * terms of version 2 of the GNU General Public License as published by the Free
036 * Software Foundation. See the "COPYING" file for a copy of this license.
037 * <p>
038 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY
039 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
040 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
041 *
042 * @author Lionel Jeanson Copyright (C) 2026
043 * 
044 */
045
046public class ConsistFunctionPanel extends JPanel implements AddressListener {
047
048    private final ThrottleManager throttleManager;
049    private AddressPanel addressPanel = null;    
050    private JTabbedPane consistFunctionsPanels;
051    private JLabel errorLabel = new JLabel(Bundle.getMessage("ThrottleConsistsFunctionPanelError"));
052    private static final RosterIconFactory ICN_FACT = InstanceManager.getDefault(RosterIconFactory.class);
053    
054    public ConsistFunctionPanel(ThrottleManager tm) {
055        throttleManager = tm;
056        initGUI();        
057        updateFunctionPanels();
058    }
059    
060    private void initGUI() {
061        setLayout(new BorderLayout());
062        consistFunctionsPanels = new JTabbedPane();    
063        add(errorLabel, BorderLayout.NORTH);       
064        add(consistFunctionsPanels, BorderLayout.CENTER);
065    }
066
067    public void dispose() {        
068        if (consistFunctionsPanels!=null) {
069            for (Component cmp : consistFunctionsPanels.getComponents()) {
070                if (cmp instanceof SimpleThrottlePanel) {
071                    ((SimpleThrottlePanel) cmp).dispose();
072                }
073            }
074            consistFunctionsPanels.removeAll();
075        }
076        consistFunctionsPanels = null;
077    }
078
079    public void setAddressPanel(AddressPanel addressPanel) {
080        if (this.addressPanel != null) {
081            this.addressPanel.removeAddressListener(this);
082        }
083        this.addressPanel = addressPanel;
084        if (this.addressPanel != null) {
085            this.addressPanel.addAddressListener(this);
086        }
087        updateFunctionPanels();
088    }
089
090     public DccThrottle getFunctionThrottle() {
091         if (consistFunctionsPanels == null || consistFunctionsPanels.getComponentCount() == 0) {
092            return null;
093         }
094         Component lastCmp = consistFunctionsPanels.getComponentAt(consistFunctionsPanels.getComponentCount()-1);
095         if (! (lastCmp instanceof SimpleThrottlePanel)) {
096            return null;
097         }         
098         // the throttle of the last panel will be the head unit one, we'll use that one for consist functions
099         return ((SimpleThrottlePanel)lastCmp).getThrottle();
100     }
101
102      public RosterEntry getFunctionRosterEntry() {
103         if (consistFunctionsPanels == null || consistFunctionsPanels.getComponentCount() == 0) {
104            return null;
105         }
106         Component lastCmp = consistFunctionsPanels.getComponentAt(consistFunctionsPanels.getComponentCount()-1);
107         if (! (lastCmp instanceof SimpleThrottlePanel)) {
108            return null;
109         }         
110         // the throttle of the last panel will be the head unit one, we'll use that one for consist functions
111         return ((SimpleThrottlePanel)lastCmp).getRosterEntry();
112      }
113
114    private void updateFunctionPanels() {
115        if (consistFunctionsPanels == null) {
116            errorLabel.setVisible(true);
117            return; // we're self destructing
118        }
119        // clean up       
120        consistFunctionsPanels.removeAll();        
121        for (Component cmp : consistFunctionsPanels.getComponents()) {
122            if (cmp instanceof SimpleThrottlePanel) {
123                ((SimpleThrottlePanel) cmp).dispose();
124            }
125        }        
126        // shall we?
127        if ((addressPanel == null) || (addressPanel.getThrottle() == null)) {
128            return;
129        }
130        Consist consist = addressPanel.getConsistEntry();
131        if (consist == null) {
132            return;
133        }
134        // let's go
135        errorLabel.setVisible(false);
136        ArrayList<DccLocoAddress> consistList = consist.getConsistList();
137        // go backward, we want the head on the right (added last)
138        for (int i=consistList.size()-1; i>=0; i--) {
139            SimpleThrottlePanel stp = new SimpleThrottlePanel(null, throttleManager, false, true, false);
140            stp.setAddress(consistList.get(i));
141            Icon tabIcon = null;
142            String tabText = Bundle.getMessage("ThrottleAddress") + " " + consistList.get(i);
143            // do we have a matching roster entry
144            List<RosterEntry> l = Roster.getDefault().matchingList(null, null, "" + consistList.get(i).getNumber(), null, null, null, null);
145            if (!l.isEmpty()) {                
146                tabText = l.get(0).getId();
147                if (consist.getLocoDirection(consistList.get(i))) {
148                    tabIcon = ICN_FACT.getIcon(l.get(0));
149                } else {
150                    tabIcon = ICN_FACT.getReversedIcon(l.get(0));
151                }
152            }
153            consistFunctionsPanels.addTab(tabText, tabIcon, stp);
154        }
155        consistFunctionsPanels.setSelectedIndex(consistList.size()-1); // select last (head)
156    }
157
158    @Override
159    public void notifyConsistAddressReleased(LocoAddress address) {
160       updateFunctionPanels();
161    }
162
163    @Override
164    public void notifyConsistAddressThrottleFound(DccThrottle throttle) {
165        updateFunctionPanels();
166    }
167
168    @Override
169    public void notifyConsistAddressChosen(LocoAddress address) {
170        // do nothing
171    }
172
173    @Override
174    public void notifyAddressChosen(LocoAddress address) {
175        // do nothing
176    }
177
178    @Override
179    public void notifyAddressReleased(LocoAddress address) {
180        // do nothing
181    }
182
183    @Override
184    public void notifyAddressThrottleFound(DccThrottle throttle) {
185        // do nothing
186    }
187
188    @Override
189    public void notifyRosterEntrySelected(RosterEntry re) {
190        updateFunctionPanels();
191    }
192
193    public Element getXml() {
194        Element me = new Element("ConsistFunctionsPanel"); // NOI18N
195        // put nothing
196        return me;
197    }
198
199    public void setXml(Element e) {
200        // do nothing
201    }
202    
203}