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

    This file was auto-generated!

    It contains the basic startup code for a Juce application.

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

#ifndef __PLUGINPROCESSOR_H_20F201CA__
#define __PLUGINPROCESSOR_H_20F201CA__

#include <JuceHeader.h>
#include <synth/synth_defs.h>
#include "JaySynthSound.h"
#include "JaySynth.h"
#include "JK_MidiClock.h"
#include <pluginterfaces/vst2.x/vstfxstore.h>

//==============================================================================
/**
*/
class  JaySynthActionListener : public ActionBroadcaster
{
public:

	enum
	{
		JAYSYNTH_CHANGED_PARAM = 0,
		JAYSYNTH_CHANGED_MIDICC,
		JAYSYNTH_CHANGED_NOTE_PRESSED,
		JAYSYNTH_CHANGED_NOTE_RELEASED,
		JAYSYNTH_CHANGED_NUM_VOICES_PLAYING,
		JAYSYNTH_CHANGED_MIDICLOCK,
		JAYSYNTH_CHANGED_PROGRAM,
		JAYSYNTH_CHANGED_LIMITER,
		JAYSYNTH_CHANGED_SIZE
	};

	void callParamChanged (int paramID)
	{
		String s;
		s = String("type=") + String(JAYSYNTH_CHANGED_PARAM);
		s = s + String(" ");
		s = s + String("paramID=") + String(paramID);
		s = s + String(" ");
		sendActionMessage(s);
	}

	void callMidiControllerChanged (int channel, int controllerID, float value)
	{
		String s;
		s = String("type=") + String(JAYSYNTH_CHANGED_MIDICC);
		s = s + String(" ");
		s = s + String("ch=") + String(channel);
		s = s + String(" ");
		s = s + String("id=") + String(controllerID);
		s = s + String(" ");
		s = s + String("value=") + String((float)value);
		s = s + String(" ");
		sendActionMessage(s);
	}

	void callMidiNotePressed (int midi_note, float velocity)
	{
		String s;
		s = String("type=") + String(JAYSYNTH_CHANGED_NOTE_PRESSED);
		s = s + String(" ");
		s = s + String("note=") + String(midi_note);
		s = s + String(" ");
		s = s + String("velocity=") + String((float)velocity);
		s = s + String(" ");
		sendActionMessage(s);
	}

	void callMidiNoteReleased (int midi_note, float velocity)
	{
		String s;
		s = String("type=") + String(JAYSYNTH_CHANGED_NOTE_RELEASED);
		s = s + String(" ");
		s = s + String("note=") + String(midi_note);
		s = s + String(" ");
		s = s + String("velocity=") + String((float)velocity);
		s = s + String(" ");
		sendActionMessage(s);
	}

	void callProgramChanged(void)
	{
		String s;
		s = String("type=") + String(JAYSYNTH_CHANGED_PROGRAM);
		s = s + String(" ");
		sendActionMessage(s);
	}

	void callLimiterChanged (int value)
	{
		String s;
		s = String("type=") + String(JAYSYNTH_CHANGED_LIMITER);
		s = s + String(" ");
		s = s + String("value=") + String(value);
		s = s + String(" ");
		sendActionMessage(s);
	}

	void callNumVoicesPlaying (int value)
	{
		String s;
		s = String("type=") + String(JAYSYNTH_CHANGED_NUM_VOICES_PLAYING);
		s = s + String(" ");
		s = s + String("value=") + String(value);
		s = s + String(" ");
		sendActionMessage(s);
	}

	void callMidiClock (uint32_t bpm, uint32_t type)
	{
		String s;
		s = String("type=") + String(JAYSYNTH_CHANGED_MIDICLOCK);
		s = s + String(" ");
		s = s + String("bpm=") + String(bpm);
		s = s + String(" ");
		s = s + String("brightness=") + String(type);
		s = s + String(" ");
		sendActionMessage(s);
	}

	void callBinary(void *pData, int size)
	{
		String s;
		s.createStringFromData(pData, size);
		sendActionMessage(s);
	}

	void add (ActionListener *listener)
	{
		addActionListener((ActionListener*)listener);
	}
	void remove (ActionListener *listener)
	{
		removeActionListener((ActionListener*)listener);
	}

private:
};

