/*******************************************************************************
 Channel Settings
 A Qt Widget to control the per-channel settings of Tecella Amplifiers.
********************************************************************************
Copyright (c) 2010 Tecella LLC
Licensed under the MIT License
*******************************************************************************/

#include "channel_settings.h"

#include <QtGui>
#include <QApplication>
#include <QMessageBox>

#include "TecellaAmp.h"
#include "acquisition_interrupter.h"

#include <sstream>

//***************************************************************************
// Helper functions
//***************************************************************************


//Used to aid boost::bind, since it's limited in how many parameters it can take.
//Dependency on boost::bind has been removed, but this helper function remains, just because.
struct AutoCompArgs
{
	double v_hold;
	double t_hold;
	double v_step;
	double t_step;
	bool use_leak;
	bool use_digital_leak;
	bool use_cfast;
	bool use_cslowA;
	bool use_cslowB;
	bool use_cslowC;
	bool use_cslowD;
	bool use_artifact;
	double under_comp_coefficient;
	int acq_iterations;
	int stimulus_index;
};

void tecella_auto_comp_bind_helper(TECELLA_HNDL h, AutoCompArgs &args) {
	tecella_auto_comp(h,
			  args.v_hold, args.t_hold, args.v_step, args.t_step,
			  args.use_leak, args.use_digital_leak,
			  args.use_cfast,
			  args.use_cslowA, args.use_cslowB,
			  args.use_cslowC, args.use_cslowD,
			  args.use_artifact,
			  args.under_comp_coefficient,
			  args.acq_iterations,
			  args.stimulus_index );
}

static void check_floating_point_exception()
{
	//Previously implemented, but not currently used.
	//Can be used for debug of the TecellaAmp API.
}

//***************************************************************************
// AutoCompThread
// Performs various auto compensations.
//***************************************************************************
class AutoCompThread : public QThread
{
public:
	AutoCompThread() : QThread(0) {}
	~AutoCompThread() {}

	void setHandle(TECELLA_HNDL handle_new) { handle = handle_new; }
	void setArgs( const AutoCompArgs &args_new ) { args = args_new; }
	void run()
	{
		tecella_auto_comp_bind_helper(handle, args);
	}

private:
	TECELLA_HNDL handle;
	AutoCompArgs args;
};


//***************************************************************************
// AutoOffsetThread
// Uses a jp of 0mV and 50mV and interpolates to determine the best jp
// that zeros the response when the stimulus is 0mV.
//***************************************************************************
class AutoOffsetThread : public QThread
{
public:
	AutoOffsetThread() : QThread(0) {}
	~AutoOffsetThread() {}

	void setHandle(TECELLA_HNDL handle_new) { handle = handle_new; }
	void run()
	{
		tecella_auto_offset( handle, 50, 0 );
	}

private:
	TECELLA_HNDL handle;
};

//***************************************************************************
// AutoArtifactThread
//***************************************************************************
class AutoArtifactThread : public QThread
{
public:
	AutoArtifactThread(TECELLA_HNDL handle, double v_hold, double t_hold,
			   double v_step, double t_step, int quality, int stimulus_index )
				   : QThread(0), handle(handle),
				   v_hold(v_hold), t_hold(t_hold), v_step(v_step), t_step(t_step),
				   quality(quality), stimulus_index(stimulus_index)
	{
	}

	void run()
	{
		tecella_auto_artifact_update( handle,
					      v_hold, t_hold, v_step, t_step, quality, stimulus_index );
	}

private:
	TECELLA_HNDL handle;
	double v_hold, t_hold, v_step, t_step;
	int quality, stimulus_index;
};


//***************************************************************************
// ChannelSettings
//***************************************************************************
ChannelSettings::ChannelSettings(QWidget *parent)
	: QWidget(parent), amp_handle(-1), acquisition_interrupter(0), updatingUI(false)
{
	digital_leak_display_scale = 1e6;
	digital_leak_min = -1;
	digital_leak_max = 1;
	digital_leak_lsb = 1e-9;
	digital_leak_steps = (digital_leak_max - digital_leak_min) / digital_leak_lsb + .5;
	digital_leak_precision = 9;

	ui.setupUi(this);

	//Align the JP enable checkbox to be between JP and JP Fine
	ui.gridLayout_ManualCompensation->addWidget( ui.CheckBox_JP, 0, 0, 2, 1, Qt::AlignVCenter );
	ui.gridLayout_ManualCompensation->addWidget( ui.CheckBox_Leak, 4, 0, 2, 1, Qt::AlignVCenter );

	connect( ui.widget_ChannelSelector, SIGNAL(selectedChannelsChanged(std::vector<int> &)),
		 this, SLOT(selectedChannelsChanged(std::vector<int> &)) );

	QObject::connect(ui.comboBox_Source, SIGNAL(currentIndexChanged(int)), this, SLOT(selectSource(int)));
	QObject::connect(ui.comboBox_Gain, SIGNAL(currentIndexChanged(int)), this, SLOT(selectGain(int)));
//	QObject::connect(ui.comboBox_Gain1, SIGNAL(currentIndexChanged(int)), this, SLOT(selectGain1(int)));
//	QObject::connect(ui.comboBox_Gain2, SIGNAL(currentIndexChanged(int)), this, SLOT(selectGain2(int)));
	QObject::connect(ui.comboBox_Stimulus, SIGNAL(currentIndexChanged(int)), this, SLOT(selectStimulus(int)));

	//Auto compensation
	QObject::connect(ui.PushButton_AutoOffset, SIGNAL(clicked()), this, SLOT(doAutoOffset()));
	QObject::connect(ui.PushButton_AutoLeak, SIGNAL(clicked()), this, SLOT(doAutoLeak()));
	QObject::connect(ui.PushButton_AutoCapacitance, SIGNAL(clicked()), this, SLOT(doAutoCapacitance()));
	QObject::connect(ui.comboBox_OffsetAdjustMode, SIGNAL(currentIndexChanged(int)), this, SLOT(selectOffsetAdjustMode(int)));
	
	//Enables/disables
	QObject::connect(ui.CheckBox_Leak,    SIGNAL(toggled(bool)), this, SLOT(enableLeak(bool)));
	QObject::connect(ui.CheckBox_LeakD,   SIGNAL(toggled(bool)), this, SLOT(enableLeakD(bool)));
	QObject::connect(ui.CheckBox_RSeries, SIGNAL(toggled(bool)), this, SLOT(enableRSeries(bool)));
        QObject::connect(ui.checkBox_EnableRs100, SIGNAL(toggled(bool)), this, SLOT(enableRs100(bool)));
        QObject::connect(ui.checkBox_IclampOn, SIGNAL(toggled(bool)), this, SLOT(IclampOn(bool)));
        QObject::connect(ui.CheckBox_JP,      SIGNAL(toggled(bool)), this, SLOT(enableJP(bool)));
	QObject::connect(ui.CheckBox_Bessel,  SIGNAL(toggled(bool)), this, SLOT(enableBessel(bool)));
	QObject::connect(ui.CheckBox_CFast,   SIGNAL(toggled(bool)), this, SLOT(enableCFast(bool)));
	QObject::connect(ui.CheckBox_CSlowA,  SIGNAL(toggled(bool)), this, SLOT(enableCSlowA(bool)));
	QObject::connect(ui.CheckBox_CSlowB,  SIGNAL(toggled(bool)), this, SLOT(enableCSlowB(bool)));
	QObject::connect(ui.CheckBox_CSlowC,  SIGNAL(toggled(bool)), this, SLOT(enableCSlowC(bool)));
	QObject::connect(ui.CheckBox_CSlowD,  SIGNAL(toggled(bool)), this, SLOT(enableCSlowD(bool)));
	QObject::connect(ui.checkBox_Artifact,SIGNAL(toggled(bool)), this, SLOT(enableArtifact(bool)));
	
	//Spin boxes
	QObject::connect(ui.SpinBox_Leak,    SIGNAL(valueChanged(double)), this, SLOT(setLeak(double)));
	QObject::connect(ui.SpinBox_LeakFine,SIGNAL(valueChanged(double)), this, SLOT(setLeakFine(double)));
	QObject::connect(ui.SpinBox_LeakD,   SIGNAL(valueChanged(double)), this, SLOT(setLeakD(double)));
	QObject::connect(ui.SpinBox_RSeries, SIGNAL(valueChanged(double)), this, SLOT(setRSeries(double)));
	QObject::connect(ui.SpinBox_JP,      SIGNAL(valueChanged(double)), this, SLOT(setJP(double)));
	QObject::connect(ui.SpinBox_JPFine,  SIGNAL(valueChanged(double)), this, SLOT(setJPFine(double)));
	QObject::connect(ui.SpinBox_IcmdOffset,  SIGNAL(valueChanged(double)), this, SLOT(setIcmdOffset(double)));
	QObject::connect(ui.SpinBox_Bessel,  SIGNAL(valueChanged(int)), this, SLOT(setBessel(int)));
	QObject::connect(ui.SpinBox_CFast,   SIGNAL(valueChanged(double)), this, SLOT(setCFast(double)));
	QObject::connect(ui.SpinBox_CSlowA,  SIGNAL(valueChanged(double)), this, SLOT(setCSlowA(double)));
	QObject::connect(ui.SpinBox_CSlowB,  SIGNAL(valueChanged(double)), this, SLOT(setCSlowB(double)));
	QObject::connect(ui.SpinBox_CSlowC,  SIGNAL(valueChanged(double)), this, SLOT(setCSlowC(double)));
	QObject::connect(ui.SpinBox_CSlowD,  SIGNAL(valueChanged(double)), this, SLOT(setCSlowD(double)));
	QObject::connect(ui.SpinBox_HPF,     SIGNAL(valueChanged(double)), this, SLOT(setHPF(double)));
	QObject::connect(ui.SpinBox_BoostFrequency, SIGNAL(valueChanged(double)), this, SLOT(setBoostFrequency(double)));
	QObject::connect(ui.SpinBox_BoostGain, SIGNAL(valueChanged(double)), this, SLOT(setBoostGain(double)));
	
	//Horizontal sliders
	QObject::connect(ui.HSlider_Leak,    SIGNAL(valueChanged(int)), this, SLOT(setLeak(int)));
	QObject::connect(ui.HSlider_LeakFine,SIGNAL(valueChanged(int)), this, SLOT(setLeakFine(int)));
	QObject::connect(ui.HSlider_LeakD,   SIGNAL(valueChanged(int)), this, SLOT(setLeakD(int)));
        QObject::connect(ui.HSlider_RSeries, SIGNAL(valueChanged(int)), this, SLOT(setRSeries(int)));
        QObject::connect(ui.HSlider_JP,      SIGNAL(valueChanged(int)), this, SLOT(setJP(int)));
	QObject::connect(ui.HSlider_JPFine,  SIGNAL(valueChanged(int)), this, SLOT(setJPFine(int)));
	QObject::connect(ui.HSlider_IcmdOffset,  SIGNAL(valueChanged(int)), this, SLOT(setIcmdOffset(int)));
	QObject::connect(ui.HSlider_Bessel,  SIGNAL(valueChanged(int)), this, SLOT(setBessel(int)));
	QObject::connect(ui.HSlider_CFast,   SIGNAL(valueChanged(int)), this, SLOT(setCFast(int)));
	QObject::connect(ui.HSlider_CSlowA,  SIGNAL(valueChanged(int)), this, SLOT(setCSlowA(int)));
	QObject::connect(ui.HSlider_CSlowB,  SIGNAL(valueChanged(int)), this, SLOT(setCSlowB(int)));
	QObject::connect(ui.HSlider_CSlowC,  SIGNAL(valueChanged(int)), this, SLOT(setCSlowC(int)));
	QObject::connect(ui.HSlider_CSlowD,  SIGNAL(valueChanged(int)), this, SLOT(setCSlowD(int)));
	QObject::connect(ui.HSlider_HPF,     SIGNAL(valueChanged(int)), this, SLOT(setHPF(int)));
	QObject::connect(ui.HSlider_BoostFrequency, SIGNAL(valueChanged(int)), this, SLOT(setBoostFrequency(int)));
	QObject::connect(ui.HSlider_BoostGain, SIGNAL(valueChanged(int)), this, SLOT(setBoostGain(int)));

	//Zap
	QObject::connect(ui.pushButton_Zap, SIGNAL(toggled(bool)), this, SLOT(doZap(bool)));
	QObject::connect(ui.spinBox_Zap_dur, SIGNAL(valueChanged(int)), this, SLOT(zapDurationChanged(int)) );
	connect( ui.checkBox_ZapPulse, SIGNAL(toggled(bool)), this, SLOT(useZapPulse(bool)) );
}

