#ifndef AVIFILE_AVIFILE_H
#define AVIFILE_AVIFILE_H

/********************************************************
 *
 *       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.
 *
 ********************************************************/

#include <avifmt.h>
#include <image.h>
#include <except.h>
#include <StreamInfo.h>
#include <audioencoder.h>
#include <videodecoder.h>
class AviStream
{
protected:
    AVIStreamHeader m_header;
public:
    static const unsigned int ERR = ~0U;
    enum StreamType{
	Audio,
	Video,
	Other
    };
};

/**
 *    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.
 *
 *   IAviReadStream 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. As a better alternative, see ReadHandlers.h header file.
 *
 *   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 Seek(double), and only when it's definitely needed, because it
 * is very lengthy operation. For broadcast streams Seek(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 IAviReadStream : public AviStream
{
public:
    virtual ~IAviReadStream();
    /** Checks for end of stream. */
    virtual bool Eof() const 						=0;
    virtual StreamType GetType() const					=0;
    virtual HRESULT GetHeader(void* pheader, size_t n) const   =0;
    /**
     * For video stream returns video format information ( usually in
     * BITMAPINFOHEADER format ). For other stream types fails.
     */
    virtual HRESULT GetVideoFormatInfo(void* bi, size_t size) const	=0;
    /**
     * For audio stream returns audio format information. Stores main
     * format info in bi - 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.
     *
     * For other stream types fails.
     */
    virtual HRESULT GetAudioFormatInfo(void* bi, char** ext) 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 it no longer needed!
     */
    virtual StreamInfo* GetStreamInfo() const				=0;
    /** Duration of one frame/sample. */
    virtual double GetFrameTime() const 				=0;

    /**
     * these four are only meaningful for video streams, but no checking
     * is done
     * 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 HRESULT Seek(framepos_t pos) 				=0;
    virtual HRESULT SeekTime(double pos)				=0;
    virtual framepos_t SeekToKeyFrame(framepos_t pos) 			=0;
    virtual double SeekTimeToKeyFrame(double pos)			=0;
    virtual HRESULT SkipFrame()						=0;
    virtual HRESULT SkipTo(double pos)					=0;
    /**
     * Time for frame - ERR -> current time in stream
     */
    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;
    /**
     * Initializes decoders.
     */
    virtual HRESULT StartStreaming() 					=0;
    /**
     * Closes decoders.
     */
    virtual HRESULT StopStreaming() 					=0;
    virtual HRESULT SetOutputFormat(void* bi, size_t size)		=0;
    /**
     * Specifies desired output format. Call after StartStreaming.
     * Only valid for video streams. Not implemented.
     */

    /* 		         For video streams:  				*/

    /**
     * Returns current output format. Call after StartStreaming.
     */
    virtual HRESULT GetOutputFormat(void* format, size_t size) const  =0;
    virtual IVideoDecoder* GetDecoder()	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 HRESULT GetFrameFlags(int* flags) const			=0;
    virtual HRESULT SetDirection(bool direction) 			=0;

    /**
     * Reads one frame, decompresses it into internal buffer
     * and increases position counter.
     * Call before any other operation with frame.
     */
    virtual HRESULT ReadFrame(bool render = true)			=0;
    /**
     * Returns pointer to internal frame buffer.
     * It will call ReadFrame internaly when readFrame == true.
     */
    virtual CImage* GetFrame(bool readFrame = false)  			=0;
    /**
     * returns whether player should drop the frame - DO NOT USE!
     */
    virtual bool DropFrame() const					=0;
    /**
     * Can be calculated from video format as width*height*bpp/8
     */
    virtual size_t GetFrameSize() const					=0;

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

    /**
     * Reads and decompresses variable number of frames into
     * user-supplied buffer.
     */
    virtual HRESULT ReadFrames(void* buffer, size_t bufsize, size_t samples,
			       size_t& samples_read, size_t& bytes_read) =0;
    /**
     * Directly reads data from file. Incompatible with streaming.
     */
    virtual HRESULT ReadDirect(void* buffer, size_t bufsize, size_t samples,
			       size_t& samples_read, size_t& bytes_read) =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;
};

