/*
    ------------------------------------------------------------------

    This file is part of the Open Ephys GUI
    Copyright (C) 2014 Open Ephys

    ------------------------------------------------------------------

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "RHD2000Editor.h"
#include <cmath>

#include "../../Editors/ChannelSelector.h"
#include "../../SourceNode/SourceNode.h"
#include "RHD2000Thread.h"

#ifdef WIN32
#if (_MSC_VER < 1800) //round doesn't exist on MSVC prior to 2013 version
inline double round(double x)
{
    return floor(x+0.5);
}
#endif
#endif

FPGAchannelList::FPGAchannelList(GenericProcessor* proc_, Viewport* p, FPGAcanvas* c) : chainUpdate(false), viewport(p), canvas(c)
{
    proc = (SourceNode*)proc_;
    channelComponents.clear();

    numberingSchemeLabel = new Label("Numbering scheme:","Numbering scheme:");
    numberingSchemeLabel->setEditable(false);
    numberingSchemeLabel->setBounds(10,10,150, 25);
    numberingSchemeLabel->setColour(Label::textColourId,juce::Colours::white);
    addAndMakeVisible(numberingSchemeLabel);

    numberingScheme = new ComboBox("numberingScheme");
    numberingScheme->addItem("Continuous",1);
    numberingScheme->addItem("Per Stream",2);
    numberingScheme->setBounds(160,10,100,25);
    numberingScheme->addListener(this);
    numberingScheme->setSelectedId(1, dontSendNotification);
    addAndMakeVisible(numberingScheme);

    impedanceButton = new UtilityButton("Measure Impedance", Font("Default", 13, Font::plain));
    impedanceButton->setRadius(3);
    impedanceButton->setBounds(280,10,140,25);
    impedanceButton->addListener(this);
    addAndMakeVisible(impedanceButton);

    RHD2000Editor* e = static_cast<RHD2000Editor*>(proc->getEditor());
    saveImpedanceButton = new ToggleButton("Save impedance measurements");
    saveImpedanceButton->setBounds(430,10,110,25);
    saveImpedanceButton->setToggleState(e->getSaveImpedance(),dontSendNotification);
    saveImpedanceButton->addListener(this);
    addAndMakeVisible(saveImpedanceButton);

    autoMeasureButton = new ToggleButton("Measure impedance at acquisition start");
    autoMeasureButton->setBounds(550,10,150,25);
    autoMeasureButton->setToggleState(e->getAutoMeasureImpedance(),dontSendNotification);
    autoMeasureButton->addListener(this);
    addAndMakeVisible(autoMeasureButton);

    gains.clear();
    gains.add(0.01);
    gains.add(0.1);
    gains.add(1);
    gains.add(2);
    gains.add(5);
    gains.add(10);
    gains.add(20);
    gains.add(50);
    gains.add(100);
    gains.add(500);
    gains.add(1000);


    update();
}

FPGAchannelList::~FPGAchannelList()
{

}

void FPGAchannelList::paint(Graphics& g)
{
}

void FPGAchannelList::buttonClicked(Button* btn)
{
    RHD2000Editor* p = (RHD2000Editor*)proc->getEditor();
    if (btn == impedanceButton)
    {
        p->measureImpedance();
    }
    else if (btn == saveImpedanceButton)
    {
        p->setSaveImpedance(btn->getToggleState());
    }
    else if (btn == autoMeasureButton)
    {
        p->setAutoMeasureImpedance(btn->getToggleState());
    }
}

void FPGAchannelList::update()
{
   // const int columnWidth = 330;
	const int columnWidth = 250;
    // Query processor for number of channels, types, gains, etc... and update the UI
    channelComponents.clear();
    staticLabels.clear();

    RHD2000Thread* thread = (RHD2000Thread*)proc->getThread();
    DataChannel::DataChannelTypes type;

    // find out which streams are active.
    bool hsActive[MAX_NUM_HEADSTAGES+1];
    //bool adcActive = false;
    int numActiveHeadstages = 0;
    int hsColumn[MAX_NUM_HEADSTAGES + 1];
    int numChannelsPerHeadstage[MAX_NUM_HEADSTAGES + 1];
    chainUpdate = false;

    for (int k = 0; k<MAX_NUM_HEADSTAGES; k++)
    {
        if (thread->isHeadstageEnabled(k))
        {
            numChannelsPerHeadstage[k] = thread->getActiveChannelsInHeadstage(k);
            hsActive[k] = true;
            hsColumn[k] = numActiveHeadstages*columnWidth;
            numActiveHeadstages++;
        }
        else
        {
            numChannelsPerHeadstage[k] = 0;
            hsActive[k] = false;
            hsColumn[k] = 0;
        }
    }

    if (thread->getNumDataOutputs(DataChannel::ADC_CHANNEL,0) > 0)
    {
		numChannelsPerHeadstage[MAX_NUM_HEADSTAGES] = thread->getNumDataOutputs(DataChannel::ADC_CHANNEL, 0);
        hsActive[MAX_NUM_HEADSTAGES] = true;
        hsColumn[MAX_NUM_HEADSTAGES] = numActiveHeadstages*columnWidth;
        numActiveHeadstages++;
    }
    else
    {
        numChannelsPerHeadstage[MAX_NUM_HEADSTAGES] = 0;
        hsActive[MAX_NUM_HEADSTAGES] = false;
        hsColumn[MAX_NUM_HEADSTAGES] = 0;
    }

    StringArray streamNames;
    streamNames.add("Port A1");
    streamNames.add("Port A2");
    streamNames.add("Port B1");
    streamNames.add("Port B2");
    streamNames.add("Port C1");
    streamNames.add("Port C2");
    streamNames.add("Port D1");
    streamNames.add("Port D2");
    streamNames.add("ADC");

    for (int k = 0; k < MAX_NUM_HEADSTAGES + 1; k++)
    {
        if (hsActive[k])
        {
            Label* lbl = new Label(streamNames[k],streamNames[k]);
            lbl->setEditable(false);
            lbl->setBounds(10+hsColumn[k],40,columnWidth, 25);
            lbl->setJustificationType(juce::Justification::centred);
            lbl->setColour(Label::textColourId,juce::Colours::white);
            staticLabels.add(lbl);
            addAndMakeVisible(lbl);

        }

    }

    for (int k = 0; k < MAX_NUM_HEADSTAGES + 1; k++)
    {
        if (hsActive[k])
        {
            for (int ch = 0; ch < numChannelsPerHeadstage[k]+ (k < MAX_NUM_HEADSTAGES ? 3 : 0); ch++)
            {
                int channelGainIndex = 1;
                int realChan = thread->getChannelFromHeadstage(k, ch);
                float ch_gain = proc->getDataChannel(realChan)->getBitVolts() / proc->getBitVolts(proc->getDataChannel(realChan));
                for (int j = 0; j < gains.size(); j++)
                {
                    if (fabs(gains[j] - ch_gain) < 1e-3)
                    {
                        channelGainIndex = j;
                        break;
                    }
                }
                if (k < MAX_NUM_HEADSTAGES)
                    type = ch < numChannelsPerHeadstage[k] ? DataChannel::HEADSTAGE_CHANNEL : DataChannel::AUX_CHANNEL;
                else
                    type = DataChannel::ADC_CHANNEL;

                FPGAchannelComponent* comp = new FPGAchannelComponent(this, realChan, channelGainIndex + 1, thread->getChannelName(realChan), gains,type);
                comp->setBounds(10 + hsColumn[k], 70 + ch * 22, columnWidth, 22);
                comp->setUserDefinedData(k);
                addAndMakeVisible(comp);
                channelComponents.add(comp);
            }
        }
    }


    StringArray ttlNames;
    proc->getEventChannelNames(ttlNames);
    // add buttons for TTL channels
    for (int k=0; k<ttlNames.size(); k++)
    {
        FPGAchannelComponent* comp = new FPGAchannelComponent(this,k, -1, ttlNames[k],gains,DataChannel::INVALID); //let's treat invalid as an event channel
        comp->setBounds(10+numActiveHeadstages*columnWidth,70+k*22,columnWidth,22);
        comp->setUserDefinedData(k);
        addAndMakeVisible(comp);
        channelComponents.add(comp);
    }

    Label* lbl = new Label("TTL Events","TTL Events");
    lbl->setEditable(false);
    lbl->setBounds(numActiveHeadstages*columnWidth,40,columnWidth, 25);
    lbl->setJustificationType(juce::Justification::centred);
    lbl->setColour(Label::textColourId,juce::Colours::white);
    staticLabels.add(lbl);
    addAndMakeVisible(lbl);

    chainUpdate = true;
}

void FPGAchannelList::disableAll()
{
    for (int k=0; k<channelComponents.size(); k++)
    {
        channelComponents[k]->disableEdit();
    }
	impedanceButton->setEnabled(false);
	saveImpedanceButton->setEnabled(false);
	autoMeasureButton->setEnabled(false);
	numberingScheme->setEnabled(false);
}

void FPGAchannelList::enableAll()
{
    for (int k=0; k<channelComponents.size(); k++)
    {
        channelComponents[k]->enableEdit();
    }
	impedanceButton->setEnabled(true);
	saveImpedanceButton->setEnabled(true);
	autoMeasureButton->setEnabled(true);
	numberingScheme->setEnabled(true);
}

void FPGAchannelList::setNewGain(int channel, float gain)
{
    RHD2000Thread* thread = (RHD2000Thread*)proc->getThread();
    thread->modifyChannelGain(channel, gain);
    if (chainUpdate)
        proc->requestChainUpdate();
}

void FPGAchannelList::setNewName(int channel, String newName)
{
    RHD2000Thread* thread = (RHD2000Thread*)proc->getThread();
    thread->modifyChannelName(channel, newName);
    if (chainUpdate)
        proc->requestChainUpdate();
}

void FPGAchannelList::updateButtons()
{
}

int FPGAchannelList::getNumChannels()
{
    return 0;
}

void FPGAchannelList::comboBoxChanged(ComboBox* b)
{
    if (b == numberingScheme)
    {
        SourceNode* p = (SourceNode*)proc;
        RHD2000Thread* thread = (RHD2000Thread*)p->getThread();
        int scheme = numberingScheme->getSelectedId();
        thread->setDefaultNamingScheme(scheme);
        update();
        p->requestChainUpdate();
    }
}

void FPGAchannelList::updateImpedance(Array<int> streams, Array<int> channels, Array<float> magnitude, Array<float> phase)
{
	int i = 0;
    for (int k = 0; k < streams.size(); k++)
    {
		if (i >= channelComponents.size())
			break; //little safety

		if (channelComponents[i]->type != DataChannel::HEADSTAGE_CHANNEL)
		{
			k--;
		}
		else
		{
			channelComponents[i]->setImpedanceValues(magnitude[k], phase[k]);
		}
		i++;
    }

}


/****************************************************/
FPGAchannelComponent::FPGAchannelComponent(FPGAchannelList* cl, int ch, int gainIndex_, String N, Array<float> gains_, DataChannel::DataChannelTypes type_) :
type(type_), gains(gains_), channelList(cl), channel(ch), name(N), gainIndex(gainIndex_)
{
    Font f = Font("Small Text", 13, Font::plain);

    staticLabel = new Label("Channel","Channel");
    staticLabel->setFont(f);
    staticLabel->setEditable(false);
    addAndMakeVisible(staticLabel);

    editName = new Label(name,name);
    editName->setFont(f);
    editName->setEditable(true);
    editName->setColour(Label::backgroundColourId,juce::Colours::lightgrey);
    editName->addListener(this);
    addAndMakeVisible(editName);
/*    if (gainIndex > 0)
    {

        gainComboBox = new ComboBox("Gains");
        for (int k=0; k<gains.size(); k++)
        {
            if (gains[k] < 1)
            {
                gainComboBox->addItem("x"+String(gains[k],2),k+1);
            }
            else
            {
                gainComboBox->addItem("x"+String((int)gains[k]),k+1);
            }
        }
        gainComboBox->setSelectedId(gainIndex, sendNotificationSync);
        gainComboBox->addListener(this);
        addAndMakeVisible(gainComboBox);
    }
    else
    {*/
        gainComboBox = nullptr;
    //}

    if (type == DataChannel::HEADSTAGE_CHANNEL)
    {
        impedance = new Label("Impedance","? Ohm");
        impedance->setFont(Font("Default", 13, Font::plain));
        impedance->setEditable(false);
        addAndMakeVisible(impedance);
    }
    else
    {
        impedance = nullptr;
    }
}
FPGAchannelComponent::~FPGAchannelComponent()
{

}