void ChannelSettings::setAmpHandle( TECELLA_HNDL amp_handle_new )
{
	//This function updates all the UI elements to have the proper
	// units, ranges, and selections for the specified amplifier.
	//It also hides/disables UI elements that do not apply to the amp.

	bool reset_settings = false;
	const wchar_t *label;
	TECELLA_REG_PROPS reg_props;

	bool bvalue;

	amp_handle = amp_handle_new;
	TECELLA_ERRNUM e1 = tecella_get_hw_props(amp_handle, &hwprops);
	TECELLA_ERRNUM e2 = tecella_get_hw_props_ex_01(amp_handle, &hwprops_ex_01);
	if(e1!=TECELLA_ERR_OK || e2!=TECELLA_ERR_OK)
	{
		//If we can't get the hwprops then the amplifier handle is bad
		ui.widget_ChannelSelector->setChannelCount(0);
		amp_handle = -1;
		selected_channels.clear();
		return;
	}

	ui.widget_ChannelSelector->setChannelCount( hwprops.nchans );
	ui.widget_ChannelSelector->selectAll();

	if( !reset_settings) {
		updatingUI = true;
	}

	//Gain selector
	ui.comboBox_Gain->clear();
	for(int i=0; i<hwprops.ngains; ++i) {
		tecella_get_gain_label(amp_handle, i, &label);
		ui.comboBox_Gain->addItem( QString::fromWCharArray( label ) );
	}

	//Extended gain selection for when telegraphs and external mode
	// control are enabled.  Allows both iclamp and vclamp gain to be
	// altered without haveing to change the mode in the UI.
//YT: Don't see the point in changing telegraph signal for a mode
//YT: that is not being used.  Users are confused with these.
//YT: Requires gain1_label and gain2_label to always exist/be initialized,
//YT: preventing further streamlining and refactoring of code.
/*	ui.label_Gain1->setText(
			QString::fromWCharArray( hwprops_ex_01.gain1_name ) + ":" );
	ui.comboBox_Gain1->clear();
	for(int i=0; i<hwprops_ex_01.ngains1; ++i) {
		tecella_get_gain1_label(amp_handle, i, &label);
		ui.comboBox_Gain1->addItem( QString::fromWCharArray( label ) );
	}

	ui.label_Gain2->setText(
			QString::fromWCharArray( hwprops_ex_01.gain2_name ) + ":" );
	ui.comboBox_Gain2->clear();
	for(int i=0; i<hwprops_ex_01.ngains2; ++i) {
		tecella_get_gain2_label(amp_handle, i, &label);
		ui.comboBox_Gain2->addItem( QString::fromWCharArray( label ) );
	}
*/

    //YT: Hide the extra gains.
    ui.widget_ExtendedGains->setShown(0);
    //ui.widget_ExtendedGains->setShown(
    //		hwprops_ex_01.ngains1>0 || hwprops_ex_01.ngains2>0 );

	//Source selection
	ui.comboBox_Source->clear();
	for(int i=0; i<hwprops.nsources; ++i) {
		tecella_get_source_label(amp_handle, i, &label);
		ui.comboBox_Source->addItem( QString::fromWCharArray( label ) );
	}
	ui.comboBox_Source->setCurrentIndex(1);

	//Stimulus selection for amplifiers with multiple stimuli.
	ui.comboBox_Stimulus->clear();
	for(int i=0; i<hwprops.nstimuli; ++i) {
		std::stringstream ss;
		ss << (i+1);
		ui.comboBox_Stimulus->addItem( QString::fromStdString( ss.str() ) );
	}

	if(hwprops.nstimuli > 1 && hwprops_ex_01.supports_chan_set_stimulus) {
		ui.comboBox_Stimulus->setShown(true);
		ui.label_Stimulus->setShown(true);
	}
	else  {
		ui.comboBox_Stimulus->setShown(false);
		ui.label_Stimulus->setShown(false);
	}

	//Iclamp/Vclamp enable
    //YT: Remove Enable IClamp checkbox
    //ui.checkBox_EnableIClamp->setShown( hwprops_ex_01.supports_iclamp_enable );
	ui.checkBox_EnableVcmd->setShown( hwprops_ex_01.supports_vcmd_enable );
    // Rs100  YT_2012_02_10: This used to say checkBox_EnableVcmd, so don't know how it worked before
    ui.checkBox_EnableRs100->setShown( hwprops_ex_01.supports_Rs100_enable );

    //YT: Use supports_iclamp_enable to display IclampON checkbox
    //YT: I think this is what BA was trying to do
    ui.checkBox_IclampOn->setShown( hwprops_ex_01.supports_iclamp_enable );

	tecella_chan_get_boost_enable_supported( amp_handle, &bvalue );
	ui.checkBox_EnableFreqBoost->setShown( bvalue );

	//Offset adjust mode
	ui.comboBox_OffsetAdjustMode->clear();
	ui.comboBox_OffsetAdjustMode->addItem( "Off" );
	ui.comboBox_OffsetAdjustMode->addItem( "Adjust Stimulus (JP)" );
	ui.comboBox_OffsetAdjustMode->addItem( "Adjust Response" );
	ui.comboBox_OffsetAdjustMode->addItem( "Adjust Stimulus & Response" );

	//Bessel low pass filter
	ui.CheckBox_Bessel->setChecked(hwprops.supports_bessel);
	ui.CheckBox_Bessel->setDisabled(true);
	ui.CheckBox_Bessel->setShown(hwprops.supports_bessel);
	ui.SpinBox_Bessel->setShown(hwprops.supports_bessel);
	ui.HSlider_Bessel->setShown(hwprops.supports_bessel);
	ui.label_Bessel->setShown(hwprops.supports_bessel);

	//High pass filter
	ui.CheckBox_HPF->setChecked(hwprops_ex_01.supports_hpf);
	ui.CheckBox_HPF->setDisabled(true);
	ui.CheckBox_HPF->setShown(hwprops_ex_01.supports_hpf);
	ui.SpinBox_HPF->setShown(hwprops_ex_01.supports_hpf);
	ui.HSlider_HPF->setShown(hwprops_ex_01.supports_hpf);
	ui.SpinBox_HPF->setRange(hwprops_ex_01.hpf_value_min, hwprops_ex_01.hpf_value_max);
	ui.SpinBox_HPF->setSingleStep(1.0);
	ui.HSlider_HPF->setRange(hwprops_ex_01.hpf_value_min, hwprops_ex_01.hpf_value_max);
	ui.label_HPF->setShown(hwprops_ex_01.supports_hpf);

	//Show/hide controls for supported features of the amplifier.
	tecella_get_reg_props(amp_handle, TECELLA_REG_LEAK, &reg_props);
	ui.checkBox_UseLeakA->setEnabled(reg_props.supported);
    //ui.checkBox_UseLeakA->setChecked(reg_props.supported);
	ui.CheckBox_Leak->setChecked(reg_props.supported);
	ui.CheckBox_Leak->setEnabled(reg_props.can_be_disabled);
	ui.CheckBox_Leak->setShown(reg_props.supported);
	ui.SpinBox_Leak->setShown(reg_props.supported);
	ui.HSlider_Leak->setShown(reg_props.supported);
	ui.label_Leak->setShown(reg_props.supported);

	tecella_get_reg_props(amp_handle, TECELLA_REG_LEAK_FINE, &reg_props);
	ui.SpinBox_LeakFine->setShown(reg_props.supported);
	ui.HSlider_LeakFine->setShown(reg_props.supported);
	ui.label_LeakFine->setShown(reg_props.supported);

	ui.CheckBox_LeakD->setChecked(true);

	tecella_get_reg_props(amp_handle, TECELLA_REG_RSERIES, &reg_props);
	ui.CheckBox_RSeries->setChecked(reg_props.supported);
	ui.CheckBox_RSeries->setShown(reg_props.supported);
	ui.SpinBox_RSeries->setShown(reg_props.supported);
	ui.HSlider_RSeries->setShown(reg_props.supported);
	ui.label_RSeries->setShown(reg_props.supported);

	tecella_get_reg_props(amp_handle, TECELLA_REG_JP, &reg_props);
	ui.CheckBox_JP->setChecked(reg_props.supported);
	ui.CheckBox_JP->setShown(reg_props.supported);
	ui.SpinBox_JP->setShown(reg_props.supported);
	ui.HSlider_JP->setShown(reg_props.supported);
	ui.label_JP->setShown(reg_props.supported);
	
	tecella_get_reg_props(amp_handle, TECELLA_REG_JP_FINE, &reg_props);
	ui.SpinBox_JPFine->setShown(reg_props.supported);
	ui.HSlider_JPFine->setShown(reg_props.supported);
	ui.label_JPFine->setShown(reg_props.supported);

	tecella_get_reg_props(amp_handle, TECELLA_REG_ICMD_OFFSET, &reg_props);
	ui.CheckBox_IcmdOffset->setChecked(reg_props.supported);
	ui.CheckBox_IcmdOffset->setShown(reg_props.supported);
	ui.SpinBox_IcmdOffset->setShown(reg_props.supported);
	ui.HSlider_IcmdOffset->setShown(reg_props.supported);
	ui.label_IcmdOffset->setShown(reg_props.supported);

	tecella_get_reg_props(amp_handle, TECELLA_REG_CFAST, &reg_props);
	ui.checkBox_UseCFast->setEnabled(reg_props.supported);
	ui.checkBox_UseCFast->setChecked(reg_props.supported);
	ui.CheckBox_CFast->setChecked(reg_props.supported);
	ui.CheckBox_CFast->setShown(reg_props.supported);
	ui.SpinBox_CFast->setShown(reg_props.supported);
	ui.HSlider_CFast->setShown(reg_props.supported);
	ui.label_CFast->setShown(reg_props.supported);

	tecella_get_reg_props(amp_handle, TECELLA_REG_CSLOW_A, &reg_props);
	ui.checkBox_UseCSlowA->setEnabled(reg_props.supported);
    //ui.checkBox_UseCSlowA->setChecked(reg_props.supported);
	ui.CheckBox_CSlowA->setChecked(reg_props.supported);
	ui.CheckBox_CSlowA->setShown(reg_props.supported);
	ui.SpinBox_CSlowA->setShown(reg_props.supported);
	ui.HSlider_CSlowA->setShown(reg_props.supported);
	ui.label_CSlowA->setShown(reg_props.supported);

	tecella_get_reg_props(amp_handle, TECELLA_REG_CSLOW_B, &reg_props);
	ui.checkBox_UseCSlowB->setEnabled(reg_props.supported);
    //ui.checkBox_UseCSlowB->setChecked(reg_props.supported);
    ui.CheckBox_CSlowB->setChecked(reg_props.supported);
	ui.CheckBox_CSlowB->setShown(reg_props.supported);
	ui.SpinBox_CSlowB->setShown(reg_props.supported);
	ui.HSlider_CSlowB->setShown(reg_props.supported);
	ui.label_CSlowB->setShown(reg_props.supported);

	tecella_get_reg_props(amp_handle, TECELLA_REG_CSLOW_C, &reg_props);
	ui.checkBox_UseCSlowC->setEnabled(reg_props.supported);
    //ui.checkBox_UseCSlowC->setChecked(reg_props.supported);
    ui.CheckBox_CSlowC->setChecked(reg_props.supported);
	ui.CheckBox_CSlowC->setShown(reg_props.supported);
	ui.SpinBox_CSlowC->setShown(reg_props.supported);
	ui.HSlider_CSlowC->setShown(reg_props.supported);
	ui.label_CSlowC->setShown(reg_props.supported);

	tecella_get_reg_props(amp_handle, TECELLA_REG_CSLOW_D, &reg_props);
	ui.checkBox_UseCSlowD->setEnabled(reg_props.supported);
    //ui.checkBox_UseCSlowD->setChecked(reg_props.supported);
	ui.CheckBox_CSlowD->setChecked(reg_props.supported);
	ui.CheckBox_CSlowD->setShown(reg_props.supported);
	ui.SpinBox_CSlowD->setShown(reg_props.supported);
	ui.HSlider_CSlowD->setShown(reg_props.supported);
	ui.label_CSlowD->setShown(reg_props.supported);

	tecella_get_reg_props(amp_handle, TECELLA_REG_BOOST_FREQUENCY, &reg_props);
	ui.SpinBox_BoostFrequency->setShown(reg_props.supported);
	ui.HSlider_BoostFrequency->setShown(reg_props.supported);
	ui.label_BoostFrequency->setShown(reg_props.supported);

	tecella_get_reg_props(amp_handle, TECELLA_REG_BOOST_GAIN, &reg_props);
	ui.SpinBox_BoostGain->setShown(reg_props.supported);
	ui.HSlider_BoostGain->setShown(reg_props.supported);
	ui.label_BoostGain->setShown(reg_props.supported);

	//Set the ranges and units of the controls
	tecella_get_reg_props(amp_handle, TECELLA_REG_LEAK, &reg_props);
	ui.SpinBox_Leak->setRange(reg_props.v_min,reg_props.v_max);
	ui.SpinBox_Leak->setSingleStep(reg_props.v_lsb);
	ui.SpinBox_Leak->setSuffix(QString::fromWCharArray(reg_props.units));
	ui.label_Leak->setText( QString::fromWCharArray(reg_props.label) );

	tecella_get_reg_props(amp_handle, TECELLA_REG_LEAK_FINE, &reg_props);
	ui.SpinBox_LeakFine->setRange(reg_props.v_min,reg_props.v_max);
	ui.SpinBox_LeakFine->setSingleStep(reg_props.v_lsb);
	ui.SpinBox_LeakFine->setSuffix(QString::fromWCharArray(reg_props.units));
	ui.label_LeakFine->setText( QString::fromWCharArray(reg_props.label) );

	ui.SpinBox_LeakD->setRange(digital_leak_min,digital_leak_max);
	ui.SpinBox_LeakD->setSingleStep(digital_leak_lsb);
	ui.SpinBox_LeakD->setDecimals(digital_leak_precision);
	ui.SpinBox_LeakD->setSuffix("MOhm-1");
	ui.HSlider_LeakD->setRange(0, digital_leak_steps);

	tecella_get_reg_props(amp_handle, TECELLA_REG_RSERIES, &reg_props);
	ui.SpinBox_RSeries->setRange(reg_props.v_min,reg_props.v_max);
	ui.SpinBox_RSeries->setSingleStep(reg_props.v_lsb);
	ui.SpinBox_RSeries->setSuffix(QString::fromWCharArray(reg_props.units));
	ui.label_RSeries->setText( QString::fromWCharArray(reg_props.label) );
	
	tecella_get_reg_props(amp_handle, TECELLA_REG_JP, &reg_props);
	ui.SpinBox_JP->setRange(reg_props.v_min,reg_props.v_max);
	ui.SpinBox_JP->setSingleStep(reg_props.v_lsb);
	ui.SpinBox_JP->setSuffix(QString::fromWCharArray(reg_props.units));
	ui.label_JP->setText( QString::fromWCharArray(reg_props.label) );
	
	tecella_get_reg_props(amp_handle, TECELLA_REG_JP_FINE, &reg_props);
	ui.SpinBox_JPFine->setRange(reg_props.v_min,reg_props.v_max);
	ui.SpinBox_JPFine->setSingleStep(reg_props.v_lsb);
	ui.SpinBox_JPFine->setSuffix(QString::fromWCharArray(reg_props.units));
	ui.label_JPFine->setText( QString::fromWCharArray(reg_props.label) );

	tecella_get_reg_props(amp_handle, TECELLA_REG_ICMD_OFFSET, &reg_props);
	ui.SpinBox_IcmdOffset->setRange(reg_props.v_min,reg_props.v_max);
	ui.SpinBox_IcmdOffset->setSingleStep(reg_props.v_lsb);
	ui.SpinBox_IcmdOffset->setSuffix(QString::fromWCharArray(reg_props.units));
	ui.label_IcmdOffset->setText( QString::fromWCharArray(reg_props.label) );
	
	ui.SpinBox_Bessel->setTecellaHandle(amp_handle);
	ui.SpinBox_Bessel->setSuffix("kHz");
	
	tecella_get_reg_props(amp_handle, TECELLA_REG_CFAST, &reg_props);
	ui.SpinBox_CFast->setRange(reg_props.v_min,reg_props.v_max);
	ui.SpinBox_CFast->setSingleStep(reg_props.v_lsb);
	ui.SpinBox_CFast->setSuffix(QString::fromWCharArray(reg_props.units));
	ui.label_CFast->setText( QString::fromWCharArray(reg_props.label) );
	
	tecella_get_reg_props(amp_handle, TECELLA_REG_CSLOW_A, &reg_props);
	ui.SpinBox_CSlowA->setRange(reg_props.v_min,reg_props.v_max);
	ui.SpinBox_CSlowA->setSingleStep(reg_props.v_lsb);
	ui.SpinBox_CSlowA->setSuffix(QString::fromWCharArray(reg_props.units));
	ui.label_CSlowA->setText( QString::fromWCharArray(reg_props.label) );
	
	tecella_get_reg_props(amp_handle, TECELLA_REG_CSLOW_B, &reg_props);
	ui.SpinBox_CSlowB->setRange(reg_props.v_min,reg_props.v_max);
	ui.SpinBox_CSlowB->setSingleStep(reg_props.v_lsb);
	ui.SpinBox_CSlowB->setSuffix(QString::fromWCharArray(reg_props.units));	
	ui.label_CSlowB->setText( QString::fromWCharArray(reg_props.label) );
	
	tecella_get_reg_props(amp_handle, TECELLA_REG_CSLOW_C, &reg_props);
	ui.SpinBox_CSlowC->setRange(reg_props.v_min,reg_props.v_max);
	ui.SpinBox_CSlowC->setSingleStep(reg_props.v_lsb);
	ui.SpinBox_CSlowC->setSuffix(QString::fromWCharArray(reg_props.units));
	ui.label_CSlowC->setText( QString::fromWCharArray(reg_props.label) );
	
	tecella_get_reg_props(amp_handle, TECELLA_REG_CSLOW_D, &reg_props);
	ui.SpinBox_CSlowD->setRange(reg_props.v_min,reg_props.v_max);
	ui.SpinBox_CSlowD->setSingleStep(reg_props.v_lsb);
	ui.SpinBox_CSlowD->setSuffix(QString::fromWCharArray(reg_props.units));
	ui.label_CSlowD->setText( QString::fromWCharArray(reg_props.label) );

	tecella_get_reg_props(amp_handle, TECELLA_REG_BOOST_FREQUENCY, &reg_props);
	ui.SpinBox_BoostFrequency->setRange(reg_props.v_min,reg_props.v_max);
	ui.SpinBox_BoostFrequency->setSingleStep(reg_props.v_lsb);
	ui.SpinBox_BoostFrequency->setSuffix(QString::fromWCharArray(reg_props.units));
	ui.label_BoostFrequency->setText( QString::fromWCharArray(reg_props.label) );

	tecella_get_reg_props(amp_handle, TECELLA_REG_BOOST_GAIN, &reg_props);
	ui.SpinBox_BoostGain->setRange(reg_props.v_min,reg_props.v_max);
	ui.SpinBox_BoostGain->setSingleStep(reg_props.v_lsb);
	ui.SpinBox_BoostGain->setSuffix(QString::fromWCharArray(reg_props.units));
	ui.label_BoostGain->setText( QString::fromWCharArray(reg_props.label) );

	ui.doubleSpinBox_AutoHoldingVoltage->setRange(
			hwprops.stimulus_value_min*1e3,hwprops.stimulus_value_max*1e3 );
	ui.doubleSpinBox_AutoHoldingVoltage->setSingleStep(
			hwprops.stimulus_value_lsb*1e3 );
	ui.doubleSpinBox_AutoHoldingVoltage->setDecimals(2);

	ui.doubleSpinBox_AutoPulseAmplitude->setRange(
			hwprops.stimulus_value_min*1e3,hwprops.stimulus_value_max*1e3 );
	ui.doubleSpinBox_AutoPulseAmplitude->setSingleStep(
			hwprops.stimulus_value_lsb*1e3 );
	ui.doubleSpinBox_AutoPulseAmplitude->setDecimals(2);

	//Default values
	ui.doubleSpinBox_AutoHoldingVoltage->setValue(0);
    ui.doubleSpinBox_AutoPulseAmplitude->setValue(-5);

	//Default register values
	if( reset_settings )
	{
		setJP(0.0);
		setJPFine(0.0);
		setIcmdOffset(0.0);
		ui.SpinBox_CSlowD->setValue(0);
		ui.SpinBox_Leak->setValue(0);
		ui.SpinBox_LeakD->setValue(0);

		int bessel_value;
		tecella_bessel_freq2value(amp_handle, 2.5, &bessel_value);
		ui.SpinBox_Bessel->setValue( bessel_value );
	}

	//Zap
	ui.frame_Zap->setShown( hwprops.supports_zap );
	ui.spinBox_Zap_amp->setMinimum( hwprops.zap_value_min*1000 );
	ui.spinBox_Zap_amp->setMaximum( hwprops.zap_value_max*1000 );
	ui.spinBox_Zap_amp->setSingleStep( hwprops.zap_value_lsb*1000 );

	//Load the current amplifier values into the UI
	updateUI();
}