class JaySynthAudioProcessor :
	public Timer,
	public AudioProcessor, 
	public JaySynthSound,
	public JaySynthListener
{
public:
    //==============================================================================
    JaySynthAudioProcessor();
    ~JaySynthAudioProcessor();

    //==============================================================================
    void prepareToPlay (double sampleRate, int samplesPerBlock);
    void releaseResources();

    void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages);

    //==============================================================================
    AudioProcessorEditor* createEditor();
    bool hasEditor() const;
    //==============================================================================
    const String getName() const;

    int getNumParameters();

    void setParameter (int index, float newValue);
    float getParameter (int index);
	void setParam(int index, synth_float_t param, bool doModifyPatch, bool doNotifyHost);
	synth_float_t getParam(int index);
	void controlParam(int index, synth_float_t newValue);

    const String getParameterName (int index);
    const String getParameterText (int index);

    const String getInputChannelName (int channelIndex) const;
    const String getOutputChannelName (int channelIndex) const;
    bool isInputChannelStereoPair (int index) const;
    bool isOutputChannelStereoPair (int index) const;
	bool silenceInProducesSilenceOut() const
	{
		return false;
	}
	double getTailLengthSeconds() const
	{
		return 0;
	}
	
    bool acceptsMidi() const;
    bool producesMidi() const;

    //==============================================================================
    int getNumPrograms();
    int getCurrentProgram();
    void setCurrentProgram (int index);
    const String getProgramName (int index);
    void changeProgramName (int index, const String& newName);

    //==============================================================================
	void copyXmlToBinary (const XmlElement& xml, juce::MemoryBlock& destData);
	XmlElement* getXmlFromBinary (const void* data, const int sizeInBytes);
	const String getParameterNameXML (int index);

	// Patch encode
	void patchEncodeXml(XmlElement *xml, JaySynthSound const *patch);

	// Bank encode
	void bankEncodeXml(XmlElement *xml);

	// Patch decode
	void patchDecodeXml(const XmlElement *xml, JaySynthSound *patch);
	void patchDecodeXml_legacy(const XmlElement& xml, JaySynthSound *patch);

	// Bank decode
	void bankDecodeXml(const XmlElement *xml);
	void bankDecodeXml_legacy(const XmlElement& xml);

	// Patch import, main entry
	void patchImportXml (const XmlElement *xml, JaySynthSound *patch);
	
	// Bank import, main entry
	void bankImportXml (const XmlElement *xml);

	// Patch import VST 2.4
    void setCurrentProgramStateInformation (const void* data, int sizeInBytes);

	// Bank import VST 2.4
    void setStateInformation (const void* data, int sizeInBytes);

	// Patch export VST 2.4
    void getCurrentProgramStateInformation (MemoryBlock& destData);

	// Bank export VST 2.4
    void getStateInformation (MemoryBlock& destData);

	// Patch load from file
	void loadPatchFromFile (const File &file, int index);

	// Bank load from file
	void loadBankFromFile (const File &file);

	// Patch save to file
	void savePatchToFile (const File &file, int index);

	// Bank save to file
	void saveBankToFile (const File &file);

	void commitEditBuffer();
    //==============================================================================
    // These properties are public so that our editor component can access them
    // A bit of a hacky way to do it, but it's only a demo! Obviously in your own
    // code you'll do this much more neatly..

    // this is kept up to date with the midi messages that arrive, and the UI component
    // registers with it so it can represent the incoming messages
    MidiKeyboardState keyboardState;

    // this keeps a copy of the last set of time info that was acquired during an audio
    // callback - the UI component will read this and display it.
    AudioPlayHead::CurrentPositionInfo lastPosInfo;

    // these are used to persist the UI's size - the values are stored along with the
    // filter's other parameters, and the UI component will update them when it gets
    // resized.
	void synthChanged(int type, void *pData);

	void addActionListener(ActionListener* listener)
	{
		actionListener.add(listener);
	}

	void removeActionListener(ActionListener* listener)
	{
		actionListener.remove(listener);
	}

	void sendParamChangeToHost(int index, float value)
	{
		AudioProcessor::sendParamChangeMessageToListeners(index, value);
	}

	JaySynth::midiCC_info_t* getLastMidiControllers(void)
	{
		return pSynth->getLastMidiCC_infos();
	}

	JaySynth::midi_note_info_t* getLastMidiNoteInfos(void)
	{
		return pSynth->getLastMidiNote_infos();
	}

	void setCurrentProgramName (String name)
	{
		changeProgramName(getCurrentProgram(), name);
		updateHostDisplay();
	}

	const String getCurrentProgramName (void)
	{
		String s;

		if (isPatchModified() || getMidiCC_editBuffer()->isMidiCCmodified())
		{
			s = getProgramName(getCurrentProgram()) + String(" [edited]");
		}
		else
		{
			s = getProgramName(getCurrentProgram());
		}
	
		return s;
	};

	bool isPatchModified(void)
	{
		return isModified;
	}

	void clearPatchModified(void)
	{
		isModified = false;
	}

	void setPatchModified(void)
	{
		isModified = true;
	}

	JaySynthMidiCC *getMidiCC_editBuffer(void)
	{
		return &midCC_editBuffer;
	}

	void applyMidiController(JaySynth::midiCC_info_t *pMidiCC_info, bool forceApply);
	void applyCurrentMidiControllers(void);

	void humanizeModeChanged(void)
	{
		pSynth->humanizeModeChanged();
	}

	void unisonoModeChanged(void)
	{
		pSynth->unisonoModeChanged();
	}

	void humanizeVarianceChanged_VCO(void)
	{
		pSynth->humanizeVarianceChanged_VCO();
	}

	void humanizeVarianceChanged_VCF(void)
	{
		pSynth->humanizeVarianceChanged_VCF();
	}

	void humanizeVarianceChanged_ENV(void)
	{
		pSynth->humanizeVarianceChanged_ENV();
	}

	void perVoiceControlChanged(int paramID)
	{
		pSynth->updateParameter(paramID);
	}

	void perVoiceControlsChanged(void)
	{
		pSynth->updateParameters();
	}

	String wavesGetPath();
	bool isWavesAvailable();

	void timerCallback();

private:
    // the synth!
	bool isModified;
	bool m_isWavesAvailable;
    int lastUIWidth, lastUIHeight;
	synth_float_t master_volume, m_fs;
	int currProgram;
	JaySynthSound patches[NUM_PROGRAMS];
	JaySynthSound *currpatch;
	JaySynthActionListener actionListener;
    JaySynth *pSynth;
	void *the_editor;
	JaySynthMidiCC midCC_editBuffer;
	int m_limiter_active;
	synth_float_t m_limiter_env;
	JK_MidiClock m_JK_MidiClock;
	
    //==============================================================================
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JaySynthAudioProcessor);
};

//typedef JaySynthAudioProcessor::Listener JaySynthAudioProcessorListener;


#endif  // __PLUGINPROCESSOR_H_20F201CA__
