#ifndef AVIFILE_AVIFILE_H
#define AVIFILE_AVIFILE_H

#include "videodecoder.h"
#include "avm_stl.h"

AVM_BEGIN_NAMESPACE;

class CImage;
class StreamInfo;
class IVideoRenderer;
class PthreadMutex;
class IAudioDecoder;
class IVideoDecoder;

/**
 *       Classes for reading & writing movie files.
 *   Interfaces are the same for all supported formats:
 *      AVI, local and networked ASF 1.0. Writing is
 *           possible only into AVI files.
 */

/**
 * Abstract class for reading streams
 */
class IStream
{
public:
    static const uint_t ERR = ~0U;
    static const uint_t KEYFRAME = 0x00000010L;
    enum StreamType
    {
	Audio,
	Video,
	Other
    };
    enum StreamFormat
    {
	Avi = 1,
	Wav = 10
    };
    virtual ~IStream();
};

/**
 *    This class provides means of sequential access to data from one
 * stream, usually video or soundtrack. Though AVI files rarely contain
 * more than one soundtrack, they are also supported. Streams of other
 * kind are not handled.
 *
 *   It maintains an internal position counter, which can be read with
 * @ref GetPos()/@ref GetTime() and changed with @ref Seek()/@ref SeekToKeyframe().
 * This counter is modified by all reading functions; function that reads
 * 10 frames will increase counter by 10.
 *
 *   IReadStream is capable of implicit video and audio decompression,
 * so that you won't need to work with decoders directly. All you need is
 * to call @ref StartStreaming(), and if it succeeds ( does not throw exception ),
 * call members that decompress data into internal buffer ( @ref ReadFrame() ),
 * return this buffer ( @ref GetFrame() ) or decompress data into your memory
 * ( @ref ReadFrames() ).
 *
 *   If you want to have direct access to compressed data, use @ref ReadDirect().
 * Note that this call is incompatible with streaming, so calls to @ref ReadDirect()
 * between @ref StartStreaming() and @ref StopStreaming() will fail. It is done so
 * because dropping even one data frame during sequence decompression will ruin
 * current picture.
 *
 *   Since version 0.6, these interfaces are also able to handle ASF 1.0 local
 * files, pre-recorded and broadcast MMS URLs. Special care is needed when
 * you want to deal with such files.
 *   First of all, you shouldn't assume that all video and audio frames have
 * the same duration ( result of GetFrameTime() ). It is not true for ASF files.
 * Instead, use GetTime().
 *   For MMS URLs, do not rely on GetPos() and Seek(framepos_t) at all.
 * These operations are simply unavailable in MMS protocol.
 * Use only SeekTime(double), and only when it's definitely needed, because it
 * is very lengthy operation. For live broadcast streams SeekTime(double)
 * is not available, either.
 *   There's an important issue with Seek() and multiple streams in MMS.
 * For the sake of efficiency library does not allow streams to be seekable
 * independently. Each Seek() operation on one stream automatically repositions
 * pointer for others approximately to the same position.
 *
 */
class IReadStream : public IStream
{
public:
    /** Checks for end of stream. */
    virtual bool Eof() const 						=0;
    virtual StreamType GetType() const					=0;
    /**
     * might disappear - use StreamInfo - this might only useful for stream copy
     * - size query - pheader == 0
     */
    virtual uint_t GetHeader(void* pheader = 0, uint_t size = 0) const	=0;
    /**
     * For audio stream returns audio format information. Stores main
     * format info in wf - usually WAVEFORMATEX structure
     *
     * if wf == 0 -- query for the size of structure
     * For other stream types fails.
     */
    virtual uint_t GetAudioFormat(void* format = 0, uint_t size = 0) const =0;
    /**
     * For video stream returns video format information
     * ( usually in BITMAPINFOHEADER format ).
     * For other stream types fails.
     *
     * if format == 0 -- query for the size of structure
     */
    virtual uint_t GetVideoFormat(void* format = 0, uint_t size = 0) const =0;
    /** Total length of stream in samples. */
    virtual framepos_t GetLength() const				=0;
    /** Total length of stream in seconds. */
    virtual double GetLengthTime() const				=0;
    /**
     * Returns various informations in StreamInfo structure like
     * stream size in bytes, avg chunk size and some others...
     * It is users responsibility to free this object
     * when stream information is no longer needed!
     *
     * THIS IS PREFFERED method to get information about stream!
     * GetVideoFormatInfo & GetAudioFormatInfo should be used only
     * when you know what you are doing - mainly for data copying
     */
    virtual StreamInfo* GetStreamInfo() const				=0;
    /** Duration of one frame/sample. */
    virtual double GetFrameTime() const 				=0;