class IAviReadFile
{
public:
    virtual ~IAviReadFile();
    virtual unsigned int StreamCount() 					=0;
    virtual unsigned int VideoStreamCount() 				=0;
    virtual unsigned int AudioStreamCount() 				=0;
    virtual IAviReadStream* GetStream(streamid_t stream_id,
				      AviStream::StreamType type)	=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.
     */
    //virtual HRESULT GetHeader(MainAVIHeader* header) 		=0;
    virtual HRESULT GetHeader(void* pheader, size_t n) const  	=0;
    /**
     * See doc/README-DEVEL
     *   For local files isOpened() will return true immediately after opening.
     */
    virtual bool IsOpened() const    					=0;
    virtual bool IsValid() const  	       				=0;
    virtual bool IsRedirector()	const	  				=0;
    /**
     * This is the only thing that you can do with redirector.
     * Argument of the function may change in future versions.
     */
    virtual bool GetURLs(std::vector<std::string>& urls) const		=0;
};

class IAviWriteStream : public AviStream
{
    friend class IAviWriteFile;
public:
    virtual ~IAviWriteStream() 						=0;
    //
    // these two should be called before data insertion begins
    //
    //
    // for video streams, specify time in microsecs per frame in frame_rate
    // for audio streams - count of bytes per second
    //
    virtual HRESULT AddChunk(const void* chunk, size_t size, int flags = 0) =0;

    virtual size_t GetLength() const					=0;
    virtual StreamType GetType() const 					=0;
};

class IAviVideoWriteStream : public AviStream
{
public:
    virtual ~IAviVideoWriteStream();
    virtual HRESULT AddFrame(CImage* chunk, size_t* pSize=0, int* pKeyframe=0)
									=0;
    virtual HRESULT Start()						=0;
    virtual HRESULT Stop()						=0;
    virtual HRESULT Set(...) { return -1; }
    virtual HRESULT Get(...) { return -1; }
    //0..10000
    virtual HRESULT SetQuality(int quality)				=0;
    virtual HRESULT SetKeyFrame(int frequency)				=0;
    virtual size_t GetLength() const     				=0;
};

class IAviAudioWriteStream : public AviStream
{
public:
    virtual ~IAviAudioWriteStream(){}
    virtual HRESULT AddData(void* data, size_t size)			=0;
    virtual HRESULT Start()						=0;
    virtual HRESULT Stop()						=0;
    virtual HRESULT Set(...) { return -1; }
    virtual HRESULT Get(...) { return -1; }
    //0..10
    virtual HRESULT SetQuality(int quality)				=0;
    virtual size_t GetLength() const   				=0;
};

class IAviWriteFile
{
    friend class IAviWriteStream;
public:
    virtual ~IAviWriteFile();
    virtual IAviVideoWriteStream* AddVideoStream(fourcc_t fourcc,
						 BITMAPINFOHEADER* srchdr,
						 int frame_rate,
						 int flags = 0)		=0;
    virtual IAviAudioWriteStream* AddAudioStream(fourcc_t fourcc,
						 WAVEFORMATEX* fmt,
						 int bitrate,
						 int flags = 0)		=0;
    virtual IAviWriteStream* AddStream(enum AviStream::StreamType type,
				       const void* format, size_t format_size,
				       fourcc_t handler, int frame_rate,
				       size_t samplesize=0, int quality = 0,
				       int flags = 0)			=0;
    virtual HRESULT Reserve(size_t size)				=0;
    virtual HRESULT WriteChunk(fourcc_t fourcc, void* data, size_t size)=0;
    virtual int64_t FileSize()						=0;
    virtual const char* FileName() const				=0;
};

class IAviSegWriteFile: public IAviWriteFile
{
public:
    virtual void Segment() =0;
};

IAviReadFile* CreateIAviReadFile(const char* name);
IAviWriteFile* CreateIAviWriteFile(const char* name, int flags=0, int mask=00777);
IAviSegWriteFile* CreateSegmentedFile(const char* name, size_t flimit=0x7F000000,
				      int flags=0, int mask=00777);

#endif // AVIFILE_AVIFILE_H
