#ifndef __IStream_H
#define __IStream_H

#include <stdbool.h>
#include <stdint.h>
#include "kernel.h"
#include "TimLib:MixStream.h"
#include "Desc.h"
#include "bitbuffer.h"

typedef unsigned char byte;

typedef struct GlobHdr GlobHdr;
typedef struct IStream IStream;
typedef struct IReader IReader;
typedef struct ICodec ICodec;
typedef struct IWriter IWriter;

#define ErrNum_UnsupportedFormat 0x0081b000
#define ErrNum_PlayerError       0x0081b001
#define ErrNum_ParamsError       0x0081b002
#define ErrNum_InputError        0x0081b003
#define ErrNum_CodecError        0x0081b004
#define ErrNum_InternalError     0x0081b005

typedef const _kernel_oserror* (*MetaCodec)(IStream* s, bool *b);
typedef const _kernel_oserror* (*FooterCodec)(IStream* s);

typedef struct
{
	int32_t          size;
	volatile int32_t start;
	volatile int32_t free;
	volatile int32_t finishflag;
	int32_t          dummy[12];
} inb_t;

#define max_channels 8

#define swrk_status_ready       0x00000001 // Current sub-stream playable (samperate, channels known).
#define swrk_status_playing     0x00000002 // Replay started and not currently paused.
#define swrk_status_paused      0x00000004 // Replay started but paused.
#define swrk_status_noloop      0x00000008 // Stop replay when reaching end of input data.
#define swrk_status_outputdone  0x00000010 // Finished to play all data decoded up to now.
#define swrk_status_buffering   0x00000020 // Buffering decoded data without playing a sound yet.
#define swrk_status_readmask    0x0000003F
#define swrk_status_altermask   0x00000008

#define swrk_status_RD_nodelay  0x80000000 // Backgroung IO may delayed (cf. within non-reentrant FS).
#define swrk_status_RD_restart  0x40000000 // Need to move back to beginning of stream (loop).
#define swrk_status_sub_init    0x08000000 // New sub-stream starting, reset codec.
#define swrk_status_sub_decoded 0x02000000 // Sub-stream completely decoded.
#define swrk_status_sub_played  0x01000000 // Sub-tream completely played.
#define swrk_status_decoded     0x00800000 // Stream completely decoded.
#define swrk_status_played      0x00400000 // Stream completely played.
#define swrk_status_closeatend  0x00200000 // Do not reconnect source at end (for download to file).

typedef enum
{
	  EIStream_State_FatalError     = 1
	, EIStream_State_NoSource       = 2 // no source defined
	, EIStream_State_SourceInit     = 3 // source, config and negotiations
	, EIStream_State_MetaDataLookup = 4 // meta headers processing
	, EIStream_State_CodecLookup    = 5 // stream type identification
	, EIStream_State_CodecInit      = 6 // codec init
	, EIStream_State_Buffering      = 7 // buffering output buffer
	, EIStream_State_CodecReady     = 8 // codec stream processing
	, EIStream_State_InputSaving    = 9 // saving source without decoding
} EIStream_State;

#define EMeta_StreamType        3
#define EMeta_StreamTitle       4
#define EMeta_StreamAuthor      5
#define EMeta_StreamAlbum       6
#define EMeta_StreamStation     7
#define EMeta_StreamDescription 8
#define EMeta_StreamGenre       9
#define EMeta_StreamComment     10
#define EMeta_StreamUrl         11
#define EMeta_StreamEncoder     12
#define EMeta_StreamFileType    13
#define EMeta_StreamMimeType    14
#define EMeta_StreamTrackNumber 15
#define EMeta_StreamDate        16
#define EMeta_MaxValue          16



typedef struct Meta
{
	struct Meta*    pnext;
	unsigned int    timestamp;
	unsigned int    id;
	unsigned int    level;
	unsigned int    size;
	void*           data;
} Meta;

typedef struct TimeMarker
{
	uint64_t        time;
	int32_t         position;
} TimeMarker;

