/*
reader.h - compact version of famous library mpg123
Copyright (C) 2017 Uncle Mike

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
*/

#ifndef READER_H
#define READER_H

#define READER_FD_OPENED	0x1
#define READER_ID3TAG	0x2
#define READER_SEEKABLE	0x4
#define READER_BUFFERED	0x8
#define READER_NONBLOCK	0x20
#define READER_HANDLEIO	0x40

typedef struct buffy_s
{
	byte		*data;
	mpg_ssize_t		size;
	mpg_ssize_t		realsize;
	struct buffy_s	*next;
} buffy_t;

typedef struct bufferchain_s
{
	struct buffy_s	*first;		// the beginning of the chain.
	struct buffy_s	*last;		// the end...    of the chain.
	mpg_ssize_t		size;		// aggregated size of all buffies.

	// these positions are relative to buffer chain beginning.
	mpg_ssize_t		pos;		// position in whole chain.
	mpg_ssize_t		firstpos;		// the point of return on non-forget()

	// the "real" filepos is fileoff + pos.
	mpg_off_t		fileoff;		// beginning of chain is at this file offset.
	size_t		bufblock;		// default (minimal) size of buffers.
	size_t		pool_size;	// keep that many buffers in storage.
	size_t		pool_fill;	// that many buffers are there.

	// a pool of buffers to re-use, if activated. It's a linked list that is worked on from the front.
	struct buffy_s	*pool;
} bufferchain_t;

// call this before any buffer chain use (even bc_init()).
void bc_prepare( bufferchain_t*, size_t pool_size, size_t bufblock );
// free persistent data in the buffer chain, after bc_reset().
void bc_cleanup( bufferchain_t* );
// change pool size. This does not actually allocate/free anything on itself, just instructs later operations to free less / allocate more buffers.
void bc_poolsize( bufferchain_t*, size_t pool_size, size_t bufblock );
// return available byte count in the buffer.
size_t bc_fill( bufferchain_t *bc );

typedef struct reader_data_s
{
	mpg_off_t		filelen;		// total file length or total buffer size
	mpg_off_t		filepos;		// position in file or position in buffer chain
	int		filept;

	// custom opaque I/O handle from the client.
	void		*iohandle;
	int		flags;
	long		timeout_sec;

	mpg_ssize_t (*fdread)( mpg123_handle_t*, void*, size_t );

	// user can replace the read and lseek functions. The r_* are the stored replacement functions or NULL.
	mpg_ssize_t (*r_read)( int fd, void *buf, size_t count );
	mpg_off_t (*r_lseek)( int fd, mpg_off_t offset, int whence );

	// These are custom I/O routines for opaque user handles.
	// They get picked if there's some iohandle set.
	mpg_ssize_t (*r_read_handle)( void *handle, void *buf, size_t count );
	mpg_off_t (*r_lseek_handle)( void *handle, mpg_off_t offset, int whence );

	// an optional cleaner for the handle on closing the stream.
	void (*cleanup_handle)( void *handle );

	// these two pointers are the actual workers (default map to POSIX read/lseek).
	mpg_ssize_t (*read)( int fd, void *buf, size_t count );
	mpg_off_t (*lseek)( int fd, mpg_off_t offset, int whence );

	// buffered readers want that abstracted, set internally.
	mpg_ssize_t (*fullread)( mpg123_handle_t*, byte*, mpg_ssize_t );
	
	bufferchain_t	buffer;		// not dynamically allocated, these few struct bytes aren't worth the trouble.
} reader_data_t;

// start to use mpg_off_t to properly do LFS in future ... used to be long
typedef struct reader_s
{
	int	(*init)( mpg123_handle_t* );
	void	(*close)( mpg123_handle_t* );
	mpg_ssize_t	(*fullread)( mpg123_handle_t*, byte*, mpg_ssize_t);
	int	(*head_read)( mpg123_handle_t*, ulong *newhead );		// succ: TRUE, else <= 0 (FALSE or READER_MORE)
	int	(*head_shift)( mpg123_handle_t*, ulong *head );		// succ: TRUE, else <= 0 (FALSE or READER_MORE)
	mpg_off_t	(*skip_bytes)( mpg123_handle_t*, mpg_off_t len );		// succ: >=0, else error or READER_MORE
	int	(*read_frame_body)( mpg123_handle_t*, byte*, int size );
	int	(*back_bytes)( mpg123_handle_t*, mpg_off_t bytes );
	int	(*seek_frame)( mpg123_handle_t*, mpg_off_t num );
	mpg_off_t	(*tell)( mpg123_handle_t* );
	void	(*rewind)( mpg123_handle_t* );
	void	(*forget)( mpg123_handle_t* );
} reader_t;

// open a file by path or use an opened file descriptor
int open_stream( mpg123_handle_t *fr, const char *path, int fd );
// open an external handle.
int open_stream_handle( mpg123_handle_t *fr, void *iohandle );

// feed based operation has some specials
int open_feed( mpg123_handle_t *fr );
// externally called function, returns 0 on success, -1 on error
int feed_more( mpg123_handle_t *fr, const byte *in, long count );
// forget the data that has been read (free some buffers)
void feed_forget( mpg123_handle_t *fr );
// set position (inside available data if possible), return wanted byte offset of next feed.
mpg_off_t feed_set_pos( mpg123_handle_t *fr, mpg_off_t pos );
// error fallback
void open_bad( mpg123_handle_t *fr );

#endif//READER_H