/*
 *  MInstrument.h
 *  MSE
 *
 *  Created by Hawk on 11/5/09.
 *  Copyright 2009 Maizesoft. All rights reserved.
 *
 */

#pragma once

#include "MGroup.h"
#include "MLFO.h"
#include "MReverb.h"

class MSE_API MSEThreadClient
{
public:
	MSEThreadClient(){};
	virtual void ThreadProc(){};
};

class MSE_API MSEDelegate
{
public:
	MSEDelegate(){};
	virtual bool LoadingProgress(float progress, std::string& group, std::string& sample){ return true; }	//retrun false if user canceled
	virtual bool SavingProgress(float progress, std::string& group, std::string& sample){ return true; }	//retrun false if user canceled
	virtual void GroupSelectionChanged(){};
	virtual void WhereIsSample(std::string& wrongname, char* outName) {}	//pass std::string across dll will cause heap corruption, so use char* instead
	virtual void InstrumentParameterChanged(){};
	
	virtual void EncodeSN(std::string& SN, char* outEncodedSN) {}
	virtual void AskForEncodedSN(char* outEncodedSN) {}
};

struct MSE_InstrumentProperty
{
	//pending property will be flushed into real property on exit save
	int bufferSize, bufferSizePending;
	int fixedPoint, fixedPointPending;
	int diskStreaming, diskStreamingPending;
	int polyphony, polyphonyPending;
	
	MSE_ResampleQuality resampleQuality;
	MSE_VelocityCurve velocityCurve;
	
	MSE_VoiceMode voiceMode;
	float glideTime;
	
	float gain;			//dB
	float pan;			// -1 - 0 - 1

	MSE_LFOWaveform lfoWaveform;
	MSE_LFODestination lfoDest;
	MSE_LFODepthSource lfoDepthSource;
	float lfoRate;
	float lfoDepth;

	float reverb;

	int amplitudeRangeLow, amplitudeRangeHigh;
	int expireDays;		//implemented in MSP
	int reserved[125];
};

class MSE_API MInstrument: public MSEThreadClient
{
public:
	MInstrument();
	~MInstrument();

	void NewInstrument();
	void ClearInstrument();	//clear everything in the instrument and create a empty group in it

	void CreatePlayerSkinBuffer(int size);	//memory has to be allocated inside MSE dll, otherwise it will corrupt
	void SetPlayerSkinName(std::string& name);
	
	//properties
	MSE_InstrumentProperty f_Property;
	std::string f_InstrumentName;
	std::string f_VendorName;
	int f_PlayerSkinSize;
	MSE_Byte* f_PlayerSkin;
	std::string f_PlayerSkinName;	//just for display
	std::string f_PlayerLayoutXML;
	void SetPlayerLayoutXML(std::string& xml);
	
	//interleaved 32bit float audio
	void ProcessAudio(float** output, int numOfOutput, int frames, bool realtime = true);

	//disk thread
	void ThreadProc();
	
	//MIDI
	void AddMIDIMessage(MSE_Byte cmd, MSE_Byte ch, MSE_Byte data1, MSE_Byte data2, int sampleOffset = -1);
	void AddMIDIMessage(unsigned char* rawData, int sampleOffset = -1);
	void ProcessMIDI(int frames, bool lastframe = false);
	void AllNotesOff(bool exceptHold = false, bool fastrelease = false);
	void AllNotesKill();
	
	//groups
	MGroup* CreateGroup(std::string& name);
    void MoveGroup(int from, int to);
	void RemoveGroup(MGroup* pGroup);
	void RemoveAllGroups();
	int GetGroupCount();
	MGroup* GetGroup(int index);
	void SelectGroup(MGroup* group);
	void DeselectGroup(MGroup* group);
	void DeselectAllGroups();
	bool IsGroupSelected(MGroup* group);
	MGroup* GetCurrentClientGroup();
	int GetCurrentClientGroupIndex();
	void SetCurrentClientGroup(int index);

	//SN
	void AddSerialNumber(std::string& sn);
	void RemoveAllSerialNumbers();
	void RemoveSerialNumber(int index);
	int GetSerialNumberCount();
	void GetSerialNumber(int index, char* sn);
	
	//UI Client
	void SetUIDelegate(MSEDelegate* delegate);
	MSEDelegate* GetUIDelegate();
	
	//project file
	void Save(std::string& filename, bool embedSamples = false);
	bool Load(std::string& filename, std::string& encodedSN, bool allowEmbedded = false);
	bool LoadEXS(std::string& filename);
	void IsOurInstrument(std::string& filename, std::string& returnStr, bool returnVendor = true);		//return instrument name if true
	
	//Runtime Parameters
	void UpdateProperty();
	float GetPitchBendFactor();
    void SetPitchBend(float percent);
    float GetPitchBend();
	bool PitchBending();
	float GetLFOValue();
	void SetBypass(bool bypass);
	void SetSampleRate(float sr);
	float GetSampleRate();
	int GetVersion();
	int GetLastNote();
	int GetTotalSampleCount();
	void UpdateGroupSelection(MGroup* group);
	
	//sample creation, client's responsibility to add to group
	MSample* CreateSample(std::string& filename, MSE_Rect range, MSE_Byte rootNote);

	//garbage collection
	void CloseFilesIfNeeded();
	
protected:
	std::vector<MGroup*> m_GroupList;
	std::vector<MVoice*> m_VoiceList;
	std::vector<MSE_MIDI> m_MIDIList, m_MIDIListCopy;
	std::vector<MGroup*> m_SelectedGroups;
	std::vector<std::string> f_SerialNumber;

	bool IsSerialNumberValid(std::string& serialNumber);
	
	//Voices
	void CreateVoices();
	void FreeVoices();
	MVoice** m_VoiceListCache;	//vector[] seems to be very slow

	void FlushPendingProperty();
	void DefaultProperty();
	
	//MIDI
	void SwitchGroup(MSE_MIDI msg);
	MVoice* FindAvailableVoice();
	MVoice* FindNoteOffVoice(MSE_Byte note, MSE_Byte ch);
	void ReleaseChokeVoices(int chokecluster);
	void NoteOn(MGroup* targetGroup, MSE_MIDI msg);
	void ResetControllers();

	//for release trigger velocity
	unsigned char m_VelocityMemory[128];
	double m_NoteOnTimestamp[128];

	//for group selection update (controller and program change)
	unsigned char m_ControllerMemory[128];
	unsigned char m_ProgramMemory;
	
	
	int m_CurrentMIDI;
	MGroup* m_CurrentClientGroup;	//for GUI client use only
	bool m_Bypass;
	bool m_Sustained;
	double m_ProcessTimeStamp;	//the time a new process block starts
	int m_LastNote;		//for glide
	
	MSEDelegate* m_UIDelegate;
	
	//LFO & Filters
	MLFO m_LFO;
	float m_LFOValue;
	MReverb m_Reverb;
	
	//parameters
	float m_PitchBendFactor, m_PitchBendTarget;
	bool m_PitchBending;
    float m_PitchBendPercent;
	int m_PitchBendRange;	//in semitones
	float m_LeftPanCoff, m_RightPanCoff, m_Gain, m_GainTarget;
    float m_SampleRate;
};