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

    This file is part of the Open Ephys GUI
    Copyright (C) 2013 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 "AudioNode.h"
#include "Channel.h"

AudioNode::AudioNode()
    : GenericProcessor("Audio Node"), audioEditor(0), volume(0.00001f), 
    overflowBuffer(2,10000),
    overflowOverflowBuffer(2,10000)
{

    settings.numInputs = 2048;
    settings.numOutputs = 2;

    // 128 inputs, 2 outputs (left and right channel)
    setPlayConfigDetails(getNumInputs(), getNumOutputs(), 44100.0, 128);

    //leftChan.clear();
    //rightChan.clear();

    nextAvailableChannel = 2; // keep first two channels empty

    //overflowBuffer.setSize(2, 10000); // 2 channels x 10000 samples

    numSamplesExpected = 1024;
    overflowBufferEndIndex = 0;
    overflowBufferStartIndex = 0;
    overflowSamples = 0;

}


AudioNode::~AudioNode()
{



}

AudioProcessorEditor* AudioNode::createEditor()
{

    audioEditor = new AudioEditor(this);
    //audioEditor->setUIComponent(getUIComponent());
    //audioEditor->updateBufferSizeText();

   // setEditor(editor);

    return audioEditor;

}

void AudioNode::resetConnections()
{

    nextAvailableChannel = 2; // start connections at channel 2
    wasConnected = false;

    channelPointers.clear();

}

void AudioNode::updateBufferSize()
{
    //AudioEditor* editor = (AudioEditor*) getEditor();
    audioEditor->updateBufferSizeText();
    
}

void AudioNode::setChannel(Channel* ch)
{

    int channelNum = channelPointers.indexOf(ch);

    std::cout << "Audio node setting channel to " << channelNum << std::endl;

    setCurrentChannel(channelNum);
}

void AudioNode::setChannelStatus(Channel* chan, bool status)
{

    setChannel(chan); // add 2 to account for 2 output channels

    enableCurrentChannel(status);

}

void AudioNode::enableCurrentChannel(bool state)
{

    //setCurrentChannel(nextAvailableChannel);

    if (state)
    {
        setParameter(100, 0.0f);
    }
    else
    {
        setParameter(-100, 0.0f);
    }
}


void AudioNode::addInputChannel(GenericProcessor* sourceNode, int chan)
{

    //if (chan != getProcessorGraph()->midiChannelIndex)
    //{

    int channelIndex = getNextChannel(false);

    setPlayConfigDetails(channelIndex+1,0,44100.0,128);

    channelPointers.add(sourceNode->channels[chan]);

    //} else {

    // Can't monitor events at the moment!
    //	}

}

void AudioNode::setParameter(int parameterIndex, float newValue)
{
    // change left channel, right channel, or volume
    if (parameterIndex == 1)
    {
        // volume level
        volume = newValue*0.1f;

    }
    else if (parameterIndex == 100)
    {

        channelPointers[currentChannel]->isMonitored = true;

        // add current channel
        // if (!leftChan.contains(currentChannel))
        // {
        // 	leftChan.add(currentChannel);
        // 	rightChan.add(currentChannel);
        // }
    }
    else if (parameterIndex == -100)
    {

        channelPointers[currentChannel]->isMonitored = false;
        // remove current channel
        // if (leftChan.contains(currentChannel))
        // {
        // 	leftChan.remove(leftChan.indexOf(currentChannel));
        // 	rightChan.remove(rightChan.indexOf(currentChannel));
        // }
    }

}

void AudioNode::prepareToPlay(double sampleRate_, int estimatedSamplesPerBlock)
{


    std::cout << "Processor sample rate: " << getSampleRate() << std::endl;
    std::cout << "Audio card sample rate: " << sampleRate_ << std::endl;
    std::cout << "Samples per block: " << estimatedSamplesPerBlock << std::endl;

    numSamplesExpected = (int) (getSampleRate()/sampleRate_*float(estimatedSamplesPerBlock)); 
    // processor sample rate divided by sound card sample rate

    overflowBufferStartIndex = 0;
    overflowBufferEndIndex = 0;
    overflowSamples = 0;
}