void FPGAchannelComponent::setImpedanceValues(float mag, float phase)
{
    if (impedance != nullptr)
    {
        if (mag > 10000)
            impedance->setText(String(mag/1e6,2)+" mOhm, "+String((int)phase) + " deg",juce::NotificationType::dontSendNotification);
        else if (mag > 1000)
            impedance->setText(String(mag/1e3,0)+" kOhm, "+String((int)phase) + " deg" ,juce::NotificationType::dontSendNotification);
        else
            impedance->setText(String(mag,0)+" Ohm, "+String((int)phase) + " deg" ,juce::NotificationType::dontSendNotification);
    }
    else
    {

    }
}

void FPGAchannelComponent::comboBoxChanged(ComboBox* comboBox)
{
    if (comboBox == gainComboBox)
    {
        int newGainIndex = gainComboBox->getSelectedId();
        float mult = gains[newGainIndex-1];
        float bitvolts = channelList->proc->getBitVolts(channelList->proc->getDataChannel(channel));
        channelList->setNewGain(channel, mult*bitvolts);
    }
}
void FPGAchannelComponent::labelTextChanged(Label* lbl)
{
    // channel name change
    String newName = lbl->getText();
    channelList->setNewName(channel, newName);
}

void FPGAchannelComponent::disableEdit()
{
    editName->setEnabled(false);
}

