gt: 						i = vals.length - 1;
context: /*
   * To change this template, choose Tools | Templates
   * and open the template in the editor.
   */
  package eu.seebetter.ini.chips.davis;
  
  import java.awt.Color;
  import java.awt.event.MouseWheelEvent;
  import java.awt.event.MouseWheelListener;
  import java.beans.PropertyChangeEvent;
  import java.beans.PropertyChangeListener;
  import java.util.Observable;
  import java.util.Observer;
  import java.util.logging.Logger;
  
  import javax.swing.JSpinner;
  
  import ch.unizh.ini.jaer.chip.retina.DVSTweaks;
  import eu.seebetter.ini.chips.DavisChip;
  import net.sf.jaer.biasgen.PotTweaker;
  import net.sf.jaer.chip.AEChip;
  import net.sf.jaer.chip.Chip;
  import net.sf.jaer.graphics.AEChipRenderer;
  import net.sf.jaer.graphics.AEFrameChipRenderer;
  
  /**
   * Wraps some key apsDVS sensor control in more user-friendly control panel.
   *
   * @author tobi
   */
  public class DavisUserControlPanel extends javax.swing.JPanel implements PropertyChangeListener, Observer {
  
  	private static final long serialVersionUID = 3894988983329773402L;
  
  	protected DavisChip chip = null;
  	protected DavisTweaks apsDvsTweaks;
  	DavisConfig.VideoControl videoControl;
  	DavisVideoContrastController contrastController;
  	AEChipRenderer renderer = null;
  
  	private static final Logger log = Logger.getLogger("DVSFunctionalControlPanel");
  
  	/**
  	 * Creates new form ApsDVSUserControlPanel
  	 */
  	public DavisUserControlPanel(final Chip chip) {
  		this.chip = (DavisChip) chip; // will throw ClassCastException if not right kind of chip.
  		renderer = this.chip.getRenderer();
  		apsDvsTweaks = (DavisTweaks) chip.getBiasgen();
  		initComponents();
  		// code must be after initComponents so that these components exist
  		final PotTweaker[] tweakers = { thresholdTweaker, onOffBalanceTweaker, maxFiringRateTweaker, bandwidthTweaker };
  		for (final PotTweaker tweaker : tweakers) {
  			chip.getBiasgen().getSupport().addPropertyChangeListener(tweaker); // to reset sliders on load/save of
  		}
  
  		histCB.setSelected(this.chip.isShowImageHistogram());
  		fdSp.setValue(getConfig().getFrameDelayMs());
  		edSp.setValue(getConfig().getExposureDelayMs());
  		glShutterCB.setSelected(((DavisBaseCamera) chip).getDavisConfig().isGlobalShutter());
  		displayEventsCheckBox.setSelected(getConfig().isDisplayEvents());
  		displayFramesCheckBox.setSelected(getConfig().isDisplayFrames());
  		captureFramesCheckBox.setSelected(getConfig().isCaptureFramesEnabled());
  		captureEventsCB.setSelected(getConfig().isCaptureEventsEnabled());
  		autoshotThresholdSp.setValue(this.chip.getAutoshotThresholdEvents() >> 10);
  		final int[] vals = { 10, 100, 1000 }, mults = { 1, 10, 100 };
  		final float[] fvals = { .02f, .2f, 2, 20, 200, 2000 }, fmults = { .001f, .01f, .1f, 1, 10, 100 };
  		autoshotThresholdSp.addMouseWheelListener(new SpinnerMouseWheelIntHandler(vals, mults));
  		autoExpCB.setSelected(this.chip.isAutoExposureEnabled());
  		fdSp.addMouseWheelListener(new SpinnerMouseWheelFloatHandler(fvals, fmults));
  		edSp.addMouseWheelListener(new SpinnerMouseWheelFloatHandler(fvals, fmults));
  		dvsContSp.addMouseWheelListener(new SpinnerMouseWheelIntHandler(new int[] { 10, 20 }, new int[] { 1, 2 }));
  		dvsContSp.setValue(renderer.getColorScale());
  		contrastSp.addMouseWheelListener(new SpinnerMouseWheelFloatHandler(new float[] { 1, 2 }, new float[] { .05f, .1f }));
  		setDvsColorModeRadioButtons();
  		renderer.getSupport().addPropertyChangeListener(this);
  		if (getConfig() instanceof DavisConfig) {
  			// add us as observer for various property changes
  			final DavisConfig config = getConfig();
  			videoControl = config.getVideoControl();
  			videoControl.addObserver(this); // display of various events/frames enabled comes here
  			// config.exposureControlRegister.addObserver(this);
  			// config.frameDelayControlRegister.addObserver(this); // TODO generates loop that resets the spinner if the
  			// new spinner
  			// value does not change the exposureControlRegister in ms according to new register value
  			imuVisibleCB.setSelected(config.isDisplayImu());
  			imuEnabledCB.setSelected(config.isImuEnabled());
  		}
  		contrastController = ((AEFrameChipRenderer) ((AEChip) chip).getRenderer()).getContrastController();
  		contrastController.addObserver(this); // get updates from contrast controller
  		contrastSp.setValue(contrastController.getContrast());
  		contrastSp.setEnabled(!contrastController.isUseAutoContrast());
  		autoContrastCB.setSelected(contrastController.isUseAutoContrast());
  		this.chip.addObserver(this);
  		chip.getSupport().addPropertyChangeListener(this);
  		chip.getBiasgen().getSupport().addPropertyChangeListener(this);
  
  	}
  
  	private void setDvsColorModeRadioButtons() {
  		switch (renderer.getColorMode()) {
  			case RedGreen:
  				dvsRedGrRB.setSelected(true);
  				break;
  			case GrayLevel:
  				dvsGrayRB.setSelected(true);
  				break;
  			default:
  				dvsColorButGrp.clearSelection();
  		}
  	}
  
  	private DavisConfig getConfig() {
  		return ((DavisBaseCamera) chip).getDavisConfig();
  	}
  
  	private class SpinnerMouseWheelIntHandler implements MouseWheelListener {
  
  		private final int[] vals, mults;
  
  		/**
  		 * Constructs a new instance
  		 *
  		 * @param vals
  		 *            the threshold values
  		 * @param mults
  		 *            the multipliers for spinner values less than or equal to
  		 *            that number
  		 */
  		SpinnerMouseWheelIntHandler(final int[] vals, final int[] mults) {
  			if ((vals == null) || (mults == null)) {
  				throw new RuntimeException("vals or mults is null");
  			}
  			if (vals.length != mults.length) {
  				throw new RuntimeException("vals and mults array must be same length and they are not: vals.length=" + vals.length
  					+ " mults.length=" + mults.length);
  			}
  			this.vals = vals;
  			this.mults = mults;
  		}
  
  		@Override
  		public void mouseWheelMoved(final MouseWheelEvent mwe) {
  			final JSpinner spinner = (JSpinner) mwe.getSource();
  			if (mwe.getScrollType() != MouseWheelEvent.WHEEL_UNIT_SCROLL) {
  				return;
  			}
  			try {
  				int value = (Integer) spinner.getValue();
  
  				int i = 0;
  				for (i = 0; i < vals.length; i++) {
  					if (value < vals[i]) {
  						break;
  					}
  				}
  				if (i >= vals.length) {
  					i = vals.length - 1;
  				}
  				final int mult = mults[i];
  				value -= mult * mwe.getWheelRotation();
  				if (value < 0) {
  					value = 0;
  				}
  				spinner.setValue(value);
  			}
  			catch (final Exception e) {
  				DavisUserControlPanel.log.warning(e.toString());
  				return;
  			}
  		}
  	}
  
  	private class SpinnerMouseWheelFloatHandler implements MouseWheelListener {
  
  		private final float[] vals, mults;
  
  		/**
  		 * Constructs a new instance
  		 *
  		 * @param vals
  		 *            the threshold values
  		 * @param mults
  		 *            the multipliers for spinner values less than or equal to
  		 *            that number
  		 */
  		SpinnerMouseWheelFloatHandler(final float[] vals, final float[] mults) {
  			if ((vals == null) || (mults == null)) {
  				throw new RuntimeException("vals or mults is null");
  			}
  			if (vals.length != mults.length) {
  				throw new RuntimeException("vals and mults array must be same length and they are not: vals.length=" + vals.length
  					+ " mults.length=" + mults.length);
  			}
  			this.vals = vals;
  			this.mults = mults;
  		}
  
  		@Override
  		public void mouseWheelMoved(final MouseWheelEvent mwe) {
  			final JSpinner spinner = (JSpinner) mwe.getSource();
  			if (mwe.getScrollType() != MouseWheelEvent.WHEEL_UNIT_SCROLL) {
  				return;
  			}
  			try {
  				float value = (Float) spinner.getValue();
  				int i = 0;
  				// int rot = mwe.getWheelRotation(); // >0 is roll down, want smaller value
  				if (true) {
  					for (i = 0; i < vals.length; i++) {
  						if (value <= vals[i]) {
  							break; // take value from vals array that is just above our current value, e.g. if our
  									// current value is 11, then take 20 (if that is next higher vals) as next value and
  									// mult of 20 value as decrement amount
  						}
  					}
  					if (i > (vals.length - 1)) {
  