void ChannelSettings::setAcquisitionInterrupter( AcquisitionInterrupter *acquisition_interrupter_new )
{
	acquisition_interrupter = acquisition_interrupter_new;
}

void ChannelSettings::acquisitionHint(bool acquiring)
{
	//This function/slot should be used to enable and disable UI elements
	// that should not be used during acquisition.

	//changing the selected stimulus on a channel during acquisition
	//is not supported because it would make it difficult for the
	// TecellaAmp API to track steps in the stimulus.
	ui.comboBox_Stimulus->setEnabled( !acquiring );

	//Auto compensation functions require acquisition control of
	// the amplifier.  If acquisition_interrupter is not valid, disable
	// the buttons.
	ui.PushButton_AutoArtifact->setEnabled( !acquiring || acquisition_interrupter );
	ui.PushButton_AutoCapacitance->setEnabled( !acquiring || acquisition_interrupter );
	ui.PushButton_AutoOffset->setEnabled( !acquiring || acquisition_interrupter );
	ui.PushButton_AutoLeak->setEnabled( !acquiring || acquisition_interrupter );
}

void ChannelSettings::updateUI( )
{
	updatingUI = true;

	double value;
	int ivalue;
	bool bvalue;

	int chan = selected_channels.front();

	tecella_reg_props reg_props;

	//Source
	tecella_chan_get_source(amp_handle,chan,&ivalue);
	ui.comboBox_Source->setCurrentIndex(ivalue);

	//Gain
	tecella_chan_get_gain(amp_handle,chan,&ivalue);
	ui.comboBox_Gain->setCurrentIndex(ivalue);

/*
	tecella_chan_get_gain1(amp_handle,chan,&ivalue);
	ui.comboBox_Gain1->setCurrentIndex(ivalue);

	tecella_chan_get_gain2(amp_handle,chan,&ivalue);
	ui.comboBox_Gain2->setCurrentIndex(ivalue);
*/

	//Stimulus
	tecella_chan_get_stimulus(amp_handle,chan,&ivalue);
	ui.comboBox_Stimulus->setCurrentIndex(ivalue);

    //IClamp Enable  YT: Remove EnableIClamp checkbox
    //tecella_chan_get_iclamp_enable( amp_handle, chan, &bvalue );
    //ui.checkBox_EnableIClamp->setChecked( bvalue );
	
	//Vcmd Enable
	tecella_chan_get_vcmd_enable( amp_handle, chan, &bvalue );
	ui.checkBox_EnableVcmd->setChecked( bvalue );

        // Rs100 Enable
        tecella_chan_get_Rs100_enable( amp_handle, chan, &bvalue );
        ui.checkBox_EnableRs100->setChecked( bvalue );

        // Iclamp On
        tecella_chan_get_IclampOn( amp_handle, chan, &bvalue );
        ui.checkBox_IclampOn->setChecked( bvalue );

        //Boost Enable
	tecella_chan_get_boost_enable( amp_handle, chan, &bvalue );
	ui.checkBox_EnableFreqBoost->setChecked( bvalue );

	//Bessel
	tecella_chan_get_bessel(amp_handle, chan, &ivalue);
	ui.SpinBox_Bessel->setValue(ivalue);

	//HPF
	tecella_chan_get_hpf(amp_handle, chan, &ivalue);
	ui.SpinBox_HPF->setValue(ivalue);

	//Analog leak
	tecella_get_reg_props(amp_handle, TECELLA_REG_LEAK, &reg_props);
	if( reg_props.supported ) {
		tecella_chan_get(amp_handle,TECELLA_REG_LEAK,chan,&value);
		setLeak(value);
	}
	if( reg_props.can_be_disabled ) {
		tecella_chan_get_enable(amp_handle,TECELLA_REG_LEAK,chan,&bvalue);
		ui.CheckBox_Leak->setChecked(bvalue);
	}

	//Analog leak fine
	tecella_get_reg_props(amp_handle, TECELLA_REG_LEAK_FINE, &reg_props);
	if( reg_props.supported ) {
		tecella_chan_get(amp_handle,TECELLA_REG_LEAK_FINE,chan,&value);
		setLeakFine(value);
	}
	if( reg_props.can_be_disabled ) {
		tecella_chan_get_enable(amp_handle,TECELLA_REG_LEAK_FINE,chan,&bvalue);
		//ui.CheckBox_LeakFine->setChecked(bvalue);
	}

	//Digital Leak
	tecella_chan_get_digital_leak(amp_handle, chan, &value);
	ui.SpinBox_LeakD->setValue(value * digital_leak_display_scale);
	tecella_chan_get_digital_leak_enable(amp_handle, chan, &bvalue);
	ui.CheckBox_LeakD->setChecked(bvalue);

	//Rseries
	tecella_get_reg_props(amp_handle, TECELLA_REG_RSERIES, &reg_props);
	if( reg_props.supported ) {
		tecella_chan_get(amp_handle,TECELLA_REG_RSERIES,chan,&value);
		setRSeries(value);
	}
	if( reg_props.can_be_disabled ) {
		tecella_chan_get_enable(amp_handle,TECELLA_REG_RSERIES,chan,&bvalue);
		ui.CheckBox_RSeries->setChecked(bvalue);
	}

	//JP
	tecella_get_reg_props(amp_handle, TECELLA_REG_JP, &reg_props);
	if( reg_props.supported ) {
		tecella_chan_get(amp_handle,TECELLA_REG_JP,chan,&value);
		setJP(value);
	}
	if( reg_props.can_be_disabled ) {
		tecella_chan_get_enable(amp_handle,TECELLA_REG_JP,chan,&bvalue);
		ui.CheckBox_JP->setChecked(bvalue);
	}

	//JP fine
	tecella_get_reg_props(amp_handle, TECELLA_REG_JP_FINE, &reg_props);
	if( reg_props.supported ) {
		tecella_chan_get(amp_handle,TECELLA_REG_JP_FINE,chan,&value);
		setJPFine(value);
	}
	if( reg_props.can_be_disabled ) {
		tecella_chan_get_enable(amp_handle,TECELLA_REG_JP_FINE,chan,&bvalue);
	}

	//Icmd Offset
	tecella_get_reg_props(amp_handle, TECELLA_REG_ICMD_OFFSET, &reg_props);
	if( reg_props.supported ) {
		tecella_chan_get(amp_handle,TECELLA_REG_ICMD_OFFSET,chan,&value);
		setIcmdOffset(value);
	}
	if( reg_props.can_be_disabled ) {
		tecella_chan_get_enable(amp_handle,TECELLA_REG_ICMD_OFFSET,chan,&bvalue);
		ui.CheckBox_IcmdOffset->setChecked(bvalue);
	}

	//Cfast
	tecella_get_reg_props(amp_handle, TECELLA_REG_CFAST, &reg_props);
	if( reg_props.supported ) {
		tecella_chan_get(amp_handle,TECELLA_REG_CFAST,chan,&value);
		setCFast(value);
	}
	if( reg_props.can_be_disabled ) {
		tecella_chan_get_enable(amp_handle,TECELLA_REG_CFAST,chan,&bvalue);
		ui.CheckBox_CFast->setChecked(bvalue);
	}

	//Cslows
	tecella_get_reg_props(amp_handle, TECELLA_REG_CSLOW_A, &reg_props);
	if( reg_props.supported ) {
		tecella_chan_get(amp_handle,TECELLA_REG_CSLOW_A,chan,&value);
		setCSlowA(value);
	}
	if( reg_props.can_be_disabled ) {
		tecella_chan_get_enable(amp_handle,TECELLA_REG_CSLOW_A,chan,&bvalue);
		ui.CheckBox_CSlowA->setChecked(bvalue);
	}

	tecella_get_reg_props(amp_handle, TECELLA_REG_CSLOW_B, &reg_props);
	if( reg_props.supported ) {
		tecella_chan_get(amp_handle,TECELLA_REG_CSLOW_B,chan,&value);
		setCSlowB(value);
	}
	if( reg_props.can_be_disabled ) {
		tecella_chan_get_enable(amp_handle,TECELLA_REG_CSLOW_B,chan,&bvalue);
		ui.CheckBox_CSlowB->setChecked(bvalue);
	}

	tecella_get_reg_props(amp_handle, TECELLA_REG_CSLOW_C, &reg_props);
	if( reg_props.supported ) {
		tecella_chan_get(amp_handle,TECELLA_REG_CSLOW_C,chan,&value);
		setCSlowC(value);
	}
	if( reg_props.can_be_disabled ) {
		tecella_chan_get_enable(amp_handle,TECELLA_REG_CSLOW_C,chan,&bvalue);
		ui.CheckBox_CSlowC->setChecked(bvalue);
	}

	tecella_get_reg_props(amp_handle, TECELLA_REG_CSLOW_D, &reg_props);
	if( reg_props.supported ) {
		tecella_chan_get(amp_handle,TECELLA_REG_CSLOW_D,chan,&value);
		setCSlowD(value);
	}
	if( reg_props.can_be_disabled ) {
		tecella_chan_get_enable(amp_handle,TECELLA_REG_CSLOW_D,chan,&bvalue);
		ui.CheckBox_CSlowD->setChecked(bvalue);
	}

	tecella_get_reg_props(amp_handle, TECELLA_REG_BOOST_FREQUENCY, &reg_props);
	if( reg_props.supported ) {
		tecella_chan_get(amp_handle,TECELLA_REG_BOOST_FREQUENCY,chan,&value);
		setBoostFrequency(value);
	}

	tecella_get_reg_props(amp_handle, TECELLA_REG_BOOST_GAIN, &reg_props);
	if( reg_props.supported ) {
		tecella_chan_get(amp_handle,TECELLA_REG_BOOST_GAIN,chan,&value);
		setBoostGain(value);
	}

	tecella_auto_artifact_get_capacitance(amp_handle,chan,&value);
	ui.label_ArtifactValue->setText( QString::number(value * 1e12) + "pF" );

	updatingUI = false;
}