    /**
     * next four method are only meaningful for video streams,
     * but no checking is done
     * value ERR means current frame
     */
    virtual framepos_t GetNextKeyFrame(framepos_t frame = ERR) const	=0;
    virtual framepos_t GetPrevKeyFrame(framepos_t frame = ERR) const	=0;

    virtual framepos_t SeekToNextKeyFrame() 				=0;
    virtual framepos_t SeekToPrevKeyFrame() 				=0;
    /**
     * Seeks to position  'pos'  in stream. In video streams, if
     * streaming is started, you will need to decompress all
     * video frames since last key-frame before pos.
     */
    virtual int Seek(framepos_t pos) 					=0;
    virtual int SeekTime(double pos)					=0;
    virtual framepos_t SeekToKeyFrame(framepos_t pos) 			=0;
    virtual double SeekTimeToKeyFrame(double pos)			=0;
    virtual int SkipFrame()						=0;
    virtual int SkipTo(double pos)					=0;
    /**
     * Time for frame - ERR -> current time
     */
    virtual double GetTime(framepos_t frame = ERR) const		=0;
    /**
     * Current frame position in stream.
     */
    virtual framepos_t GetPos() const					=0;

    virtual bool IsStreaming() const					=0;
    /**
     * Initialize decoder.
     */
    virtual int StartStreaming(const char* privname = 0)		=0;
    /**
     * Close decoder.
     */
    virtual int StopStreaming() 					=0;

    /*** For audio streams: ***/

    /** retrieve pointer to video decoder - use with caution!!! */
    virtual IAudioDecoder* GetAudioDecoder() const			=0;
    /**
     * Reads and decompresses variable number of frames into
     * user-supplied buffer.
     */
    virtual int ReadFrames(void* buffer, uint_t bufsize, uint_t samples,
			   uint_t& samples_read, uint_t& bytes_read)	=0;


    /*** For video streams  ***/

    /** retrieve pointer to video decoder - use with caution!!! */
    virtual IVideoDecoder* GetVideoDecoder() const			=0;
    /**
     * Returns current output format. Call after StartStreaming.
     */
    virtual uint_t GetOutputFormat(void* format = 0, uint_t size = 0) const	=0;
    /**
     * Returns flags associated with current frame. You'll need this
     * function if you use IVideoDecoder for decompression. Then,
     * pass resulting value to decoder together with frame data.
     */
    virtual int GetFrameFlags(int* flags) const				=0;
    /**
     * Can be calculated from video format as width*height*bpp/8 usually
     */
    virtual uint_t GetFrameSize() const					=0;

    /**
     * Flip image upside/down in decoder
     */
    virtual int SetDirection(bool direction) 				=0;

    /**
     * Specifies desired output format. Call before StartStreaming.
     * Only valid for video streams. Not implemented yet.
     */
    virtual int SetOutputFormat(void* bi, uint_t size)			=0;

    /**
     * Reads one frame, decompresses it into internal buffer
     * and increases position counter.
     */
    virtual int ReadFrame(bool render = true)				=0;
    /**
     * Returns pointer to internal frame buffer.
     * It will call ReadFrame internaly when readFrame == true.
     * when you no longer need this frame - call CImage::Release()
     */
    virtual CImage* GetFrame(bool readFrame = false)			=0;

    /**
     * Directly reads data from file. Incompatible with streaming.
     */
    virtual int ReadDirect(void* buffer, uint_t bufsize, uint_t samples,
			   uint_t& samples_read, uint_t& bytes_read,
			   int* flags = 0)				=0;

    /**
     * Returns approximate size of internal chunk cache. 1 is 'cache full'
     * and 0 is 'cache empty'. When cache size is too low, it may be a
     * good idea to wait for some time until it refills.
     */
    virtual double CacheSize() const					=0;