void FPGAchannelComponent::enableEdit()
{
    editName->setEnabled(true);
}

void FPGAchannelComponent::buttonClicked(Button* btn)
{
}

void FPGAchannelComponent::setUserDefinedData(int d)
{
}

int FPGAchannelComponent::getUserDefinedData()
{
    return 0;
}

void FPGAchannelComponent::resized()
{
    editName->setBounds(0,0,90,20);
    if (gainComboBox != nullptr)
    {
        gainComboBox->setBounds(100,0,70,20);
    }
    if (impedance != nullptr)
    {
       // impedance->setBounds(180,0,130,20);
		impedance->setBounds(100, 0, 130, 20);
    }

}



/**********************************************/

FPGAcanvas::FPGAcanvas(GenericProcessor* n)
{
    processor = (SourceNode*)n;
    channelsViewport = new Viewport();
    channelList = new FPGAchannelList(processor, channelsViewport, this);
    channelsViewport->setViewedComponent(channelList, false);
    channelsViewport->setScrollBarsShown(true, true);
    addAndMakeVisible(channelsViewport);

    resized();
    update();
}

FPGAcanvas::~FPGAcanvas()
{
}

void FPGAcanvas::setParameter(int x, float f)
{

}

void FPGAcanvas::setParameter(int a, int b, int c, float d)
{
}

void FPGAcanvas::paint(Graphics& g)
{
    g.fillAll(Colours::grey);

}

void FPGAcanvas::refresh()
{
    repaint();
}

void FPGAcanvas::refreshState()
{
    resized();
}


void FPGAcanvas::beginAnimation()
{
}

void FPGAcanvas::endAnimation()
{
}

void FPGAcanvas::update()
{
    // create channel buttons (name, gain, recording, impedance, ... ?)
    channelList->update();
	if (static_cast<RHD2000Thread*>(processor->getThread())->isAcquisitionActive())
	{
		channelList->disableAll();
	}
}

void FPGAcanvas::resized()
{
    //int screenWidth = getWidth();
    //int screenHeight = getHeight();

    int scrollBarThickness = channelsViewport->getScrollBarThickness();
    int numChannels = 35; // max channels per stream? (32+3)*2

    channelsViewport->setBounds(0,0,getWidth(),getHeight());
    channelList->setBounds(0,0,getWidth()-scrollBarThickness, 200+22*numChannels);
}

void FPGAcanvas::buttonClicked(Button* button)
{
}

void FPGAcanvas::updateImpedance(Array<int> streams, Array<int> channels, Array<float> magnitude, Array<float> phase)
{
    channelList->updateImpedance(streams, channels,  magnitude, phase);
}

/***********************************************************************/

