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

    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 <stdio.h>
#include "PhaseDetector.h"
#include "Editors/PhaseDetectorEditor.h"

PhaseDetector::PhaseDetector()
    : GenericProcessor("Phase Detector"), activeModule(-1),
      risingPos(false), risingNeg(false), fallingPos(false), fallingNeg(false)

{

}

PhaseDetector::~PhaseDetector()
{

}

AudioProcessorEditor* PhaseDetector::createEditor()
{
    editor = new PhaseDetectorEditor(this, true);

    std::cout << "Creating editor." << std::endl;

    return editor;
}

void PhaseDetector::addModule()
{
    DetectorModule m = DetectorModule();
    m.inputChan = -1;
    m.outputChan = -1;
    m.gateChan = -1;
    m.isActive = true;
    m.lastSample = 0.0f;
    m.type = NONE;
    m.samplesSinceTrigger = 5000;
    m.wasTriggered = false;
    m.phase = NO_PHASE;

    modules.add(m);
}

void PhaseDetector::setActiveModule(int i)
{
    activeModule = i;

}


void PhaseDetector::setParameter(int parameterIndex, float newValue)
{

    DetectorModule& module = modules.getReference(activeModule);

    if (parameterIndex == 1) // module type
    {

        int val = (int) newValue;

        switch (val)
        {
            case 0:
                module.type = NONE;
                break;
            case 1:
                module.type = PEAK;
                break;
            case 2:
                module.type = FALLING_ZERO;
                break;
            case 3:
                module.type = TROUGH;
                break;
            case 4:
                module.type = RISING_ZERO;
                break;
            default:
                module.type = NONE;
        }
    }
    else if (parameterIndex == 2)   // inputChan
    {
        module.inputChan = (int) newValue;
    }
    else if (parameterIndex == 3)   // outputChan
    {
        module.outputChan = (int) newValue;
    }
    else if (parameterIndex == 4)   // gateChan
    {
        module.gateChan = (int) newValue;
        if (module.gateChan < 0)
        {
            module.isActive = true;
        }
        else
        {
            module.isActive = false;
        }
    }

}

void PhaseDetector::updateSettings()
{

}

bool PhaseDetector::enable()
{
    return true;
}

void PhaseDetector::handleEvent(int eventType, MidiMessage& event, int sampleNum)
{
    // MOVED GATING TO PULSE PAL OUTPUT!
    // now use to randomize phase for next trial

    //std::cout << "GOT EVENT." << std::endl;

    if (eventType == TTL)
    {
        const uint8* dataptr = event.getRawData();

        // int eventNodeId = *(dataptr+1);
        int eventId = *(dataptr+2);
        int eventChannel = *(dataptr+3);

        for (int i = 0; i < modules.size(); i++)
        {
            DetectorModule& module = modules.getReference(i);

            if (module.gateChan == eventChannel)
            {
                if (eventId)
                    module.isActive = true;
                else
                    module.isActive = false;
            }
        }

    }

}

void PhaseDetector::process(AudioSampleBuffer& buffer,
                            MidiBuffer& events,
                            int& nSamples)
{

    checkForEvents(events);

    // loop through the modules
    for (int i = 0; i < modules.size(); i++)
    {
        DetectorModule& module = modules.getReference(i);

        // check to see if it's active and has a channel
        if (module.isActive && module.outputChan >= 0 &&
            module.inputChan >= 0 &&
            module.inputChan < buffer.getNumChannels())
        {

            for (int i = 0; i < nSamples; i++)
            {
                const float sample = *buffer.getReadPointer(module.inputChan, i);

                if (sample < module.lastSample && sample > 0 && module.phase != FALLING_POS)
                {

                    if (module.type == PEAK)
                    {
                        addEvent(events, TTL, i, 1, module.outputChan);
                        module.samplesSinceTrigger = 0;
                        module.wasTriggered = true;
                    }

                    module.phase = FALLING_POS;

                }
                else if (sample < 0 && module.lastSample >= 0 && module.phase != FALLING_NEG)
                {

                    if (module.type == FALLING_ZERO)
                    {
                        addEvent(events, TTL, i, 1, module.outputChan);
                        module.samplesSinceTrigger = 0;
                        module.wasTriggered = true;
                    }

                    module.phase = FALLING_NEG;

                }
                else if (sample > module.lastSample && sample < 0 && module.phase != RISING_NEG)
                {

                    if (module.type == TROUGH)
                    {
                        addEvent(events, TTL, i, 1, module.outputChan);
                        module.samplesSinceTrigger = 0;
                        module.wasTriggered = true;
                    }

                    module.phase = RISING_NEG;

                }
                else if (sample > 0 && module.lastSample <= 0 && module.phase != RISING_POS)
                {

                    if (module.type == RISING_ZERO)
                    {
                        addEvent(events, TTL, i, 1, module.outputChan);
                        module.samplesSinceTrigger = 0;
                        module.wasTriggered = true;
                    }

                    module.phase = RISING_POS;

                }

                module.lastSample = sample;

                if (module.wasTriggered)
                {
                    if (module.samplesSinceTrigger > 1000)
                    {
                        addEvent(events, TTL, i, 0, module.outputChan);
                        module.wasTriggered = false;
                    }
                    else
                    {
                        module.samplesSinceTrigger++;
                    }
                }

            }


        }

    }

}

void PhaseDetector::estimateFrequency()
{

    // int N = (numPeakIntervals < NUM_INTERVALS) ? numPeakIntervals
    //         : NUM_INTERVALS;

    // int sum = 0;

    // for (int i = 0; i < N; i++)
    // {
    //     sum += peakIntervals[i];
    // }

    // estimatedFrequency = getSampleRate()/(float(sum)/float(N));


}