#define MAX_TIMEMARKERS 512

typedef struct TimeMarkerList
{
	int32_t          size;
	int32_t          start;
	int32_t          free;
	TimeMarker       markers[MAX_TIMEMARKERS];
} TimeMarkerList;

struct IStream
{
	GlobHdr*        pGlobHdr;
	char            Tag[4];
	uint32_t        index;
	EIStream_State  state;
	unsigned int    status;
	Meta*           meta;
	MetaCodec       fnmeta;
	int             inb_area;
	inb_t*          inb;
	bytebuf         bitb;
	inb_t           saved_inb;
	uint32_t        chained_handle;
	struct
	{
		uint32_t        length;
		uint32_t        start;
		uint32_t        end;
		uint32_t        pos;
		unsigned int    os_filetype;
		char*           mimetype;
		int64_t         BytesRead;
		TimeMarkerList  timeMarkers; // positions refer to inb->free
	}               source;
	struct
	{
		IReader*        fn;
		void*           data;
		int32_t         chunksize;
	}               reader;
	struct
	{
		IWriter*        fn;
		void*           data;
		int             volume;
	}               writer;
	struct
	{
		const ICodec*   fn;
		const ICodec*   prefn;
		void*           params;
		void*           data;
		// Since last set pos
		struct
		{
			int         decoded;
			int         played;
		} samples;
	}               codec;
	struct
	{
		int             chunksize;
		int             Channels;
		unsigned int    ChannelsLayout;
		uint32_t        ChannelsMuting; // 1 bit per channel
		int             SampleRate;
		int             MeanBitRate;
		unsigned int    TimeStamp;
		int64_t         PlayingTime; // in 1/256 us
		TimeMarkerList  timeMarkers; // positions refer to codec.samples.played
		TimeMarker      refTime;
	}               info;
	void (*LastCallback)(IStream*);
	_kernel_oserror LastError;
	MixStream*		StreamList[max_channels];
	MixStream		ChannelsArray[max_channels];
};


const _kernel_oserror* IStream_Alloc(IStream* s, void** pp, size_t size);
const _kernel_oserror* IStream_AllocString(IStream* s, char** pp, const char* pref);
void IStream_Free(IStream* s, void** pp);

void IStream_ClearMetadata(IStream* s, unsigned int level);
Meta* IStream_FindMetadata(IStream* s, unsigned int id);
const _kernel_oserror* IStream_SetMetadata(IStream* s, unsigned int level, unsigned int id, const void* data, unsigned int size);
const _kernel_oserror* IStream_SetText(IStream* s, unsigned int level, unsigned int id, const char* data, unsigned int size);
const _kernel_oserror* IStream_Accept(IStream* s, const Desc*);


// Returns -1 if not enough bytes in buffer, -2 if no end of line for given buffer size
int IStream_ReadTextLine(IStream* s, char* p, int buffersize);
// Returns position of input ptr (e.g. file pos - nr of bytes in input buffer)
int IStream_GetInputPos(const IStream* s);
// Returns 1 if input method seekable, 0 otherwise
int IStream_IsSeekable(IStream* s);
// Move to given byte offset of input or do nothing if not seekable
const _kernel_oserror* IStream_Seek(IStream* s, int position);
// Try to skip N bytes from internal buffer and if necessary & possible from input reader
unsigned int IStream_SkipBytes(IStream* s, unsigned int N);
// Set given position (byte offset of input/virtual position) or do nothing if not positionable
const _kernel_oserror* IStream_SetPos(IStream* s, int position);
// To be called when a codec alters the free pos (e.g. clears the buffer)
void IStream_SaveInb(IStream* s);
// Defines an area to store output sample data
const _kernel_oserror* IStream_NewOutputBuffer(IStream* s, int32_t* psize, int32_t* parea, void** pdata);
void IStream_DeleteOutputBuffer(IStream* s, int32_t* parea);

#endif