RHD2000Editor::RHD2000Editor(GenericProcessor* parentNode,
                             RHD2000Thread* board_,
                             bool useDefaultParameterEditors
                            )
    : VisualizerEditor(parentNode, useDefaultParameterEditors), board(board_)
{
    canvas = nullptr;
    desiredWidth = 340;
    tabText = "FPGA";
    measureWhenRecording = false;
    saveImpedances = false;

	impedanceData = new ImpedanceData();
	impedanceData->valid = false;

    // add headstage-specific controls (currently just an enable/disable button)
    for (int i = 0; i < 4; i++)
    {
        HeadstageOptionsInterface* hsOptions = new HeadstageOptionsInterface(board, this, i);
        headstageOptionsInterfaces.add(hsOptions);
        addAndMakeVisible(hsOptions);
        hsOptions->setBounds(3, 28+i*20, 70, 18);
    }

    // add sample rate selection
    sampleRateInterface = new SampleRateInterface(board, this);
    addAndMakeVisible(sampleRateInterface);
    sampleRateInterface->setBounds(80, 25, 110, 50);

    // add Bandwidth selection
    bandwidthInterface = new BandwidthInterface(board, this);
    addAndMakeVisible(bandwidthInterface);
    bandwidthInterface->setBounds(80, 58, 80, 50);

    // add DSP selection
  //  dspInterface = new DSPInterface(board, this);
  //  addAndMakeVisible(dspInterface);
  //  dspInterface->setBounds(80, 58, 80, 50);

    // add rescan button
    rescanButton = new UtilityButton("RESCAN", Font("Small Text", 13, Font::plain));
    rescanButton->setRadius(3.0f);
    rescanButton->setBounds(6, 108,65,18);
    rescanButton->addListener(this);
    rescanButton->setTooltip("Check for connected headstages");
    addAndMakeVisible(rescanButton);

    for (int i = 0; i < 2; i++)
    {
        ElectrodeButton* button = new ElectrodeButton(-1);
        electrodeButtons.add(button);

        button->setBounds(200+i*25, 40, 25, 15);
        button->setChannelNum(-1);
        button->setToggleState(false, dontSendNotification);
        button->setRadioGroupId(999);

        addAndMakeVisible(button);
        button->addListener(this);

        if (i == 0)
        {
            button->setTooltip("Audio monitor left channel");
        }
        else
        {
            button->setTooltip("Audio monitor right channel");
        }
    }

    audioLabel = new Label("audio label", "Audio out");
    audioLabel->setBounds(190,25,75,15);
    audioLabel->setFont(Font("Small Text", 10, Font::plain));
    audioLabel->setColour(Label::textColourId, Colours::darkgrey);
    addAndMakeVisible(audioLabel);

    // add HW audio parameter selection
    audioInterface = new AudioInterface(board, this);
    addAndMakeVisible(audioInterface);
    audioInterface->setBounds(179, 58, 70, 50);

    clockInterface = new ClockDivideInterface(board, this);
    addAndMakeVisible(clockInterface);
    clockInterface->setBounds(179, 82, 70, 50);

    adcButton = new UtilityButton("ADC 1-8", Font("Small Text", 13, Font::plain));
    adcButton->setRadius(3.0f);
    adcButton->setBounds(179,108,70,18);
    adcButton->addListener(this);
    adcButton->setClickingTogglesState(true);
    adcButton->setTooltip("Enable/disable ADC channels");
    addAndMakeVisible(adcButton);

	ledButton = new UtilityButton("LED", Font("Very Small Text", 13, Font::plain));
	ledButton->setRadius(3.0f);
	ledButton->setBounds(140, 108, 30, 18);
	ledButton->addListener(this);
	ledButton->setClickingTogglesState(true);
	ledButton->setTooltip("Enable/disable board LEDs");
	addAndMakeVisible(ledButton);
	ledButton->setToggleState(true, dontSendNotification);

    // add DSP Offset Button
    dspoffsetButton = new UtilityButton("DSP", Font("Very Small Text", 13, Font::plain));
    dspoffsetButton->setRadius(3.0f); // sets the radius of the button's corners
    dspoffsetButton->setBounds(80, 108,30,18); // sets the x position, y position, width, and height of the button
    dspoffsetButton->addListener(this);
    dspoffsetButton->setClickingTogglesState(true); // makes the button toggle its state when clicked
    dspoffsetButton->setTooltip("Enable/disable DSP offset removal");
    addAndMakeVisible(dspoffsetButton); // makes the button a child component of the editor and makes it visible
    dspoffsetButton->setToggleState(true, dontSendNotification);

    // add DSP Frequency Selection field
    dspInterface = new DSPInterface(board, this);
    addAndMakeVisible(dspInterface);
    dspInterface->setBounds(110, 108, 30, 50);

    ttlSettleLabel = new Label("TTL Settle","TTL Settle");
    ttlSettleLabel->setFont(Font("Small Text", 11, Font::plain));
    ttlSettleLabel->setBounds(255,80,70,20);
    ttlSettleLabel->setColour(Label::textColourId, Colours::darkgrey);
    addAndMakeVisible(ttlSettleLabel);


    ttlSettleCombo = new ComboBox("FastSettleComboBox");
    ttlSettleCombo->setBounds(260,100,60,18);
    ttlSettleCombo->addListener(this);
    ttlSettleCombo->addItem("-",1);
    for (int k=0; k<8; k++)
    {
        ttlSettleCombo->addItem("TTL"+String(1+k),2+k);
    }
    ttlSettleCombo->setSelectedId(1, sendNotification);
    addAndMakeVisible(ttlSettleCombo);

    dacTTLButton = new UtilityButton("DAC TTL", Font("Small Text", 13, Font::plain));
    dacTTLButton->setRadius(3.0f);
    dacTTLButton->setBounds(260,25,65,18);
    dacTTLButton->addListener(this);
    dacTTLButton->setClickingTogglesState(true);
    dacTTLButton->setTooltip("Enable/disable DAC Threshold TTL Output");
    addAndMakeVisible(dacTTLButton);

    dacHPFlabel = new Label("DAC HPF","DAC HPF");
    dacHPFlabel->setFont(Font("Small Text", 11, Font::plain));
    dacHPFlabel->setBounds(260,42,65,20);
    dacHPFlabel->setColour(Label::textColourId, Colours::darkgrey);
    addAndMakeVisible(dacHPFlabel);

    dacHPFcombo = new ComboBox("dacHPFCombo");
    dacHPFcombo->setBounds(260,60,60,18);
    dacHPFcombo->addListener(this);
    dacHPFcombo->addItem("OFF",1);
    int HPFvalues[10] = {50,100,200,300,400,500,600,700,800,900};
    for (int k=0; k<10; k++)
    {
        dacHPFcombo->addItem(String(HPFvalues[k])+" Hz",2+k);
    }
    dacHPFcombo->setSelectedId(1, sendNotification);
    addAndMakeVisible(dacHPFcombo);

}

RHD2000Editor::~RHD2000Editor()
{

}

void RHD2000Editor::scanPorts()
{
    rescanButton->triggerClick();
}

void RHD2000Editor::measureImpedance()
{
	impedanceData->valid = false;
	board->runImpedanceTest(impedanceData);
}

void RHD2000Editor::handleAsyncUpdate()
{
	if (!impedanceData->valid)
		return;
    if (canvas == nullptr)
        VisualizerEditor::canvas = createNewCanvas();
    // update components...
	canvas->updateImpedance(impedanceData->streams, impedanceData->channels, impedanceData->magnitudes, impedanceData->phases);
    if (saveImpedances)
    {
		CoreServices::RecordNode::createNewrecordingDir();

		String path(CoreServices::RecordNode::getRecordingPath().getFullPathName()
                    + File::separatorString + "impedance_measurement.xml");
        std::cout << "Saving impedance measurements in " << path << "\n";
        File file(path);

        if (!file.getParentDirectory().exists())
            file.getParentDirectory().createDirectory();

        XmlDocument doc(file);
        ScopedPointer<XmlElement> xml = new XmlElement("CHANNEL_IMPEDANCES");
		for (int i = 0; i < impedanceData->channels.size(); i++)
        {
            XmlElement* chan = new XmlElement("CHANNEL");
            chan->setAttribute("name",board->getChannelName(i));
			chan->setAttribute("stream", impedanceData->streams[i]);
			chan->setAttribute("channel_number", impedanceData->channels[i]);
			chan->setAttribute("magnitude", impedanceData->magnitudes[i]);
			chan->setAttribute("phase", impedanceData->phases[i]);
            xml->addChildElement(chan);
        }
        xml->writeToFile(file,String::empty);
    }

}

void RHD2000Editor::setSaveImpedance(bool en)
{
    saveImpedances = en;
}

