/*
  ==============================================================================

    This file was generated by user!

  ==============================================================================
*/

#ifndef _JAYSYNTH_H_
#define _JAYSYNTH_H_

#include <synth/synth_defs.h>
#include <synth/param_scale.h>

#include <JuceHeader.h>
#include "JaySynthVoice.h"
#include "JaySynthMonophonicMGR.h"
#include "JaySynthMidiCC.h"
#include "MidiNrpn.h"

#define GET_NUM_VOICES	max_num_voices

class JaySynthListener
{
public:
	virtual void synthChanged(int /*type*/, void* /*pData*/) {};
};

class JaySynthThread : public Thread
{
public:
    JaySynthThread(WaitableEvent *pEventRdy, voice_t *pVoices, int num_voices, int offset, int step);
	~JaySynthThread();
	void go(int len);
	AudioSampleBuffer& getBuffer(void);
	void run(void);
private:
	ScopedPointer<AudioSampleBuffer> m_buffer;
	voice_t *m_pVoices;
	int m_num_voices;
	int m_voice_offset, m_voice_step;
	int m_sample_len;
	WaitableEvent *m_pEventRdy;

};

class JaySynth : public Synthesiser
{
public:
	enum
	{
		SYNTH_PER_VOICE_CONTROL_VELKEY = 0,
		SYNTH_PER_VOICE_CONTROL_SIZE
	};

	enum
	{
		SYNTH_CHANGED_PARAM = 0,
		SYNTH_CHANGED_MIDICC,
		SYNTH_CHANGED_NUM_VOICES_PLAYING,
		SYNTH_CHANGED_NOTE_PRESSED,
		SYNTH_CHANGED_NOTE_RELEASED,
		SYNTH_CHANGED_MIDICLOCK,
		SYNTH_CHANGED_SIZE
	};

	enum
	{
		MIDI_CONTROLLER_TYPE_CC = 0,
		MIDI_CONTROLLER_TYPE_NRPN,
		MIDI_CONTROLLER_TYPE_RPN,
		MIDI_CONTROLLER_TYPE_SIZE
	};
	
	typedef struct _smidiCC_info_t
	{
		int type;
		int channel;
		int ID;
		int value_raw;
		synth_float_t value;
	} midiCC_info_t;

	typedef struct _smidi_note_info_t
	{
		int note;
		synth_float_t velocity;

	} midi_note_info_t;

	typedef struct _smidi_quarter_clock_info_t
	{
		uint32_t type;
		uint32_t bpm;
	} midi_quarter_clock_info_t;
	
	midiCC_info_t* getLastMidiCC_infos(void)
	{
		return last_midiCC_info;
	}

	midiCC_info_t* getLastMidiCC_info(int controllerID)
	{
		if (controllerID < NUM_MIDI_CONTROLLERS)
			return &last_midiCC_info[controllerID];

		return NULL;
	}

	midi_note_info_t* getLastMidiNote_infos()
	{
		return &last_midi_note_info;
	}

	void setMidiCC_editBuffer(JaySynthMidiCC *pMidiCC)
	{
		pMidCC_editBuffer = pMidiCC;
	}

	typedef struct _sper_voice_control_t
	{
		synth_float_t ctrl[SYNTH_PER_VOICE_CONTROL_SIZE][SYNTH_NUM_PARAMS];
	
	} per_voice_control_t;

	JaySynth(int num_voices, String pathToWaves);
	~JaySynth();
	void setSampleRate (synth_float_t sampleRate);
 	void setBufsize (int bufsize);
	void setParam(int paramID, int voice);
	void ClearControls(void);
	void setControl(int paramID, synth_float_t value);
	void setParameter(int paramID, synth_float_t value);
	void setPerVoiceControl(int voice, int channel, int paramID, synth_float_t param);
	synth_float_t getParameter(int paramID);
	synth_float_t getVolume(void);
	int lastPitchWheelValue, lastmidiChannel;
    void handlePitchWheel (int midiChannel, int wheelValue);
    void handleController (int midiChannel, int controllerNumber, int controllerValue) {}
	void handleController (const midiCC_info_t &midiCC_info);
	void updateParameters(void);
	void updateParameter(int paramID);
	void humanizeModeChanged(void);
	void humanizeVarianceChanged_VCO(void);
	void humanizeVarianceChanged_VCF(void);
	void humanizeVarianceChanged_ENV(void);
	void unisonoModeChanged(void);
	void humanizeParam (int paramID, bool useRandom, UINT32 random_seed, synth_float_t spread_min, synth_float_t variance);
	bool isUnison(void)
	{
		return m_isUnison;
	}
	int getUnisonNumberOfVoices(void)
	{
		return unison_num_voices;
	}

