001package jmri.jmrix.can.cbus;
002
003import java.util.HashMap;
004import javax.annotation.Nonnull;
005import jmri.jmrix.AbstractMessage;
006import jmri.jmrix.can.cbus.swing.CbusFilterFrame;
007import jmri.util.ThreadingUtil;
008
009// import org.slf4j.Logger;
010// import org.slf4j.LoggerFactory;
011
012/**
013 * Class to implement filtering of CBUS frames.
014 * Long event OPCs are not altered for a node number of 0
015 * @author Steve Young (C) 2018, 2020
016 */
017public class CbusFilter {
018    private final HashMap<Integer, Boolean> _boolean_hash_map;
019    private final CbusFilterFrame _filterFrame;
020    private final HashMap<Integer, Integer> _nodes_hashmap;
021    private int _evMin = 0;
022    private int _evMax = 0;
023    private int _ndMin = 0;
024    private int _ndMax = 0;
025
026    public static final int CFMAXCATS =CbusFilterType.values().length;
027    public static final int CFMAX_NODES = 100;
028
029    /**
030     * Creates a new instance of CbusFilter
031     * @param filterFrame The Instance Frame
032     */
033    public CbusFilter(CbusFilterFrame filterFrame) {
034        _filterFrame = filterFrame;
035        _boolean_hash_map = new HashMap<>(getHMapSize(CFMAXCATS + CFMAX_NODES));
036        _nodes_hashmap = new HashMap<>(getHMapSize(CFMAX_NODES));
037        for ( int i=0 ; (i < CFMAXCATS + CFMAX_NODES) ; i++){
038            _boolean_hash_map.put(i,false);
039        }
040    }
041
042    /**
043     * Filter CanMessage or CanReply.
044     *
045     * @param test Message to Test
046     * @return Filter number which failed, else -1
047     */
048    public int filter( @Nonnull AbstractMessage test) {
049        for (CbusFilterType singleFilter : CbusFilterType.allFilters(test.getElement(0))) {
050            int _toReturn = singleFilter.action(test,this);
051            if (_toReturn>-1){
052                return _toReturn;
053            }
054            else if (_toReturn==-2){ // Extended or RTR CAN Frame, No OPC's to filter.
055                break;
056            }
057        }
058        return -1;
059    }
060
061    /**
062     * Get position in Node List for a given Node Number
063     * @param node Node Number
064     * @param nodes Main Node map.
065     * @return ID used in main Boolean filter list / CbusFilterPanel ID
066     */
067    private static int positionInNodeList(int node, @Nonnull HashMap<Integer,Integer> nodes){
068        for (var o : nodes.entrySet()) {
069            if (o.getValue().equals(node)) {
070                return o.getKey();
071            }
072        }
073        return -1;
074    }
075
076    /**
077     * Get the main Filter Frame.
078     * @return CbusFilterFrame instance
079     */
080    protected CbusFilterFrame getCbusFilterFrame() {
081        return _filterFrame;
082    }
083
084    /**
085     * Get Map of Filters.
086     * @return Map of Boolean values.
087     */
088    protected final HashMap<Integer,Boolean> getActiveFilters(){
089        return _boolean_hash_map;
090    }
091
092    /**
093     * Get Map of Nodes.
094     * @return Map of Node Numbers.
095     */
096    protected final HashMap<Integer,Integer> getNodes() {
097        return _nodes_hashmap;
098    }
099
100    /**
101     * Perform Node checks for a given node number.
102     * If a new Node Number is found, is added to the main Node List
103     * and a new filter created.
104     *
105     * @param node Node Number
106     * @param cf CbusFilter instance
107     * @return Filter number which failed, else -1
108     */
109    protected static int checknode(int node,@Nonnull CbusFilter cf) {
110
111        if (!cf.getNodes().containsValue(node)){
112            cf.getNodes().put(cf.getNodes().size()+ CFMAXCATS,node);
113            // log.info("added new node {} to position {}",node,positionInNodeList(node,cf.getNodes()));
114            if (cf.getCbusFilterFrame() !=null) {
115                cf.getCbusFilterFrame().addNode(node,(positionInNodeList(node,cf.getNodes())));
116            }
117        }
118
119        if ( cf.getActiveFilters().get(CbusFilterType.CFNODE.ordinal())){
120            return CbusFilterType.CFNODE.ordinal();
121        } else {
122            incrementCount(CbusFilterType.CFNODE.ordinal(),cf); }
123        if ( cf.getActiveFilters().get(positionInNodeList(node,cf.getNodes())) ){
124            return positionInNodeList(node,cf.getNodes());
125        } else {
126            incrementCount(positionInNodeList(node,cf.getNodes()),cf); }
127        return -1;
128    }
129
130    /**
131     * Increment Filter count for a given filter ID.
132     * @param filternum Filter ID
133     * @param cf CbusFilter instance
134     */
135    protected static void incrementCount(int filternum, @Nonnull CbusFilter cf){
136        // log.debug("increment count {}",filternum);
137        if (cf.getCbusFilterFrame() != null ) {
138            ThreadingUtil.runOnGUIEventually( ()->{
139                cf.getCbusFilterFrame().passIncrement(filternum);
140            });
141        }
142    }
143
144    /**
145     * Set a single Filter to pass or filter.
146     * @param id Filter ID
147     * @param trueorfalse true to filter, false to pass through.
148     */
149    public void setFilter(int id, boolean trueorfalse) {
150        _boolean_hash_map.put(id, trueorfalse);
151    }
152
153    /**
154     * Set the event or node min and max values.
155     *
156     * @param filter CFEVENTMIN, CFEVENTMAX, CFNODEMIN or CFNODEMAX
157     * @param val min or max value
158     */
159    public void setMinMax(@Nonnull CbusFilterType filter, int val){
160        switch (filter) {
161            case CFEVENTMIN:
162                _evMin = val;
163                break;
164            case CFEVENTMAX:
165                _evMax = val;
166                break;
167            case CFNODEMIN:
168                _ndMin = val;
169                break;
170            case CFNODEMAX:
171                _ndMax = val;
172                break;
173            default:
174                break;
175        }
176    }
177
178    /**
179     * Get Minimum Event Number.
180     * @return Minimum Event
181     */
182    public int getEvMin() {
183        return _evMin;
184    }
185
186    /**
187     * Get Maximum Event Number.
188     * @return Maximum Event
189     */
190    public int getEvMax() {
191        return _evMax;
192    }
193
194    /**
195     * Get Minimum Node Number.
196     * @return Minimum Node
197     */
198    public int getNdMin() {
199        return _ndMin;
200    }
201
202    /**
203     * Get Maximum Node Number.
204     * @return Maximum Node
205     */
206    public int getNdMax() {
207        return _ndMax;
208    }
209
210    /**
211     * Get optimum HashMap Size.
212     * @param reqdCapacity Required finite capacity
213     * @return value to use on creation
214     */
215    public static final int getHMapSize(int reqdCapacity){
216        return ((reqdCapacity*4+2)/3);
217    }
218
219    // private final static Logger log = LoggerFactory.getLogger(CbusFilter.class);
220}