void RHD2000Editor::setAutoMeasureImpedance(bool en)
{
    measureWhenRecording = en;
}

bool RHD2000Editor::getSaveImpedance()
{
    return saveImpedances;
}

bool RHD2000Editor::getAutoMeasureImpedance()
{
    return measureWhenRecording;
}

void RHD2000Editor::comboBoxChanged(ComboBox* comboBox)
{
    if (comboBox == ttlSettleCombo)
    {
        int selectedChannel = ttlSettleCombo->getSelectedId();
        if (selectedChannel == 1)
        {
            board->setFastTTLSettle(false,0);
        }
        else
        {
            board->setFastTTLSettle(true,selectedChannel-2);
        }
    }
    else if (comboBox == dacHPFcombo)
    {
        int selection = dacHPFcombo->getSelectedId();
        if (selection == 1)
        {
            board->setDAChpf(100,false);
        }
        else
        {
            int HPFvalues[10] = {50,100,200,300,400,500,600,700,800,900};
            board->setDAChpf(HPFvalues[selection-2],true);
        }
    }
}


void RHD2000Editor::buttonEvent(Button* button)
{
    if (button == rescanButton && !acquisitionIsActive)
    {
        board->scanPorts();

        for (int i = 0; i < 4; i++)
        {
            headstageOptionsInterfaces[i]->checkEnabledState();
        }
        // board->updateChannelNames();
		CoreServices::updateSignalChain(this);
    }
    else if (button == electrodeButtons[0])
    {
        channelSelector->setRadioStatus(true);
    }
    else if (button == electrodeButtons[1])
    {
        channelSelector->setRadioStatus(true);
    }
    else if (button == adcButton && !acquisitionIsActive)
    {
        board->enableAdcs(button->getToggleState());
        //        board->updateChannelNames();
        std::cout << "ADC Button toggled" << "\n";
		CoreServices::updateSignalChain(this);
        std::cout << "Editor visible." << "\n";
    }
    else if (button == dacTTLButton)
    {
        board->setTTLoutputMode(dacTTLButton->getToggleState());
    }
    else if (button == dspoffsetButton && !acquisitionIsActive)
    {
        std::cout << "DSP offset " << button->getToggleState() << "\n";
        board->setDSPOffset(button->getToggleState());
    }
	else if (button == ledButton)
	{
		board->enableBoardLeds(button->getToggleState());
	}
    /*
	else
	{
		VisualizerEditor::buttonEvent(button);
	}
    */

}

void RHD2000Editor::channelChanged (int channel, bool /*newState*/)
{
    // Audio output is tied to DAC channels 0 and 1
    for (int i = 0; i < 2; i++)
    {
        if (electrodeButtons[i]->getToggleState())
        {
            electrodeButtons[i]->setChannelNum (channel);
            electrodeButtons[i]->repaint();
            board->setDACchannel (i, channel - 1); // HW channels are zero-based
        }
    }
}

void RHD2000Editor::startAcquisition()
{
    if (measureWhenRecording)
        measureImpedance();

    channelSelector->startAcquisition();

    rescanButton->setEnabledState(false);
    adcButton->setEnabledState(false);
    dspoffsetButton-> setEnabledState(false);
    acquisitionIsActive = true;
	if (canvas != nullptr)
		canvas->channelList->disableAll();
        //canvas->channelList->setEnabled(false);
}

void RHD2000Editor::stopAcquisition()
{

    channelSelector->stopAcquisition();

    rescanButton->setEnabledState(true);
    adcButton->setEnabledState(true);
    dspoffsetButton-> setEnabledState(true);

    acquisitionIsActive = false;
	if (canvas != nullptr)
		canvas->channelList->enableAll();
    //  canvas->channelList->setEnabled(true);
}

void RHD2000Editor::saveCustomParameters(XmlElement* xml)
{
    xml->setAttribute("SampleRate", sampleRateInterface->getSelectedId());
    xml->setAttribute("LowCut", bandwidthInterface->getLowerBandwidth());
    xml->setAttribute("HighCut", bandwidthInterface->getUpperBandwidth());
    xml->setAttribute("ADCsOn", adcButton->getToggleState());
    xml->setAttribute("SampleRate", sampleRateInterface->getSelectedId());
    xml->setAttribute("LowCut", bandwidthInterface->getLowerBandwidth());
    xml->setAttribute("HighCut", bandwidthInterface->getUpperBandwidth());
    xml->setAttribute("ADCsOn", adcButton->getToggleState());
    xml->setAttribute("AudioOutputL", electrodeButtons[0]->getChannelNum());
    xml->setAttribute("AudioOutputR", electrodeButtons[1]->getChannelNum());
    xml->setAttribute("NoiseSlicer", audioInterface->getNoiseSlicerLevel());
    xml->setAttribute("TTLFastSettle", ttlSettleCombo->getSelectedId());
    xml->setAttribute("DAC_TTL", dacTTLButton->getToggleState());
    xml->setAttribute("DAC_HPF", dacHPFcombo->getSelectedId());
    xml->setAttribute("DSPOffset", dspoffsetButton->getToggleState());
    xml->setAttribute("DSPCutoffFreq", dspInterface->getDspCutoffFreq());
    xml->setAttribute("save_impedance_measurements",saveImpedances);
    xml->setAttribute("auto_measure_impedances",measureWhenRecording);
	xml->setAttribute("LEDs", ledButton->getToggleState());
	xml->setAttribute("ClockDivideRatio", clockInterface->getClockDivideRatio());
}

void RHD2000Editor::loadCustomParameters(XmlElement* xml)
{

    sampleRateInterface->setSelectedId(xml->getIntAttribute("SampleRate"));
    bandwidthInterface->setLowerBandwidth(xml->getDoubleAttribute("LowCut"));
    bandwidthInterface->setUpperBandwidth(xml->getDoubleAttribute("HighCut"));
    adcButton->setToggleState(xml->getBoolAttribute("ADCsOn"), sendNotification);
    //electrodeButtons[0]->setChannelNum(xml->getIntAttribute("AudioOutputL"));
    //board->assignAudioOut(0, xml->getIntAttribute("AudioOutputL"));
    //electrodeButtons[1]->setChannelNum(xml->getIntAttribute("AudioOutputR"));
    //board->assignAudioOut(1, xml->getIntAttribute("AudioOutputR"));
    audioInterface->setNoiseSlicerLevel(xml->getIntAttribute("NoiseSlicer"));
    ttlSettleCombo->setSelectedId(xml->getIntAttribute("TTLFastSettle"));
    dacTTLButton->setToggleState(xml->getBoolAttribute("DAC_TTL"), sendNotification);
    dacHPFcombo->setSelectedId(xml->getIntAttribute("DAC_HPF"));
    dspoffsetButton->setToggleState(xml->getBoolAttribute("DSPOffset"), sendNotification);
    dspInterface->setDspCutoffFreq(xml->getDoubleAttribute("DSPCutoffFreq"));
    saveImpedances = xml->getBoolAttribute("save_impedance_measurements");
    measureWhenRecording = xml->getBoolAttribute("auto_measure_impedances");
	ledButton->setToggleState(xml->getBoolAttribute("LEDs", true),sendNotification);
    clockInterface->setClockDivideRatio(xml->getIntAttribute("ClockDivideRatio")); 
}


