////////////////////////////////////////////////////////////////////////////////////////
// ADCTester license
// This code was created in 2017 for the Library of Congress
// and the other federal government agencies participating in the
// Federal Agencies Digitization Guidelines Initiative and it is in the public domain.
////////////////////////////////////////////////////////////////////////////////////////

#ifndef AUDIOENGINE_H
#define AUDIOENGINE_H

#include "wx/wx.h"
#include <vector>
#include "portaudio.h"
#include <wx/thread.h>
#include <memory>
#include <malloc.h>

#include "pablio/pablio.h"
#include "LevelAnalyser.h"
#include "ParametersQueue.h"
#include "ProcessParams.h"

#include "../DSP/FFTAnalyser.h"
#include "../TestManager.h"

#define safenew new

class AVPTesterFrame;
class AudioIO;
class AudioThread;
class AudioTestThread;

typedef struct AudioThreadEvent {
    int  processID;
    int  eventID;
    int  eventRange;
    int  eventCounter;
    wxString eventMessage;
    wxString debugInfo;
    bool threadFinished;
}AudioThreadEvent;

enum eventcode
{
    AVP_PROCESS_FILE_ERROR = -4,
    AVP_PROCESS_CONFIG_ERROR,
    AVP_PROCESS_AUDIO_ERROR,
    AVP_PROCESS_TERM_FAIL,
    AVP_PROCESS_TERM_OK,
    AVP_PROCESS_START,
    AVP_PROCESS_STAGE,
    AVP_PROCESS_RESULT
};

extern AudioIO *gAudioIO;


void InitAudioIO();
void DeinitAudioIO();

class AudioIO
{
    public:
        AudioIO();
        virtual ~AudioIO();

        void SetParent(AVPTesterFrame* parent);

        void StartDevicesCalibration();
        void StopDevicesCalibration();
        int  doIODevicesCalibration();

        TestManager* GetTestManager() { return mTestManager;  }
        void StartTestProcedure( int testIndex = -1 );
        void StopTestProcedure();
        int  doADCTest();
        int  performADCTestUnit(int testIndex);

        void reportEvent(int processID,
                         int eventID,
                         wxString message,
                         bool killProcess = false,
                         wxString debugInfo = wxEmptyString,
                         int eRange = 0,
                         int eCount = 0);

        std::unique_ptr<AudioThread> mThread;
        std::unique_ptr<AudioTestThread> mADCTestThread;

        LevelAnalyser* GetInputsLevelAnalyser();
        LevelAnalyser* GetOutputsLevelAnalyser();
        FFTPlotData GetFFTPlotData(bool* newdata);

        void SetParameter(AudioParam msg, bool flushQueue =  false);
        double GetMasterSampleRate() { return mCaptureSampleRate; }

        void LoadCalibrationSettings();
        virtual std::vector<AudioParam> getCalibrationParameters();

    protected:

        int CreateLevelAnalysers(size_t srate, size_t interval);
        void DeleteLevelAnalysers();

        ////////////////////////////////////////////////////////////////////////
        //ADC Test procedure
        //Loads chosen I/O configuration
        bool GetCurrentIOConfiguration( double& sampleRate, //test sample rate
                                        int& captureDeviceIdx, //input device (ADC-DUT) PortAudio Index
                                        int& captureChannels,
                                        int& playbackDeviceIdx,//Output device (DAC) PortAudio Index
                                        int& playbackChannels);

        //opens selected I/O devices
        PaError OpenDevices(double sampleRate,
                            int captureDeviceIdx,
                            int captureChannels,
                            int playbackDeviceIdx,
                            int playbackChannels);

        PaError CloseDevices();

        //experimental
        PaError OpenDevicesDualStream(double sampleRate,
                            int captureDeviceIdx,
                            int captureChannels,
                            int playbackDeviceIdx,
                            int playbackChannels);

        PaError CloseDevicesDualStream();

        //Plays test signal and records response
        int PlaybackAcquire(wxString signalFile, wxString responseFile);

        void FlushParameterQueue();
        void ProcessParameter( AudioParam param);

        AVPTesterFrame* mParent;

        int mSelectedTestIndex;
        int mTotalNoTests;

        size_t mCaptureFrameSize;
        double mCaptureSampleRate;
        int mNoPlaybackChannels;
        int mNoCaptureChannels;
        PaStream *mPortStreamV19_Main;
        PaStream *mPortStreamV19_Aux;
        size_t mCaptureSleep;
        bool mIsDualStreams;

        bool bPAIsOpen;
        bool bIsStopped;
        volatile bool mIsSafe;
        bool mEngineOK;

        LevelAnalyser* mInputLevelMetric;
        LevelAnalyser* mOutputLevelMetric;
        FFTAnalyser* mFFTrta;
        size_t mSTFTLength;

        ParametersQueue mParametersQueue;
        std::vector<AudioParam> mCalibrationParameters;

        double mToneLevel;
        double mOutputGain;
        int mSelectedChannel;

        TestManager* mTestManager;

    private:
        int getInputDevIndex(const wxString &hostName = wxEmptyString, const wxString &devName = wxEmptyString);
        int getOutputDevIndex(const wxString &hostName = wxEmptyString, const wxString &devName = wxEmptyString);
};

#endif // AUDIOENGINE_H