    /**
     * private mainly for aviplay usage
     */
    virtual int SetBuffering(uint_t maxsz = 1, IImageAllocator* ia = 0) =0;
    /**
     * retrieve information about buffers - max size and current status
     */
    virtual uint_t GetBuffering(uint_t* bufsz = 0) const		=0;

#ifdef AVM_COMPATIBLE
    /**
     * backward compatible inline declaration - do not use in new programs
     */

    /**
     * \deprecated
     * For audio stream returns audio format information. Stores main
     * format info in wf - usually WAVEFORMATEX structure - and pointer
     * to complete format in ext if ext is nonzero and if its size is
     * more than 18=sizeof(WAVEFORMATEX).
     * If *ext!=NULL on return, you should free it with   delete
     * when it's not needed anymore.
     */
    uint_t GetAudioFormatInfo(void* wf, char** ext) const
    {
	uint_t sz = GetAudioFormat();
	char* b = new char[sz];
	GetAudioFormat(b, sz);
	if (wf)
	{
	    for (unsigned i = 0; i < sz && i < sizeof(WAVEFORMATEX); i++)
		((char*)wf)[i] = b[i];
	}
	if (ext)
	{
	    if (*ext)
	    {
		for (unsigned i = 0; i < sz; i++)
		    (*ext)[i] = b[i];
                delete[] b;
	    }
	    else
		*ext = b;
	}
	else
            delete[] b;

        return 0;
    }
    /** \deprecated */
    uint_t GetVideoFormatInfo(void* bi, uint_t size) const { return GetVideoFormat(bi, size); }
    /** \deprecated */
    IVideoDecoder* GetDecoder() const { return GetVideoDecoder(); };
#endif
};

class IReadFile
{
public:
    enum {
	// currently these names aren't final
        // DO NOT USE if you want to stay compatible for NOW !

	// ignore indexes in files (i.e. if avi index gives false info)
	IGNORE_INDEX = 1,
	// ignore more header informations
	// (might help with some broken files)
	IGNORE_HEADER = 2,

	// do not create precaching thread
	NO_THREAD = (1 << 31)
    };
    virtual ~IReadFile();
    virtual uint_t StreamCount() 					=0;
    virtual uint_t VideoStreamCount() 					=0;
    virtual uint_t AudioStreamCount() 					=0;
    /**
     * May return something not completely correct for ASF file,
     * because it is not always possible to convert ASF to AVI.
     * For MMS URL returns structure with many invalid fields.
     * If you want to retrieve AVI header - pass approriate structure size
     * as it's rather internal you shouldn't need to use this function
     */
    virtual uint_t GetHeader(void* pheader, uint_t size)		=0;
    virtual IReadStream* GetStream(uint_t stream_id,
				   IStream::StreamType type)		=0;
    /**
     * With redirector you could only get list of URLs.
     */
    virtual bool GetURLs(avm::vector<avm::string>& urls)		=0;
    /**
     * See doc/README-DEVEL
     *   For local files IsOpened() will return true immediately after opening.
     */
    virtual bool IsOpened()    						=0;
    virtual bool IsValid()  	       					=0;
    virtual bool IsRedirector()	  					=0;
};

/**
 * Interface for storing any data into a stream
 */
class IWriteStream : public IStream
{
public:
    virtual int AddChunk(const void* chunk, uint_t size, int flags = 0) =0;
    virtual uint_t GetLength() const					=0;
    virtual StreamType GetType() const 					=0;
};

/**
 * Interface for video compressed stream
 */
class IVideoWriteStream
{
public:
    // pData will be filled with pointer to encoded data
    virtual int AddFrame(CImage* chunk, uint_t* pSize=0, int* pKeyframe=0, char** const pData = 0) =0;
    virtual const CodecInfo& GetCodecInfo() const			=0;
    virtual uint_t GetLength() const					=0;
    virtual int Start()							=0;
    virtual int Stop()							=0;

#ifdef AVM_COMPATIBLE
    //0..10000
    // use codecs atribute!
    virtual int SetQuality(int quality)		{ return -1; }
    virtual int SetKeyFrame(int frequency)	{ return -1; }
#endif
};

/**
 * Interface for audio compressed stream
 */