Visualizer* RHD2000Editor::createNewCanvas()
{
    GenericProcessor* processor = (GenericProcessor*) getProcessor();
    canvas= new FPGAcanvas(processor);
    //ActionListener* listener = (ActionListener*) canvas;
    //getUIComponent()->registerAnimatedComponent(listener);
    return canvas;
}

// Bandwidth Options --------------------------------------------------------------------

BandwidthInterface::BandwidthInterface(RHD2000Thread* board_,
                                       RHD2000Editor* editor_) :
    board(board_), editor(editor_)
{

    name = "Bandwidth";

    lastHighCutString = "7500";
    lastLowCutString = "1";

    actualUpperBandwidth = 7500.0f;
    actualLowerBandwidth = 1.0f;

    upperBandwidthSelection = new Label("UpperBandwidth",lastHighCutString); // this is currently set in RHD2000Thread, the cleaner would be to set it here again
    upperBandwidthSelection->setEditable(true,false,false);
    upperBandwidthSelection->addListener(this);
    upperBandwidthSelection->setBounds(30,30,60,20);
    upperBandwidthSelection->setColour(Label::textColourId, Colours::darkgrey);
    addAndMakeVisible(upperBandwidthSelection);


    lowerBandwidthSelection = new Label("LowerBandwidth",lastLowCutString);
    lowerBandwidthSelection->setEditable(true,false,false);
    lowerBandwidthSelection->addListener(this);
    lowerBandwidthSelection->setBounds(25,10,60,20);
    lowerBandwidthSelection->setColour(Label::textColourId, Colours::darkgrey);

    addAndMakeVisible(lowerBandwidthSelection);



}

BandwidthInterface::~BandwidthInterface()
{

}


void BandwidthInterface::labelTextChanged(Label* label)
{

    if (!(editor->acquisitionIsActive) && board->foundInputSource())
    {
        if (label == upperBandwidthSelection)
        {

            Value val = label->getTextValue();
            double requestedValue = double(val.getValue());

            if (requestedValue < 100.0 || requestedValue > 20000.0 || requestedValue < lastLowCutString.getFloatValue())
            {
                CoreServices::sendStatusMessage("Value out of range.");

                label->setText(lastHighCutString, dontSendNotification);

                return;
            }

            actualUpperBandwidth = board->setUpperBandwidth(requestedValue);

            std::cout << "Setting Upper Bandwidth to " << requestedValue << "\n";
            std::cout << "Actual Upper Bandwidth:  " <<  actualUpperBandwidth  << "\n";
            label->setText(String(round(actualUpperBandwidth*10.f)/10.f), dontSendNotification);

        }
        else
        {

            Value val = label->getTextValue();
            double requestedValue = double(val.getValue());

            if (requestedValue < 0.1 || requestedValue > 500.0 || requestedValue > lastHighCutString.getFloatValue())
            {
				CoreServices::sendStatusMessage("Value out of range.");

                label->setText(lastLowCutString, dontSendNotification);

                return;
            }

            actualLowerBandwidth = board->setLowerBandwidth(requestedValue);

            std::cout << "Setting Lower Bandwidth to " << requestedValue << "\n";
            std::cout << "Actual Lower Bandwidth:  " <<  actualLowerBandwidth  << "\n";

            label->setText(String(round(actualLowerBandwidth*10.f)/10.f), dontSendNotification);
        }
    }
    else if (editor->acquisitionIsActive)
    {
		CoreServices::sendStatusMessage("Can't change bandwidth while acquisition is active!");
        if (label == upperBandwidthSelection)
            label->setText(lastHighCutString, dontSendNotification);
        else
            label->setText(lastLowCutString, dontSendNotification);
        return;
    }

}

void BandwidthInterface::setLowerBandwidth(double value)
{
    actualLowerBandwidth = board->setLowerBandwidth(value);
    lowerBandwidthSelection->setText(String(round(actualLowerBandwidth*10.f)/10.f), dontSendNotification);
}

void BandwidthInterface::setUpperBandwidth(double value)
{
    actualUpperBandwidth = board->setUpperBandwidth(value);
    upperBandwidthSelection->setText(String(round(actualUpperBandwidth*10.f)/10.f), dontSendNotification);
}

double BandwidthInterface::getLowerBandwidth()
{
    return actualLowerBandwidth;
}

double BandwidthInterface::getUpperBandwidth()
{
    return actualUpperBandwidth;
}


void BandwidthInterface::paint(Graphics& g)
{

    g.setColour(Colours::darkgrey);

    g.setFont(Font("Small Text",10,Font::plain));

    g.drawText(name, 0, 0, 200, 15, Justification::left, false);

    g.drawText("Low: ", 0, 10, 200, 20, Justification::left, false);

    g.drawText("High: ", 0, 30, 200, 20, Justification::left, false);

}

// Sample rate Options --------------------------------------------------------------------