void AudioNode::process(AudioSampleBuffer& buffer,
                        MidiBuffer& midiMessages,
                        int& nSamples)
{
    float gain;
    //std::cout << "Audio node sample count: " << nSamples << std::endl; ///buffer.getNumSamples() << std::endl;

    // clear the left and right channels
    buffer.clear(0,0,buffer.getNumSamples());
    buffer.clear(1,0,buffer.getNumSamples());

    if (overflowSamples > 1024)
    {
        std::cout << "Clearing overflow buffer!" << std::endl;
        // just forget about it
        overflowBuffer.clear();
        overflowSamples = 0;
    }

    int totalAvailableSamples = nSamples + overflowSamples;
    int leftoverSamples = 0;
    int samplesFromOverflowBuffer = 0;
    int remainingSamples = 0;

   // jassert (sourceStartSample >= 0 && sourceStartSample + numSamples <= source.size);

    if (channelPointers.size() > 0)
    {

        // 2. now copy from the incoming buffer

        bool copiedBuffer = false;

        for (int i = 2; i < buffer.getNumChannels(); i++)
        {

            if (channelPointers[i-2]->isMonitored)
            {

                if (!copiedBuffer)
                {
                    // 1. copy overflow buffer

                    samplesFromOverflowBuffer = ((overflowSamples <= numSamplesExpected) ? 
                                              overflowSamples :
                                              numSamplesExpected);

                    std::cout << " " << std::endl;
                    std::cout << "Copying " << samplesFromOverflowBuffer << " samples from overflow buffer." << std::endl;

                    if (samplesFromOverflowBuffer > 0)
                    {

                        buffer.addFrom(0,   // destination channel
                           0,               // destination start sample
                           overflowBuffer,  // source
                           0,               // source channel
                           0,               // source start sample
                           samplesFromOverflowBuffer, //  number of samples
                           1.0f             // gain to apply
                          );

                        buffer.addFrom(1,       // destination channel
                           0,           // destination start sample
                           overflowBuffer,      // source
                           1,           // source channel
                           0,           // source start sample
                           samplesFromOverflowBuffer, //  number of samples
                           1.0f       // gain to apply
                          );

                        overflowBuffer.clear(0, samplesFromOverflowBuffer);

                        std::cout << "Samples remaining in overflow buffer: " << overflowSamples - samplesFromOverflowBuffer << std::endl;


                        if (samplesFromOverflowBuffer < overflowSamples)
                        {

                            // move remaining samples to the front of the buffer
                            // have to double-copy because copying from a buffer into itself is not allowed

                             overflowOverflowBuffer.addFrom(0,   // destination channel
                               0,               // destination start sample
                               overflowBuffer,  // source
                               0,               // source channel
                               samplesFromOverflowBuffer,               // source start sample
                               overflowSamples - samplesFromOverflowBuffer,   //  number of samples
                               1.0f             // gain to apply
                              );

                            overflowOverflowBuffer.addFrom(1,   // destination channel
                               0,               // destination start sample
                               overflowBuffer,  // source
                               1,               // source channel
                               samplesFromOverflowBuffer,               // source start sample
                               overflowSamples - samplesFromOverflowBuffer,   //  number of samples
                               1.0f             // gain to apply
                              );

                            overflowBuffer.addFrom(0,   // destination channel
                               0,               // destination start sample
                               overflowOverflowBuffer,  // source
                               0,               // source channel
                               0,               // source start sample
                               overflowSamples - samplesFromOverflowBuffer,   //  number of samples
                               1.0f             // gain to apply
                              );

                            overflowBuffer.addFrom(1,   // destination channel
                               0,               // destination start sample
                               overflowOverflowBuffer,  // source
                               1,               // source channel
                               0,               // source start sample
                               overflowSamples - samplesFromOverflowBuffer,   //  number of samples
                               1.0f             // gain to apply
                              );

                            overflowOverflowBuffer.clear();

                        }

                        overflowSamples = overflowSamples - samplesFromOverflowBuffer;

                    }

                    int remainingSamples = numSamplesExpected - samplesFromOverflowBuffer;

                    std::cout << "Copying " << remainingSamples << " samples from incoming buffer of " << nSamples << " samples" << std::endl;

                    leftoverSamples = nSamples - remainingSamples;

                    if (leftoverSamples < 0)
                    {
                        leftoverSamples = nSamples;
                    }

                    jassert (leftoverSamples >= 0);

                    std::cout << "Samples remaining in incoming buffer: " << leftoverSamples << std::endl;

                    copiedBuffer = true;
                }

                gain = volume/(float(0x7fff) * channelPointers[i-2]->bitVolts);

                if (remainingSamples > 0)
                {

                    buffer.addFrom(0,       // destination channel
                               samplesFromOverflowBuffer,           // destination start sample
                               buffer,      // source
                               i,           // source channel
                               0,           // source start sample
                               remainingSamples, //  number of samples
                               gain       // gain to apply
                              );

                    buffer.addFrom(1,       // destination channel
                               samplesFromOverflowBuffer,           // destination start sample
                               buffer,      // source
                               i,           // source channel
                               0,           // source start sample
                               remainingSamples, //  number of samples
                               gain       // gain to apply
                              );
                }

                if (leftoverSamples > 0)
                {
                      overflowBuffer.addFrom(0,       // destination channel
                               overflowSamples,           // destination start sample
                               buffer,      // source
                               i,           // source channel
                               remainingSamples,           // source start sample
                               leftoverSamples, //  number of samples
                               gain       // gain to apply
                              );

                    overflowBuffer.addFrom(0,       // destination channel
                               overflowSamples,           // destination start sample
                               buffer,      // source
                               i,           // source channel
                               remainingSamples,           // source start sample
                               leftoverSamples, //  number of samples
                               gain       // gain to apply
                              );

                }

            }
        }



        overflowSamples += leftoverSamples;

    }

    nSamples = numSamplesExpected;

}