class IAudioWriteStream
{
public:
    virtual int AddData(void* data, uint_t size)			=0;
    virtual const CodecInfo& GetCodecInfo() const			=0;
    virtual uint_t GetLength() const					=0;
    //???virtual int SetAudioPreload(int preloadtime = 500.0, int rate = 100, bool bytime = true);
    virtual int Start()							=0;
    virtual int Stop()							=0;

#ifdef AVM_COMPATIBLE
    //0..10
    virtual int SetQuality(int quality)		{ return -1; }
#endif

};

class IWriteFile
{
public:
    virtual ~IWriteFile();
    // DO NOT delete these returned stream!
    virtual IVideoWriteStream* AddVideoStream(fourcc_t fourcc,
					      const BITMAPINFOHEADER* srchdr,
					      int frame_rate, int flags = 0) =0;
    virtual IVideoWriteStream* AddVideoStream(const CodecInfo& ci,
					      const BITMAPINFOHEADER* srchdr,
					      int frame_rate, int flags = 0) =0;
    virtual IVideoWriteStream* AddVideoStream(const VideoEncoderInfo* vi,
					      int frame_rate, int flags = 0) =0;
    virtual IAudioWriteStream* AddAudioStream(fourcc_t fourcc,
					      const WAVEFORMATEX* format,
					      int bitrate, int flags = 0) =0;
    virtual IAudioWriteStream* AddAudioStream(const CodecInfo& ci,
					      const WAVEFORMATEX* format,
					      int bitrate, int flags = 0) =0;
    virtual IWriteStream* AddStream(IStream::StreamType type,
				    const void* format,
				    uint_t format_size,
				    fourcc_t handler,
				    int frame_rate,
				    uint_t samplesize = 0,
				    int quality = 0,
				    int flags  = 0)			=0;
    // make stream with same parameters as given stream
    // useful for making copy of the stream
    virtual IWriteStream* AddStream(IReadStream* pCopyStream)		=0;
    virtual const char* GetFileName() const				=0;
    virtual int64_t GetFileSize() const					=0;
    virtual int Reserve(uint_t size)					=0;
    virtual int WriteChunk(fourcc_t fourcc, void* data, uint_t size)	=0;

    // close current file and open new avi file with same parameters
    virtual int Segment()                                               =0;

    // waits till the next keyframe and then segments
    virtual void SegmentAtKeyframe() =0;
    virtual void setSegmentName(avm::string new_name) =0;

};


/**
 * Open stream from given URL
 */
IReadFile* CreateReadFile(const char* url, unsigned int flags = 0);
/**
 * Create writable stream
 *
 */
IWriteFile* CreateWriteFile(const char* filename,
			    int64_t flimit = 0, // limit for segmentation
			    IStream::StreamFormat fmt = IStream::Avi,
			    int flags = 0,
			    int mask = 00666    // mask for file opennig
			   );

AVM_END_NAMESPACE;


//////////////////////////////////////
// backward compatibility
// - so far still use old API calls!!!
//////////////////////////////////////

#ifdef AVM_COMPATIBLE

typedef avm::IStream AviStream;
typedef avm::IReadStream IAviReadStream;

typedef avm::IReadFile IAviReadFile;
typedef avm::IWriteFile IAviWriteFile;
typedef avm::IWriteFile ISegWriteFile;
typedef avm::IWriteFile IAviSegWriteFile;
typedef avm::IWriteStream IAviWriteStream;
typedef avm::IAudioWriteStream IAviAudioWriteStream;
typedef avm::IVideoWriteStream IAviVideoWriteStream;

static inline IAviReadFile* CreateIAviReadFile(const char* url)
{
    return avm::CreateReadFile(url, 0);
}

static inline IAviWriteFile* CreateIAviWriteFile(const char* url, int flags = 0, int mask = 00666)
{
    return avm::CreateWriteFile(url, 0, avm::IStream::Avi, flags, mask);
}

static inline IAviSegWriteFile* CreateSegmentedFile(const char* url, uint_t flimit = 0x7F000000,
				      int flags = 0, int mask = 00666)
{
    return avm::CreateWriteFile(url, flimit, avm::IStream::Avi, flags, mask);
}

#endif // AVM_COMPATIBLE

#endif // AVIFILE_AVIFILE_H
