#if !defined(_UTILS_H_INCLUDED_)
#define _UTILS_H_INCLUDED_


#include "../pfc/pfc.h"


class NOVTABLE CStream
{
public:
	virtual UINT ReadData(void*,UINT,bool*)=0;
	virtual void Flush()=0;
	virtual ~CStream() {};

	//for sampling
	virtual void Pause(int) {};
	virtual void Eof() {}
};

class CPipe : public CStream
{
	BYTE* buf;
	volatile UINT buf_s,buf_n,buf_rp,buf_wp;
	critical_section sec;
	UINT align;
	volatile bool closed;
public:	
	void WriteData(void*,UINT);
	UINT CanWrite() {return buf_s-buf_n;}
	UINT ReadData(void*,UINT,bool*);
	void Flush()
	{
		sec.enter();
		buf_n=0;
		sec.leave();

	}
	CPipe(UINT _align=4,UINT freq=44100)
	{
		buf_s=MulDiv(1024*256,freq,22050);
		buf=(BYTE*)malloc(buf_s);
		buf_wp=buf_rp=0;
		buf_n=0;
		align=_align;
		closed=0;
	}
	~CPipe()
	{
		if (buf) free(buf);
	}
	void Eof() {closed=1;}
};


class MIDI_file;

#pragma pack(push)
#pragma pack(1)
typedef struct
{
	WORD fmt,trax,dtx;
} MIDIHEADER;
#pragma pack(pop)

WORD _inline rev16(WORD x) {return (x>>8)|(x<<8);}
//#define rev16(X) (((X)&0xFF)<<8)|(((X)>>8)&0xFF)
DWORD _fastcall rev32(DWORD);

#define _rv(X) ((((DWORD)(X)&0xFF)<<24)|(((DWORD)(X)&0xFF00)<<8)|(((DWORD)(X)&0xFF0000)>>8)|(((DWORD)(X)&0xFF000000)>>24))

#define FixHeader(H) {(H).fmt=rev16((H).fmt);(H).trax=rev16((H).trax);(H).dtx=rev16((H).dtx);}

struct write_buf;

typedef struct
{
	int pos,tm;
} TMAP_ENTRY;

struct CTempoMap
{
public:
	TMAP_ENTRY *data;
	int pos,size;
	void AddEntry(int _p,int tm);
	~CTempoMap() {if (data) free(data);}
	int BuildTrack(grow_buf & out);
};

CTempoMap* tmap_merge(CTempoMap*,CTempoMap*);

typedef struct
{
	int pos,ofs,len;
} SYSEX_ENTRY;

struct CSysexMap
{
public:
	DWORD d_size,e_size;
	SYSEX_ENTRY *events;
	BYTE* data;
	int pos,d_pos;
	void CleanUp();
	void AddEvent(const BYTE* e,DWORD s,DWORD t);
	~CSysexMap();
	CSysexMap* Translate(MIDI_file* _mf);//MIDI_file* mf
	int BuildTrack(grow_buf & out);
	const char* GetType();
};


typedef struct tagKAR
{
	UINT time;
	UINT start,end;
	BOOL foo;
} KAR_ENTRY;


KAR_ENTRY * kmap_create(MIDI_file* mf,UINT prec,UINT * num,char** text);


CTempoMap* tmap_create();
CSysexMap* smap_create();


int EncodeDelta(BYTE* dst,int d);
unsigned int DecodeDelta(const BYTE* src,unsigned int* _d, unsigned int limit=-1);
int ReadSysex(const BYTE* src,int ml);

char* BuildFilterString(UINT res_id, char* ext, int* len);
BOOL DoOpenFile(HWND w,char* fn,UINT res_id,char* ext,BOOL save);

typedef void (*SYSEXFUNC)(void*,BYTE*,UINT);
void sysex_startup(SYSEXFUNC,void*);
void sysex_startup_midiout(UINT m_id);
bool need_sysex_start();

typedef struct
{
	DWORD tm;
	DWORD ev;
} MIDI_EVENT;

MIDI_EVENT* do_table(MIDI_file* mf,UINT prec,UINT * size,UINT* _lstart,DWORD cflags);

void gb_write_delta(grow_buf & gb,DWORD d);

void do_messages(HWND w,bool* br);
ATOM do_callback_class(WNDPROC p);
HWND create_callback_wnd(ATOM cl,void* p);

class sysex_table
{
private:
	struct entry
	{
		entry * next;
		int size,time;
		BYTE * data;
	};
	entry * entries;
	enum {MHP_MAGIC='0PHM'};
public:
	sysex_table() {entries=0;}
	~sysex_table() {reset();}
	int num_entries() const;
	int get_entry(int idx,BYTE ** p_data,int * p_size,int * p_time) const;
	void insert_entry(int idx,BYTE * data,int size,int time);
	int remove_entry(int idx);
	
	inline void add_entry(BYTE * data,int size,int time) {insert_entry(num_entries(),data,size,time);}
	inline void modify_entry(int idx,BYTE * data,int size,int time) {remove_entry(idx);insert_entry(idx,data,size,time);}
	inline void reset() {while(entries) remove_entry(0);}
	inline int get_time(int idx) const {int time;return get_entry(idx,0,0,&time) ? time : 0;}

	int file_read(const char * path);
	int file_write(const char * path) const;
	void * memblock_write(int * size) const;
	int memblock_read(const void * ptr,int size);

	int print_preview(int idx,char * out) const;
	void print_edit(int idx,HWND wnd) const;
	
	void copy(const sysex_table & src);
	sysex_table(const sysex_table & src) {entries=0;copy(src);}
	sysex_table& operator=(const sysex_table & src) {copy(src);return *this;}
	int is_empty() {return !entries;}
};


#endif