void ChannelSettings::selectSource(int index)
{
	if(updatingUI) return;
	
	if( selected_channels.size() == hwprops.nchans )
	{
		tecella_chan_set_source(amp_handle, TECELLA_ALLCHAN, index);
	}
	else
	{
		for(unsigned int i=0; i<selected_channels.size(); i++)
			tecella_chan_set_source(amp_handle, selected_channels[i], index);
	}

	updateUI();
}

void ChannelSettings::selectGain(int index)
{
	if(updatingUI) return;

	if( selected_channels.size() == hwprops.nchans )
	{
		tecella_chan_set_gain(amp_handle, TECELLA_ALLCHAN, index);
	}
	else
	{
		for(unsigned int i=0; i<selected_channels.size(); i++)
			tecella_chan_set_gain(amp_handle, selected_channels[i], index);
	}

	updateUI();
}

void ChannelSettings::selectGain1(int index)
{
	if(updatingUI) return;

	if( selected_channels.size() == hwprops.nchans )
	{
		tecella_chan_set_gain1(amp_handle, TECELLA_ALLCHAN, index);
	}
	else
	{
		for(unsigned int i=0; i<selected_channels.size(); i++)
			tecella_chan_set_gain1(amp_handle, selected_channels[i], index);
	}

	updateUI();
}