SampleRateInterface::SampleRateInterface(RHD2000Thread* board_,
                                         RHD2000Editor* editor_) :
    board(board_), editor(editor_)
{

    name = "Sample Rate";

    sampleRateOptions.add("1.00 kS/s");
    sampleRateOptions.add("1.25 kS/s");
    sampleRateOptions.add("1.50 kS/s");
    sampleRateOptions.add("2.00 kS/s");
    sampleRateOptions.add("2.50 kS/s");
    sampleRateOptions.add("3.00 kS/s");
    sampleRateOptions.add("3.33 kS/s");
    sampleRateOptions.add("4.00 kS/s");
    sampleRateOptions.add("5.00 kS/s");
    sampleRateOptions.add("6.25 kS/s");
    sampleRateOptions.add("8.00 kS/s");
    sampleRateOptions.add("10.0 kS/s");
    sampleRateOptions.add("12.5 kS/s");
    sampleRateOptions.add("15.0 kS/s");
    sampleRateOptions.add("20.0 kS/s");
    sampleRateOptions.add("25.0 kS/s");
    sampleRateOptions.add("30.0 kS/s");


    rateSelection = new ComboBox("Sample Rate");
    rateSelection->addItemList(sampleRateOptions, 1);
    rateSelection->setSelectedId(17, dontSendNotification);
    rateSelection->addListener(this);

    rateSelection->setBounds(0,15,300,20);
    addAndMakeVisible(rateSelection);


}

SampleRateInterface::~SampleRateInterface()
{

}

void SampleRateInterface::comboBoxChanged(ComboBox* cb)
{
    if (!(editor->acquisitionIsActive) && board->foundInputSource())
    {
        if (cb == rateSelection)
        {
            board->setSampleRate(cb->getSelectedId()-1);

            std::cout << "Setting sample rate to index " << cb->getSelectedId()-1 << "\n";

			CoreServices::updateSignalChain(editor);
        }
    }
}

int SampleRateInterface::getSelectedId()
{
    return rateSelection->getSelectedId();
}

void SampleRateInterface::setSelectedId(int id)
{
    rateSelection->setSelectedId(id);
}


void SampleRateInterface::paint(Graphics& g)
{

    g.setColour(Colours::darkgrey);

    g.setFont(Font("Small Text",10,Font::plain));

    g.drawText(name, 0, 0, 200, 15, Justification::left, false);

}


// Headstage Options --------------------------------------------------------------------

HeadstageOptionsInterface::HeadstageOptionsInterface(RHD2000Thread* board_,
                                                     RHD2000Editor* editor_,
                                                     int hsNum) :
    isEnabled(false), board(board_), editor(editor_)
{

    switch (hsNum)
    {
        case 0 :
            name = "A";
            break;
        case 1:
            name = "B";
            break;
        case 2:
            name = "C";
            break;
        case 3:
            name = "D";
            break;
        default:
            name = "X";
    }

    hsNumber1 = hsNum*2; // data stream 1
    hsNumber2 = hsNumber1+1; // data stream 2

    channelsOnHs1 = 0;
    channelsOnHs2 = 0;



    hsButton1 = new UtilityButton(" ", Font("Small Text", 13, Font::plain));
    hsButton1->setRadius(3.0f);
    hsButton1->setBounds(23,1,20,17);
    hsButton1->setEnabledState(false);
    hsButton1->setCorners(true, false, true, false);
    hsButton1->addListener(this);
    addAndMakeVisible(hsButton1);

    hsButton2 = new UtilityButton(" ", Font("Small Text", 13, Font::plain));
    hsButton2->setRadius(3.0f);
    hsButton2->setBounds(43,1,20,17);
    hsButton2->setEnabledState(false);
    hsButton2->setCorners(false, true, false, true);
    hsButton2->addListener(this);
    addAndMakeVisible(hsButton2);

    checkEnabledState();
}

HeadstageOptionsInterface::~HeadstageOptionsInterface()
{

}

void HeadstageOptionsInterface::checkEnabledState()
{
    isEnabled = (board->isHeadstageEnabled(hsNumber1) ||
                 board->isHeadstageEnabled(hsNumber2));

    if (board->isHeadstageEnabled(hsNumber1))
    {
        channelsOnHs1 = board->getActiveChannelsInHeadstage(hsNumber1);
        hsButton1->setLabel(String(channelsOnHs1));
        hsButton1->setEnabledState(true);
    }
    else
    {
        channelsOnHs1 = 0;
        hsButton1->setLabel(" ");
        hsButton1->setEnabledState(false);
    }

    if (board->isHeadstageEnabled(hsNumber2))
    {
        channelsOnHs2 = board->getActiveChannelsInHeadstage(hsNumber2);
        hsButton2->setLabel(String(channelsOnHs2));
        hsButton2->setEnabledState(true);
    }
    else
    {
        channelsOnHs2 = 0;
        hsButton2->setLabel(" ");
        hsButton2->setEnabledState(false);
    }

    repaint();

}

void HeadstageOptionsInterface::buttonClicked(Button* button)
{

    if (!(editor->acquisitionIsActive) && board->foundInputSource())
    {

        //std::cout << "Acquisition is not active" << "\n";
        if ((button == hsButton1) && (board->getChannelsInHeadstage(hsNumber1) == 32))
        {
            if (channelsOnHs1 == 32)
                channelsOnHs1 = 16;
            else
                channelsOnHs1 = 32;

            //std::cout << "HS1 has " << channelsOnHs1 << " channels." << "\n";

            hsButton1->setLabel(String(channelsOnHs1));
            board->setNumChannels(hsNumber1, channelsOnHs1);

            //board->updateChannels();
            editor->updateSettings();

        }
        else if ((button == hsButton2) && (board->getChannelsInHeadstage(hsNumber2) == 32))
        {
            if (channelsOnHs2 == 32)
                channelsOnHs2 = 16;
            else
                channelsOnHs2 = 32;

            hsButton2->setLabel(String(channelsOnHs2));
            board->setNumChannels(hsNumber2, channelsOnHs2);
            //board->updateChannels();
            editor->updateSettings();
        }

		CoreServices::updateSignalChain(editor);
    }

}


void HeadstageOptionsInterface::paint(Graphics& g)
{
    g.setColour(Colours::lightgrey);

    g.fillRoundedRectangle(5,0,getWidth()-10,getHeight(),4.0f);

    if (isEnabled)
        g.setColour(Colours::black);
    else
        g.setColour(Colours::grey);

    g.setFont(Font("Small Text",15,Font::plain));

    g.drawText(name, 8, 2, 200, 15, Justification::left, false);

}


// (Direct OpalKelly) Audio Options --------------------------------------------------------------------

