001package jmri.jmrix.can.cbus;
002
003import java.util.EnumSet;
004import javax.annotation.CheckForNull;
005import javax.annotation.Nonnull;
006import jmri.jmrix.AbstractMessage;
007import jmri.jmrix.can.CanFrame;
008
009/**
010 * ENUM to represent various CBUS OPC Filters.
011 * 
012 * @author Steve Young (C) 2020
013 */
014public enum CbusFilterType {
015    CFIN(Bundle.getMessage("Incoming"),null) {
016        @Override
017        public int action(AbstractMessage m, CbusFilter cf) {
018            if (m instanceof jmri.jmrix.can.CanReply) {
019                return super.action(m,cf);
020            }
021            return -1;
022        } },
023    CFOUT(Bundle.getMessage("Outgoing"),null) {
024        @Override
025        public int action(AbstractMessage m, CbusFilter cf) {
026            if (m instanceof jmri.jmrix.can.CanMessage) {
027                return super.action(m, cf);
028            }
029            return -1;
030        } },
031    CFEVENT(Bundle.getMessage("CbusEvents"),null),
032    CFEVENTMIN(Bundle.getMessage("MinEvent"),CFEVENT) {
033        @Override
034        public int action(AbstractMessage m, CbusFilter cf) {
035            if ( CbusMessage.getEvent(m) < cf.getEvMin()) {
036                return super.action(m,cf);
037            }
038            return -1;
039        }
040    },
041    CFEVENTMAX(Bundle.getMessage("MaxEvent"),CFEVENT) {
042        @Override
043        public int action(AbstractMessage m, CbusFilter cf) {
044            if ( CbusMessage.getEvent(m) > cf.getEvMax()){
045                return super.action(m,cf);
046            }
047            return -1;
048        } },
049    CFON(Bundle.getMessage("CbusOnEvents"),CFEVENT),
050    CFOF(Bundle.getMessage("CbusOffEvents"),CFEVENT),
051    CFSHORT(Bundle.getMessage("ShortEvents"),CFEVENT),
052    CFLONG(Bundle.getMessage("LongEvents"),CFEVENT),
053    CFSTD(Bundle.getMessage("StandardEvents"),CFEVENT),
054    CFREQUEST(Bundle.getMessage("RequestEvents"),CFEVENT),
055    CFRESPONSE(Bundle.getMessage("ResponseEvents"),CFEVENT),
056    CFED0(Bundle.getMessage("EVD0"),CFEVENT),
057    CFED1(Bundle.getMessage("EVD1"),CFEVENT),
058    CFED2(Bundle.getMessage("EVD2"),CFEVENT),
059    CFED3(Bundle.getMessage("EVD3"),CFEVENT),
060
061    CFDATA(Bundle.getMessage("OPC_DA"),null),
062    CFACDAT("ACDAT",CFDATA),
063    CFDDES("DDES + DDWS",CFDATA),
064    CFRQDAT("RQDAT",CFDATA),
065    CFARDAT("ARDAT",CFDATA),
066    CFDDRS("DDRS",CFDATA),
067    CFRQDDS("RQDDS",CFDATA),
068    CFCABDAT("CABDAT",CFDATA),
069
070    CFCS(Bundle.getMessage("CommandStation"),null),
071    CFCSAQRL(Bundle.getMessage("LocoCommands"),CFCS),
072    CFCSKA(Bundle.getMessage("KeepAlive"),CFCS),
073    CFCSDSPD(Bundle.getMessage("SpeedDirection"),CFCS),
074    CFCSFUNC(Bundle.getMessage("Functions"),CFCS),
075    CFCSPROG(Bundle.getMessage("Programming"),CFCS),
076    CFCSLC(Bundle.getMessage("LayoutCommands"),CFCS),
077    CFCSC(Bundle.getMessage("CommandStationControl"),CFCS),
078
079    CFNDCONFIG(Bundle.getMessage("NodeConfiguration"),null),
080    CFNDSETUP(Bundle.getMessage("GeneralNodeSetup"),CFNDCONFIG),
081    CFNDVAR(Bundle.getMessage("NodeVariables"),CFNDCONFIG),
082    CFNDEV(Bundle.getMessage("NodeEvents"),CFNDCONFIG),
083    CFNDNUM(Bundle.getMessage("NodeNumbers"),CFNDCONFIG),
084
085    CFMISC(Bundle.getMessage("Misc"),null),
086    CFEXTRTR("Extended / RTR",CFMISC){
087        @Override 
088        public int action(AbstractMessage m, CbusFilter cf) {
089        if (m instanceof CanFrame && ((CanFrame) m).extendedOrRtr() ) {
090            if ( cf.isFilterActive(ordinal()) ){
091                return ordinal();
092            } else {
093                return -2; // special return as unable to contiinue filtering if extended or rtr
094            }
095        }
096        return -1;
097        } },
098    CFNETWK(Bundle.getMessage("NetworkCommands"),CFMISC),
099    CFCLOCK(Bundle.getMessage("CBUS_FCLK"),CFMISC),
100    CFOTHER(Bundle.getMessage("Others"),CFMISC),
101    CFUNKNOWN(Bundle.getMessage("Unknown"),CFMISC),
102
103    CFNODE(Bundle.getMessage("CbusNodes"),null) {
104        @Override
105        public String getToolTip(){
106            return null;
107        }
108
109    },
110    CFNODEMIN(Bundle.getMessage("MinNode"),CFNODE) {
111        @Override
112        public int action(AbstractMessage m, CbusFilter cf) {
113            if ( CbusMessage.getNodeNumber(m) < cf.getNdMin()){
114                return super.action(m, cf);
115            }
116            return -1;
117        }
118    },
119    CFNODEMAX(Bundle.getMessage("MaxNode"),CFNODE) {
120        @Override
121        public int action(AbstractMessage m, CbusFilter cf) {
122            if ( CbusMessage.getNodeNumber(m) > cf.getNdMax()){
123                return super.action(m, cf);
124            }
125            return -1;
126        } };
127
128    /**
129     * Perform Filter check for a particular message.
130     * Can be overridden by specific filters.
131     * 
132     * @param m CanMessage or CanReply
133     * @param cf main CbusFilter instance
134     * @return Filter category which blocked, else -1 or -2 if passed 
135     */
136    public int action(AbstractMessage m, @Nonnull CbusFilter cf){
137        if ( cf.isFilterActive(ordinal()) ){
138            return ordinal();
139        } else {
140            return -1;
141        }
142    }
143
144    private final String _bundleString;
145    private final CbusFilterType _category;
146
147    /**
148     * Create new CbusFilterType.
149     */
150    CbusFilterType(String bundle, CbusFilterType category) {
151        this._bundleString=bundle;
152        this._category = category;
153    }
154
155    /**
156     * Get Filter Name
157     * @return Filter Name
158     */
159    public final String getName(){
160        return _bundleString;
161    }
162
163    /**
164     * Get Filter Category
165     * @return Filter Category, else null if Category Head
166     */
167    @CheckForNull
168    public final CbusFilterType getCategory() {
169        return _category;
170    }
171
172    /**
173     * Get an EnumSet of Category Heads
174     * @return set
175     */
176    public static final java.util.Set<CbusFilterType> getCatHeads() {
177        EnumSet<CbusFilterType> catSet = EnumSet.noneOf(CbusFilterType.class);
178        catSet.add(CFEVENT);
179        catSet.add(CFNODE);
180        catSet.add(CFDATA);
181        catSet.add(CFCS);
182        catSet.add(CFNDCONFIG);
183        catSet.add(CFMISC);
184        return catSet;
185    }
186
187    /**
188     * Is the Filter a parent of a category?
189     * @return true if category parent
190     */
191    public final boolean isCategoryHead() {
192        return getCatHeads().contains(this);
193    }
194
195    /**
196     * Should the Filter always be displayed?
197     * @return true if category head or in / out filter.
198     */
199    public final boolean alwaysDisplay() {
200        java.util.Set<CbusFilterType> alwaysDisplay = getCatHeads();
201        alwaysDisplay.add(CFIN);
202        alwaysDisplay.add(CFOUT);
203        return alwaysDisplay.contains(this);
204    }
205
206    /**
207     * Get if the Filter needs to display a number spinner
208     * @return true to display a spinner
209     */
210    public final boolean showSpinners() {
211        EnumSet<CbusFilterType> spinnerSet = EnumSet.noneOf(CbusFilterType.class);
212        spinnerSet.addAll(EnumSet.of(CFEVENTMIN,CFEVENTMAX,CFNODEMIN,CFNODEMAX));
213        return spinnerSet.contains(this);
214    }
215
216    /**
217     * Get All Filters for a particular OPC
218     * @param opc OPC to get Filter List for
219     * @return set of Filters to use for the OPC.
220     */
221    @Nonnull
222    public static final EnumSet<CbusFilterType> allFilters(int opc) {
223        EnumSet<CbusFilterType> mergedSet = EnumSet.noneOf(CbusFilterType.class);
224        mergedSet.addAll(EnumSet.of(CFIN,CFOUT,CFEXTRTR));
225        mergedSet.addAll(CbusOpCodes.getOpcFilters(opc));
226        if (mergedSet.contains(CbusFilterType.CFEVENT)){
227            mergedSet.addAll(EnumSet.of(CbusFilterType.CFEVENTMIN,CbusFilterType.CFEVENTMAX));
228        }
229        if (mergedSet.contains(CbusFilterType.CFNODE)){
230           mergedSet.addAll(EnumSet.of(CbusFilterType.CFNODEMIN,CbusFilterType.CFNODEMAX));
231        }
232        return mergedSet;
233    }
234
235    /**
236     * Get ToolTip Text for the Filter
237     * @return HMTL list of OPCs with description, may be null if no ToolTip
238     */
239    @CheckForNull
240    public String getToolTip(){
241        StringBuilder t = new StringBuilder();
242        for ( int i=0 ; (i < 257) ; i++) {
243            if (CbusOpCodes.getOpcFilters(i).contains(this) 
244            && !CbusOpCodes.getOpcName(i).isEmpty()){
245                t.append(CbusOpCodes.getOpcName(i))
246                .append(" : ")
247                .append(Bundle.getMessage("CBUS_" + CbusOpCodes.getOpcName(i)))
248                .append(" : ")
249                .append(Bundle.getMessage("CTIP_" + CbusOpCodes.getOpcName(i)))
250                .append("<br>");
251            }
252        }
253        if (!t.toString().isEmpty()){
254            t.insert(0,"<html>");
255            t.append("</html>");
256            return t.toString();
257        }
258        return null;
259    }
260
261    /**
262     * Get Filter Type by name.
263     * @param name the #getName string to search for.
264     * @return Filter Type, or null if not found.
265     */
266    @CheckForNull
267    public static CbusFilterType getFilterByName(String name) {
268        for ( CbusFilterType type : CbusFilterType.values() ) {
269            if ( type.getName().equals(name) ) {
270                return type;
271            }
272        }
273        return null;
274    }
275
276}