void ChannelSettings::selectGain2(int index)
{
	if(updatingUI) return;

	if( selected_channels.size() == hwprops.nchans )
	{
		tecella_chan_set_gain2(amp_handle, TECELLA_ALLCHAN, index);
	}
	else
	{
		for(unsigned int i=0; i<selected_channels.size(); i++)
			tecella_chan_set_gain2(amp_handle, selected_channels[i], index);
	}

	updateUI();
}

void ChannelSettings::selectStimulus(int index)
{
	if(updatingUI) return;

	if( selected_channels.size() == hwprops.nchans )
	{
		tecella_chan_set_stimulus(amp_handle, TECELLA_ALLCHAN, index);
	}
	else
	{
		for(unsigned int i=0; i<selected_channels.size(); i++)
			tecella_chan_set_stimulus(amp_handle, selected_channels[i], index);
	}

	updateUI();
}

//returns true if caller should assume acquisition has been paused.
//acquisition may not be paused if acquisition_interrupter is not set.
bool ChannelSettings::pauseAcquisition()
{
	if( acquisition_interrupter ) {
		if( !acquisition_interrupter->pause_acquisition(true) ) {
			QMessageBox::information(this, "Warning", "Unable to interrupt acquisition to perform automatic compensation.", "Ok");
			return false;
		}
	}
	return true;
}