AudioInterface::AudioInterface(RHD2000Thread* board_,
                               RHD2000Editor* editor_) :
    board(board_), editor(editor_)
{

    name = "Noise Slicer";

    lastNoiseSlicerString = "0";

    actualNoiseSlicerLevel = 0.0f;

    noiseSlicerLevelSelection = new Label("Noise Slicer",lastNoiseSlicerString); // this is currently set in RHD2000Thread, the cleaner would be to set it here again
    noiseSlicerLevelSelection->setEditable(true,false,false);
    noiseSlicerLevelSelection->addListener(this);
    noiseSlicerLevelSelection->setBounds(30,10,30,20);
    noiseSlicerLevelSelection->setColour(Label::textColourId, Colours::darkgrey);
    addAndMakeVisible(noiseSlicerLevelSelection);


}

AudioInterface::~AudioInterface()
{

}


void AudioInterface::labelTextChanged(Label* label)
{
    if (board->foundInputSource())
    {
        if (label == noiseSlicerLevelSelection)
        {

            Value val = label->getTextValue();
            int requestedValue = int(val.getValue()); // Note that it might be nice to translate to actual uV levels (16*value)

            if (requestedValue < 0 || requestedValue > 127)
            {
				CoreServices::sendStatusMessage("Value out of range.");

                label->setText(lastNoiseSlicerString, dontSendNotification);

                return;
            }

            actualNoiseSlicerLevel = board->setNoiseSlicerLevel(requestedValue);

            std::cout << "Setting Noise Slicer Level to " << requestedValue << "\n";
            label->setText(String((roundFloatToInt)(actualNoiseSlicerLevel)), dontSendNotification);

        }
    }
    else
    {
        Value val = label->getTextValue();
        int requestedValue = int(val.getValue()); // Note that it might be nice to translate to actual uV levels (16*value)
        if (requestedValue < 0 || requestedValue > 127)
        {
			CoreServices::sendStatusMessage("Value out of range.");
            label->setText(lastNoiseSlicerString, dontSendNotification);
            return;
        }
    }
}

void AudioInterface::setNoiseSlicerLevel(int value)
{
    actualNoiseSlicerLevel = board->setNoiseSlicerLevel(value);
    noiseSlicerLevelSelection->setText(String(roundFloatToInt(actualNoiseSlicerLevel)), dontSendNotification);
}

int AudioInterface::getNoiseSlicerLevel()
{
    return actualNoiseSlicerLevel;
}


void AudioInterface::paint(Graphics& g)
{

    g.setColour(Colours::darkgrey);
    g.setFont(Font("Small Text",9,Font::plain));
    g.drawText(name, 0, 0, 200, 15, Justification::left, false);
    g.drawText("Level: ", 0, 10, 200, 20, Justification::left, false);
}


// Clock Divider options
ClockDivideInterface::ClockDivideInterface(RHD2000Thread* board_,
                                           RHD2000Editor* editor_) :
   name("Clock Divider")
 , lastDivideRatioString("1")
 , board(board_)
 , editor(editor_)
 , actualDivideRatio(1)
 
{
    divideRatioSelection = new Label("Clock Divide", lastDivideRatioString); 
    divideRatioSelection->setEditable(true,false,false);
    divideRatioSelection->addListener(this);
    divideRatioSelection->setBounds(30,10,30,20);
    divideRatioSelection->setColour(Label::textColourId, Colours::darkgrey);
    addAndMakeVisible(divideRatioSelection);
}

void ClockDivideInterface::labelTextChanged(Label* label)
{
    if (board->foundInputSource())
    {
        if (label == divideRatioSelection)
        {
            Value val = label->getTextValue();
            int requestedValue = int(val.getValue()); 

            if (requestedValue < 1 || requestedValue > 65534)
            {
				CoreServices::sendStatusMessage("Value must be between 1 and 65534.");
                label->setText(lastDivideRatioString, dontSendNotification);
                return;
            }

            actualDivideRatio = board->setClockDivider(requestedValue);
            lastDivideRatioString = String(actualDivideRatio);

            std::cout << "Setting clock divide ratio to " << actualDivideRatio << "\n";
            label->setText(lastDivideRatioString, dontSendNotification);
        }
    }
}

void ClockDivideInterface::setClockDivideRatio(int value)
{
    actualDivideRatio = board->setClockDivider(value);
    divideRatioSelection->setText(String(actualDivideRatio), dontSendNotification);
}

void ClockDivideInterface::paint(Graphics& g)
{

    g.setColour(Colours::darkgrey);
    g.setFont(Font("Small Text",9,Font::plain));
    g.drawText(name, 0, 0, 200, 15, Justification::left, false);
    g.drawText("Ratio: ", 0, 10, 200, 20, Justification::left, false);
}

// DSP Options --------------------------------------------------------------------

DSPInterface::DSPInterface(RHD2000Thread* board_,
                           RHD2000Editor* editor_) :
    board(board_), editor(editor_)
{
    name = "DSP";

    dspOffsetSelection = new Label("DspOffsetSelection",String(round(board->getDspCutoffFreq()*10.f)/10.f));
    dspOffsetSelection->setEditable(true,false,false);
    dspOffsetSelection->addListener(this);
    dspOffsetSelection->setBounds(0,0,30,20);
    dspOffsetSelection->setColour(Label::textColourId, Colours::darkgrey);

    addAndMakeVisible(dspOffsetSelection);

}

DSPInterface::~DSPInterface()
{

}


void DSPInterface::labelTextChanged(Label* label)
{

    if (!(editor->acquisitionIsActive) && board->foundInputSource())
    {
        if (label == dspOffsetSelection)
        {

            Value val = label->getTextValue();
            double requestedValue = double(val.getValue());

            actualDspCutoffFreq = board->setDspCutoffFreq(requestedValue);

            std::cout << "Setting DSP Cutoff Freq to " << requestedValue << "\n";
            std::cout << "Actual DSP Cutoff Freq:  " <<  actualDspCutoffFreq  << "\n";
            label->setText(String(round(actualDspCutoffFreq*10.f)/10.f), dontSendNotification);

        }
    }
    else if (editor->acquisitionIsActive)
    {
		CoreServices::sendStatusMessage("Can't change DSP cutoff while acquisition is active!");
    }

}

void DSPInterface::setDspCutoffFreq(double value)
{
    actualDspCutoffFreq = board->setDspCutoffFreq(value);
    dspOffsetSelection->setText(String(round(actualDspCutoffFreq*10.f)/10.f), dontSendNotification);
}


double DSPInterface::getDspCutoffFreq()
{
    return actualDspCutoffFreq;
}

void DSPInterface::paint(Graphics& g)
{

    g.setColour(Colours::darkgrey);

    g.setFont(Font("Small Text",10,Font::plain));

}