	// ========================================================================
	void advanceNextPossibleVoice(void);
	int getNextPossibleVoice(void);
	void advanceOldestVoice(void);
	int getOldestVoice(void);
	void Voicestart (JaySynthVoice* const voice,
                              SynthesiserSound* const sound,
                              const int midiChannel,
                              const int midiNoteNumber,
                              const float velocity, bool doTrigger);

	void Voicestop (JaySynthVoice* voice, const bool allowTailOff);
	synth_float_t voiceSetMidiBeat(int quarterBeatCount);
	int getNumVoicesPlaying (SynthesiserSound* soundToPlay);

	void clearKeysPressed(void);
	void setKeyPressed(int key, float vel);

	void noteOn (int midiChannel, int midiNoteNumber, float velocity);
	void noteOff (int midiChannel, int midiNoteNumber, bool allowTailOff);
    void allNotesOff (int midiChannel, bool allowTailOff);

	JaySynthVoice* getFreeVoice (SynthesiserSound* soundToPlay,
                                             const bool stealIfNoneAvailable);

    void handleMidiEvent (const MidiMessage& m);
    void handleSustainPedal (int midiChannel, bool isDown);
    void handleSostenutoPedal (int midiChannel, bool isDown);
    void handleSoftPedal (int midiChannel, bool isDown);

    void renderNextBlock (AudioSampleBuffer& outputAudio,
                          const MidiBuffer& inputMidi,
                          int startSample,
                          int numSamples);

    int getNumVoices() const
	{
		return max_num_voices;
	}

	JaySynthVoice* getVoice (const int index) const
	{
		const ScopedLock sl (lock);
		return voices [index];
	}

	void clearVoices()
	{
		const ScopedLock sl (lock);
		voices.clear();
	}

	void addVoice (JaySynthVoice* const newVoice)
	{
		const ScopedLock sl (lock);
		voices.add (newVoice);
	}

	void removeVoice (const int index)
	{
		const ScopedLock sl (lock);
		voices.remove (index);
	}

	// ========================================================================
	void addListener(JaySynthListener *listener)
	{
	    listeners.add (listener);
	}

	//==============================================================================

private:
	int m_num_audiothreads;
    BigInteger sustainPedalsDown;
	int paramID_changed, next_possible_voice, oldest_voice;
	JaySynthMidiCC *pMidCC_editBuffer;
	midiCC_info_t last_midiCC_info[NUM_MIDI_CONTROLLERS];
	midiCC_info_t midiCC_info;
	synth_float_t params[SYNTH_NUM_PARAMS];
	synth_float_t controls[SYNTH_NUM_PARAMS];
	per_voice_control_t *pPer_voice_controls;
	synth_float_t master_volume;
	synth_float_t humanize_var_vco;
	synth_float_t humanize_var_vcf;
	synth_float_t humanize_var_env;
	voice_common_t synth_common;
	param_info_t pb_range[VOICE_NUM_OSC];
	bool m_doVcoRestartOnTrigger;
	bool doHumanize, isHumanize;
	bool m_doMonophon, m_isMonophon;
	bool m_doUnison, m_isUnison;
	bool m_doMonophonRetrigger;
	int m_num_keys_pressed;
	int m_num_keys_pressed_last;
	int m_highest_key;
	int m_highest_key_last;
	int m_latest_key;
	int m_latest_key_last;
	int unison_num_voices;
	int max_num_voices, total_num_voices;
	param_info_t **humanize_voice_param;
	synth_float_t **ppHumanizedSliders;
	ListenerList <JaySynthListener> listeners;
    OwnedArray <JaySynthVoice> voices;
	synth_float_t m_unisonGain;
	midi_note_info_t *pCurrNoteInfos;
	midi_note_info_t last_midi_note_info;
	voice_t *m_pVoices;
	JaySynthThread **m_ppAudioThread;
	WaitableEvent **m_ppEventAudioThreadRdy;
	JaySynthMonophonicMGR m_monoMGR;
	const int m_numMidiclocksPerQuarterNote = 24;
	const int m_numMidiclocksPerFourQuarterBeats = 4*m_numMidiclocksPerQuarterNote;
	int m_clock_cnt_bar;
	int m_numMidiClocksPerBeat;
	int m_numMidiClocksPerBar;
	bool m_isMidiClockStarted;

	void updateMidiTiming(const MidiMessage& m);
	MidiNrpn mMidiNrpn;
};


#endif  // _JAYSYNTH_H_