//returns true if caller should assume acquisition has been resumed.
//acquisition may not be resumed if acquisition_interrupter is not set.
bool ChannelSettings::resumeAcquisition()
{
	if( acquisition_interrupter ) {
		if( !acquisition_interrupter->resume_acquisition(true) ) {
			QMessageBox::information(this, "Warning", "Could not resume normal acquisition after automatic compensation.", "Ok");
			return false;
		}
	}
	return true;
}

void ChannelSettings::doAutoOffset()
{
	if( !pauseAcquisition() ) { return; }

	ui.PushButton_AutoOffset->setText("(Busy...)");
	ui.PushButton_AutoOffset->setEnabled(false);

	AutoOffsetThread auto_offset_thread;
	auto_offset_thread.setHandle( amp_handle );
	auto_offset_thread.start();
	display_qthread_progress_estimate(
		this, "Auto compensating JP...", &auto_offset_thread, 3.0);

	ui.PushButton_AutoOffset->setText("Offset (JP)");
	ui.PushButton_AutoOffset->setEnabled(true);

	resumeAcquisition();

	check_floating_point_exception();

	updateUI();
}

void ChannelSettings::doAutoLeak()
{
	if( !pauseAcquisition() ) { return; }

	ui.PushButton_AutoLeak->setText("(Busy...)");
	ui.PushButton_AutoLeak->setEnabled(false);

	AutoCompArgs args;
	args.t_hold = 20e-3;
	args.v_hold = ui.doubleSpinBox_AutoHoldingVoltage->value() * 1e-3;
	args.t_step = 20e-3;
	args.v_step = ui.doubleSpinBox_AutoPulseAmplitude->value() * 1e-3;
	args.use_leak = ui.checkBox_UseLeakA->isChecked();
	args.use_digital_leak = ui.checkBox_UseLeakD->isChecked();
	args.use_cfast = false;
	args.use_cslowA = false;
	args.use_cslowB = false;
	args.use_cslowC = false;
	args.use_cslowD = false;
	args.use_artifact = false;
	args.under_comp_coefficient = ui.doubleSpinBox_LeakUndercomp->value();
	args.acq_iterations = std::max( 1, ui.spinBox_AutoQuality->value()/2 );
	args.stimulus_index = 0;

	AutoCompThread auto_comp_thread;
	auto_comp_thread.setHandle( amp_handle );
	auto_comp_thread.setArgs( args );
	auto_comp_thread.start();
	display_qthread_progress_estimate(
			this, "Auto compensating leak...", &auto_comp_thread, .2 * args.acq_iterations );

	ui.PushButton_AutoLeak->setText("Leak");
	ui.PushButton_AutoLeak->setEnabled(true);

	resumeAcquisition();

	check_floating_point_exception();

	updateUI();
}

void ChannelSettings::doAutoCapacitance()
{
	if( !pauseAcquisition() ) { return; }

	ui.PushButton_AutoCapacitance->setText("(Busy...)");
	ui.PushButton_AutoCapacitance->setEnabled(false);

	AutoCompArgs args;
	args.t_hold = 20e-3;
	args.v_hold = ui.doubleSpinBox_AutoHoldingVoltage->value() * 1e-3;
	args.t_step = 20e-3;
	args.v_step = ui.doubleSpinBox_AutoPulseAmplitude->value() * 1e-3;
	args.use_leak = false;
	args.use_digital_leak = false;
	args.use_cfast = ui.checkBox_UseCFast->isChecked();
	args.use_cslowA = ui.checkBox_UseCSlowA->isChecked();
	args.use_cslowB = ui.checkBox_UseCSlowB->isChecked();
	args.use_cslowC = ui.checkBox_UseCSlowC->isChecked();
	args.use_cslowD = ui.checkBox_UseCSlowD->isChecked();
	args.use_artifact = false;
	args.under_comp_coefficient = ui.doubleSpinBox_CapUndercomp->value();
	args.acq_iterations = ui.spinBox_AutoQuality->value();
	args.stimulus_index = 0;

	AutoCompThread auto_comp_thread;
	auto_comp_thread.setHandle( amp_handle );
	auto_comp_thread.setArgs( args );
	auto_comp_thread.start();
	display_qthread_progress_estimate(
			this, "Auto compensating capacitance...", &auto_comp_thread, 1 + .3 * args.acq_iterations );

	ui.PushButton_AutoCapacitance->setText("Capacitance");
	ui.PushButton_AutoCapacitance->setEnabled(true);

	resumeAcquisition();

	updateUI();
}

void ChannelSettings::doAutoArtifact(bool enable)
{
	if(enable)
	{
		if( !pauseAcquisition() ) { return; }

		ui.PushButton_AutoArtifact->setText("(Busy...)");
		ui.PushButton_AutoArtifact->setEnabled(false);

		double t_hold = 29e-3;
		double v_hold = ui.doubleSpinBox_AutoHoldingVoltage->value() * 1e-3;
		double t_step = 29e-3;
		double v_step = ui.doubleSpinBox_AutoPulseAmplitude->value() * 1e-3;
		int quality = ui.spinBox_AutoQuality->value();

		AutoArtifactThread auto_artifact_thread(
				amp_handle, v_hold, t_hold, v_step, t_step, quality, 0 );
		auto_artifact_thread.start();

		display_qthread_progress_estimate(this, "Compensating artifacts...", &auto_artifact_thread, 1 + .7 * quality);
		tecella_auto_artifact_enable(amp_handle, true);

		ui.PushButton_AutoArtifact->setText("Artifact");
		ui.PushButton_AutoArtifact->setEnabled(true);

		resumeAcquisition();

		updateUI();
	}
	else
	{
		tecella_auto_artifact_enable(amp_handle, false);

		updateUI();
	}
}

void ChannelSettings::doAutoCapAndArt()
{
	doAutoCapacitance();

	if( ui.PushButton_AutoArtifact->isChecked() ) {
		doAutoArtifact(true);
	}
	else {
		ui.PushButton_AutoArtifact->setChecked(true);
	}
}

void ChannelSettings::selectOffsetAdjustMode(int index)
{
	if(updatingUI) return;

	TECELLA_OFFSET_ADJUST_MODE mode = index==1 ? TECELLA_OAM_ADJUST_STIMULUS :
					  index==2 ? TECELLA_OAM_ADJUST_RESPONSE :
					  index==3 ? TECELLA_OAM_ADJUST_STIMULUS_AND_RESPONSE :
					  TECELLA_OAM_OFF;

	//always apply to all channels.
	tecella_auto_comp_offset_adjust_mode_set(
			amp_handle, TECELLA_ALLCHAN, mode);
}

void ChannelSettings::enableReg(TECELLA_REGISTER reg, bool enabled)
{
	if(updatingUI) return;

	if( selected_channels.size() == hwprops.nchans )
	{
		tecella_chan_set_enable(
				amp_handle, reg, TECELLA_ALLCHAN, (int)enabled);
	}
	else
	{
		for(unsigned int i=0; i<selected_channels.size(); i++) {
			tecella_chan_set_enable(
					amp_handle, reg, selected_channels[i], (int)enabled);
		}
	}

	updateUI();
}

void ChannelSettings::enableLeakD(bool enabled)
{
	if(updatingUI) return;

	if( selected_channels.size() == hwprops.nchans )
	{
		tecella_chan_set_digital_leak_enable(
				amp_handle, TECELLA_ALLCHAN, enabled);
	}
	else
	{
		for(unsigned int i=0; i<selected_channels.size(); i++) {
			tecella_chan_set_digital_leak_enable(
					amp_handle, selected_channels[i], enabled);
		}
	}

	updateUI();
}
void ChannelSettings::enableLeak(bool enabled) {
	enableReg(TECELLA_REG_LEAK, enabled);
}
void ChannelSettings::enableRSeries(bool enabled) {
	enableReg(TECELLA_REG_RSERIES, enabled);
}
void ChannelSettings::enableJP(bool enabled) {
	enableReg(TECELLA_REG_JP, enabled);
}
void ChannelSettings::enableJPFine(bool enabled) {
	enableReg(TECELLA_REG_JP_FINE, enabled);
}


void ChannelSettings::enableBessel(bool enabled) {
	//always enabled.
}
void ChannelSettings::enableCFast(bool enabled) {
	//always enabled.
}

void ChannelSettings::enableCSlowA(bool enabled) {
	enableReg(TECELLA_REG_CSLOW_A, enabled);
}
void ChannelSettings::enableCSlowB(bool enabled) {
	enableReg(TECELLA_REG_CSLOW_B, enabled);
}
void ChannelSettings::enableCSlowC(bool enabled) {
	enableReg(TECELLA_REG_CSLOW_C, enabled);
}
void ChannelSettings::enableCSlowD(bool enabled) {
	enableReg(TECELLA_REG_CSLOW_D, enabled);
}

void ChannelSettings::enableArtifact(bool enabled)
{
	if(updatingUI) return;

	if( selected_channels.size() == hwprops.nchans )
	{
		tecella_auto_artifact_enable(
				amp_handle, enabled, 0);
	}
	else
	{
		for(unsigned int i=0; i<selected_channels.size(); i++) {
			tecella_auto_artifact_enable(
					amp_handle, enabled, 0);
		}
	}

	updateUI();
}

void ChannelSettings::setLeak(int value) {
	TECELLA_REG_PROPS reg_props;
	tecella_get_reg_props(amp_handle, TECELLA_REG_LEAK, &reg_props);
	double dvalue = reg_props.v_min + value*reg_props.v_lsb;
	ui.SpinBox_Leak->setValue(dvalue);
}
void ChannelSettings::setLeakFine(int value) {
	TECELLA_REG_PROPS reg_props;
	tecella_get_reg_props(amp_handle, TECELLA_REG_LEAK_FINE, &reg_props);
	double dvalue = reg_props.v_min + value*reg_props.v_lsb;
	ui.SpinBox_LeakFine->setValue(dvalue);
}
void ChannelSettings::setLeakD(int value) {
	double dvalue = digital_leak_min + value*digital_leak_lsb;
	ui.SpinBox_LeakD->setValue(dvalue);
}
void ChannelSettings::setRSeries(int value) {
	TECELLA_REG_PROPS reg_props;
	tecella_get_reg_props(amp_handle, TECELLA_REG_RSERIES, &reg_props);
	double dvalue = reg_props.v_min + value*reg_props.v_lsb;
	ui.SpinBox_RSeries->setValue(dvalue);
}
void ChannelSettings::setJP(int value) {
	TECELLA_REG_PROPS reg_props;
	tecella_get_reg_props(amp_handle, TECELLA_REG_JP, &reg_props);
	double dvalue = reg_props.v_min + value*reg_props.v_lsb;
	ui.SpinBox_JP->setValue(dvalue);
}
void ChannelSettings::setJPFine(int value) {
	TECELLA_REG_PROPS reg_props;
	tecella_get_reg_props(amp_handle, TECELLA_REG_JP_FINE, &reg_props);
	double dvalue = reg_props.v_min + value*reg_props.v_lsb;
	ui.SpinBox_JPFine->setValue(dvalue);
}
void ChannelSettings::setIcmdOffset(int value) {
	TECELLA_REG_PROPS reg_props;
	tecella_get_reg_props(amp_handle, TECELLA_REG_ICMD_OFFSET, &reg_props);
	double dvalue = reg_props.v_min + value*reg_props.v_lsb;
	ui.SpinBox_IcmdOffset->setValue(dvalue);
}
void ChannelSettings::setBessel(int value)
{
	if(updatingUI) return;
	if(ui.SpinBox_Bessel->value() != value)	{
		ui.SpinBox_Bessel->setValue(value);
		if( selected_channels.size() == hwprops.nchans ) {
			tecella_chan_set_bessel(amp_handle, TECELLA_ALLCHAN, value);
		}
		else {
			for(unsigned int i=0; i<selected_channels.size(); i++) {
				tecella_chan_set_bessel(amp_handle, selected_channels[i], value);
			}
		}
	}	
	else if( ui.HSlider_Bessel->value() != value) {
		ui.HSlider_Bessel->setValue(value);
		if( selected_channels.size() == hwprops.nchans ) {
			tecella_chan_set_bessel(amp_handle, TECELLA_ALLCHAN, value);
		}
		else {
			for(unsigned int i=0; i<selected_channels.size(); i++) {
				tecella_chan_set_bessel(amp_handle, selected_channels[i], value);
			}
		}
	}
	updateUI();
}
void ChannelSettings::setCFast(int value) {
	TECELLA_REG_PROPS reg_props;
	tecella_get_reg_props(amp_handle, TECELLA_REG_CFAST, &reg_props);
	double dvalue = reg_props.v_min + value*reg_props.v_lsb;
	ui.SpinBox_CFast->setValue(dvalue);
}
void ChannelSettings::setCSlowA(int value) {
	TECELLA_REG_PROPS reg_props;
	tecella_get_reg_props(amp_handle, TECELLA_REG_CSLOW_A, &reg_props);
	double dvalue = reg_props.v_min + value*reg_props.v_lsb;
	ui.SpinBox_CSlowA->setValue(dvalue);
}
void ChannelSettings::setCSlowB(int value) {
	TECELLA_REG_PROPS reg_props;
	tecella_get_reg_props(amp_handle, TECELLA_REG_CSLOW_B, &reg_props);
	double dvalue = reg_props.v_min + value*reg_props.v_lsb;
	ui.SpinBox_CSlowB->setValue(dvalue);
}
void ChannelSettings::setCSlowC(int value) {
	TECELLA_REG_PROPS reg_props;
	tecella_get_reg_props(amp_handle, TECELLA_REG_CSLOW_C, &reg_props);
	double dvalue = reg_props.v_min + value*reg_props.v_lsb;
	ui.SpinBox_CSlowC->setValue(dvalue);
}
void ChannelSettings::setCSlowD(int value) {
	TECELLA_REG_PROPS reg_props;
	tecella_get_reg_props(amp_handle, TECELLA_REG_CSLOW_D, &reg_props);
	double dvalue = reg_props.v_min + value*reg_props.v_lsb;
	ui.SpinBox_CSlowD->setValue(dvalue);
}
void ChannelSettings::setBoostFrequency(int value) {
	TECELLA_REG_PROPS reg_props;
	tecella_get_reg_props(amp_handle, TECELLA_REG_BOOST_FREQUENCY, &reg_props);
	double dvalue = reg_props.v_min + value*reg_props.v_lsb;
	ui.SpinBox_BoostFrequency->setValue(dvalue);
}
void ChannelSettings::setBoostGain(int value) {
	TECELLA_REG_PROPS reg_props;
	tecella_get_reg_props(amp_handle, TECELLA_REG_BOOST_GAIN, &reg_props);
	double dvalue = reg_props.v_min + value*reg_props.v_lsb;
	ui.SpinBox_BoostGain->setValue(dvalue);
}
void ChannelSettings::setHPF(int value) {
	ui.SpinBox_HPF->setValue(value);
}


void ChannelSettings::setReg(TECELLA_REGISTER reg, double value)
{
	if(updatingUI) return;
	if( selected_channels.size() == hwprops.nchans ) {
		tecella_chan_set(amp_handle, reg, TECELLA_ALLCHAN, value);
	}
	else {
		for(unsigned int i=0; i<selected_channels.size(); i++) {
			tecella_chan_set(amp_handle, reg, selected_channels[i], value);
		}
	}

	updateUI();
}

void ChannelSettings::setLeakD(double value)
{
	if(updatingUI) return;

	int ivalue = (int)((value - digital_leak_min) / digital_leak_lsb + .5);

	if( selected_channels.size() == hwprops.nchans )
	{
		tecella_chan_set_digital_leak(
				amp_handle, TECELLA_ALLCHAN, value/digital_leak_display_scale);
	}
	else
	{
		for(unsigned int i=0; i<selected_channels.size(); i++) {
			tecella_chan_set_digital_leak(
					amp_handle, selected_channels[i], value/digital_leak_display_scale);
		}
	}

	ui.HSlider_LeakD->setValue(ivalue);

	updateUI();
}

void ChannelSettings::setLeak(double value) {
	TECELLA_REG_PROPS reg_props;
	tecella_get_reg_props(amp_handle, TECELLA_REG_LEAK, &reg_props);
	int ivalue = (int)((value - reg_props.v_min) / reg_props.v_lsb + .5);
	ui.HSlider_Leak->setValue(ivalue);
	setReg(TECELLA_REG_LEAK, value);
}
void ChannelSettings::setLeakFine(double value) {
	TECELLA_REG_PROPS reg_props;
	tecella_get_reg_props(amp_handle, TECELLA_REG_LEAK_FINE, &reg_props);
	int ivalue = (int)((value - reg_props.v_min) / reg_props.v_lsb + .5);
	ui.HSlider_LeakFine->setValue(ivalue);
	setReg(TECELLA_REG_LEAK_FINE, value);
}

void ChannelSettings::setRSeries(double value) {
	TECELLA_REG_PROPS reg_props;
	tecella_get_reg_props(amp_handle, TECELLA_REG_RSERIES, &reg_props);
	int ivalue = (int)((value - reg_props.v_min) / reg_props.v_lsb + .5);
	ui.HSlider_RSeries->setValue(ivalue);
	setReg(TECELLA_REG_RSERIES, value);
}

void ChannelSettings::setJP(double value) {
	TECELLA_REG_PROPS reg_props;
	tecella_get_reg_props(amp_handle, TECELLA_REG_JP, &reg_props);
	int ivalue = (int)((value - reg_props.v_min) / reg_props.v_lsb + .5);
	ui.HSlider_JP->setValue(ivalue);
	setReg(TECELLA_REG_JP, value);
}

void ChannelSettings::setJPFine(double value) {
	TECELLA_REG_PROPS reg_props;
	tecella_get_reg_props(amp_handle, TECELLA_REG_JP_FINE, &reg_props);
	int ivalue = (int)((value - reg_props.v_min) / reg_props.v_lsb + .5);
	ui.HSlider_JPFine->setValue(ivalue);
	setReg(TECELLA_REG_JP_FINE, value);
}

void ChannelSettings::setIcmdOffset(double value) {
	TECELLA_REG_PROPS reg_props;
	tecella_get_reg_props(amp_handle, TECELLA_REG_ICMD_OFFSET, &reg_props);
	int ivalue = (int)((value - reg_props.v_min) / reg_props.v_lsb + .5);
	ui.HSlider_IcmdOffset->setValue(ivalue);
	setReg(TECELLA_REG_ICMD_OFFSET, value);
}

void ChannelSettings::setCFast(double value) {
	TECELLA_REG_PROPS reg_props;
	tecella_get_reg_props(amp_handle, TECELLA_REG_CFAST, &reg_props);
	int ivalue = (int)((value - reg_props.v_min) / reg_props.v_lsb + .5);
	ui.HSlider_CFast->setValue(ivalue);
	setReg(TECELLA_REG_CFAST, value);
}
void ChannelSettings::setCSlowA(double value) {
	TECELLA_REG_PROPS reg_props;
	tecella_get_reg_props(amp_handle, TECELLA_REG_CSLOW_A, &reg_props);
	int ivalue = (int)((value - reg_props.v_min) / reg_props.v_lsb + .5);
	ui.HSlider_CSlowA->setValue(ivalue);
	setReg(TECELLA_REG_CSLOW_A, value);
}
void ChannelSettings::setCSlowB(double value) {
	TECELLA_REG_PROPS reg_props;
	tecella_get_reg_props(amp_handle, TECELLA_REG_CSLOW_B, &reg_props);
	int ivalue = (int)((value - reg_props.v_min) / reg_props.v_lsb + .5);
	ui.HSlider_CSlowB->setValue(ivalue);
	setReg(TECELLA_REG_CSLOW_B, value);
}
void ChannelSettings::setCSlowC(double value) {
	TECELLA_REG_PROPS reg_props;
	tecella_get_reg_props(amp_handle, TECELLA_REG_CSLOW_C, &reg_props);
	int ivalue = (int)((value - reg_props.v_min) / reg_props.v_lsb + .5);
	ui.HSlider_CSlowC->setValue(ivalue);
	setReg(TECELLA_REG_CSLOW_C, value);
}
void ChannelSettings::setCSlowD(double value) {
	TECELLA_REG_PROPS reg_props;
	tecella_get_reg_props(amp_handle, TECELLA_REG_CSLOW_D, &reg_props);
	int ivalue = (int)((value - reg_props.v_min) / reg_props.v_lsb + .5);
	ui.HSlider_CSlowD->setValue(ivalue);
	setReg(TECELLA_REG_CSLOW_D, value);
}
void ChannelSettings::setBoostFrequency(double value) {
	TECELLA_REG_PROPS reg_props;
	tecella_get_reg_props(amp_handle, TECELLA_REG_BOOST_FREQUENCY, &reg_props);
	int ivalue = (int)((value - reg_props.v_min) / reg_props.v_lsb + .5);
	ui.HSlider_BoostFrequency->setValue(ivalue);
	setReg(TECELLA_REG_BOOST_FREQUENCY, value);
}
void ChannelSettings::setBoostGain(double value) {
	TECELLA_REG_PROPS reg_props;
	tecella_get_reg_props(amp_handle, TECELLA_REG_BOOST_GAIN, &reg_props);
	int ivalue = (int)((value - reg_props.v_min) / reg_props.v_lsb + .5);
	ui.HSlider_BoostGain->setValue(ivalue);
	setReg(TECELLA_REG_BOOST_GAIN, value);
}
void ChannelSettings::setHPF(double value)
{
	if(updatingUI) return;
	if( selected_channels.size() == hwprops.nchans ) {
		tecella_chan_set_hpf(amp_handle, TECELLA_ALLCHAN, (int)value);
	}
	else {
		for(unsigned int i=0; i<selected_channels.size(); i++) {
			tecella_chan_set_hpf(amp_handle, selected_channels[i], (int)value);
		}
	}
	ui.HSlider_HPF->setValue((int)value);

	updateUI();
}

void ChannelSettings::selectedChannelsChanged(std::vector<int> &selected_channels_new)
{
	selected_channels = selected_channels_new;
	updateUI();
}

void ChannelSettings::zapDurationChanged(int duration)
{
	ui.spinBox_Zap_dur->setValue( duration );
	updateUI();
}

void ChannelSettings::useZapPulse(bool enable)
{
	ui.spinBox_Zap_dur->setEnabled(enable);
	updateUI();
}

void ChannelSettings::doZap(bool enable)
{
	if( enable )
	{
		double duration = 0;
		if( ui.checkBox_ZapPulse->isChecked() ) {
			duration = ui.spinBox_Zap_dur->value()/1000.0;
		}
		double value = ui.spinBox_Zap_amp->value()/1000.0;

		tecella_stimulus_zap(amp_handle, duration, value, &selected_channels[0], selected_channels.size());

		if( ui.checkBox_ZapPulse->isChecked() ) {
			ui.pushButton_Zap->setChecked(false);
		}
	}
	if( !enable && !ui.checkBox_ZapPulse->isChecked() )
	{
		tecella_stimulus_zap(amp_handle, 0, 0, &selected_channels[0], selected_channels.size());
	}

	updateUI();
}

void ChannelSettings::on_pushButton_ZeroJP_clicked()
{
	for(int i=0; i<selected_channels.size(); i++)
	{
		int c = selected_channels[i];
		tecella_chan_set(amp_handle, TECELLA_REG_JP, c, 0.0);
		tecella_chan_set(amp_handle, TECELLA_REG_JP_FINE, c, 0.0);
	}
	updateUI();
}

void ChannelSettings::on_pushButton_ZeroLeak_clicked()
{
	for(int i=0; i<selected_channels.size(); i++)
	{
		int c = selected_channels[i];
		tecella_chan_set(amp_handle, TECELLA_REG_LEAK, c, 0.0);
		tecella_chan_set(amp_handle, TECELLA_REG_LEAK_FINE, c, 0.0);
		tecella_chan_set_digital_leak(amp_handle, c, 0.0);
	}
	updateUI();
}

void ChannelSettings::on_pushButton_ZeroCapacitance_clicked()
{
	for(int i=0; i<selected_channels.size(); i++)
	{
		int c = selected_channels[i];
		tecella_chan_set(amp_handle, TECELLA_REG_CFAST, c, 0.0);
		tecella_chan_set(amp_handle, TECELLA_REG_CSLOW_A, c, 0.0);
		tecella_chan_set(amp_handle, TECELLA_REG_CSLOW_B, c, 0.0);
		tecella_chan_set(amp_handle, TECELLA_REG_CSLOW_C, c, 0.0);
		tecella_chan_set(amp_handle, TECELLA_REG_CSLOW_D, c, 0.0);
	}
	updateUI();
}

void ChannelSettings::on_pushButton_ZeroArtifact_clicked()
{
	doAutoArtifact(false);
}

void ChannelSettings::on_PushButton_AutoArtifact_clicked()
{
	doAutoArtifact(true);
}

void ChannelSettings::on_groupBox_General_toggled(bool toggled)
{
	ui.widget_SourceGain->setShown(toggled);
}

void ChannelSettings::on_groupBox_CompensationAuto_toggled(bool toggled)
{
	ui.widget_AutoCompensation->setShown( toggled );
}

void ChannelSettings::on_groupBox_CompensationDetail_toggled(bool toggled)
{
	ui.widget_CompensationDetail->setShown( toggled );
}

//void ChannelSettings::on_checkBox_EnableIClamp_toggled(bool checked)
//{
//	if(updatingUI) return;
//
//	for(int i=0; i<selected_channels.size(); i++)
//	{
//		int c = selected_channels[i];
//		tecella_chan_set_iclamp_enable( amp_handle, c, checked );
//	}
//
//	updateUI();
//}

void ChannelSettings::on_checkBox_EnableVcmd_toggled(bool checked)
{
        if(updatingUI) return;

        for(int i=0; i<selected_channels.size(); i++)
        {
                int c = selected_channels[i];
                tecella_chan_set_vcmd_enable( amp_handle, c, checked );
        }

        updateUI();
}

// Rs100
void ChannelSettings::on_checkBox_EnableRs100_toggled(bool checked)
{
        if(updatingUI) return;

        for(int i=0; i<selected_channels.size(); i++)
        {
                int c = selected_channels[i];
                tecella_chan_set_Rs100_enable( amp_handle, c, checked );
        }

        updateUI();
}

// IclampOn
void ChannelSettings::on_checkBox_IclampOn_toggled(bool checked)
{
        if(updatingUI) return;

        for(int i=0; i<selected_channels.size(); i++)
        {
                int c = selected_channels[i];
                tecella_chan_set_IclampOn( amp_handle, c, checked );
        }

        updateUI();
}

void ChannelSettings::on_checkBox_EnableFreqBoost_toggled(bool checked)
{
	if(updatingUI) return;

	for(int i=0; i<selected_channels.size(); i++)
	{
		int c = selected_channels[i];
		tecella_chan_set_boost_enable( amp_handle, c, checked );
	}

	updateUI();
}
