/***********************************************************************
 * VirtualDub Modification for OGM
 *
 * Copyright (C) 2002 Cyrius
 *
 * 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 2 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.
 *   
 * You should have received a copy of the GNU General Public License along
 * with this program (see the file COPYING); if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * or visit http://www.gnu.org/copyleft/gpl.html
 *
 ***********************************************************************
 *
 *
 *
 */

#ifndef f_OGMREADHANDLER_H
#define f_OGMREADHANDLER_H

#include <windows.h>
#include <vfw.h>

#include "../ogg/ogg.h"
#include "../vorbis/codec.h"
#include "OggDS.h"

#include "../AVIReadHandler.h"
#include "OGMStreams.h"
#include "OGMPacketizer.h"
#include "OGMCommon.h"

#include "../List.h"
#include "../File64.h"
#include "../FastReadStream.h"
#include "../ProgressDialog.h"
#include "../Error.h"

enum { S_OTHER = -1, S_VIDEO = 0, S_AUDIO = 1, S_VORBIS = 2, S_TEXT = 3};

// 16/11/2002, Cyrius : changed all lists to List2<ListNode2<...>>

class pageNode : public ListNode2<pageNode> {
public:
	unsigned __int64 file_pos;
	// Bit 63 of file_pos will tell if this is a continued page or not
	// the 63 other bits should be enough for a file size :)
	WORD size;
	ogg_int64_t granulepos;
	unsigned char nb_packets;
	long *n_samples;
	long *n_bytes;

	pageNode(void) {
		file_pos = 0;
		size = 0;
		granulepos = -1;
		nb_packets = 0;
		n_samples = NULL;
		n_bytes = NULL;
	};

	~pageNode(void) {
		if(n_samples)
			delete[] n_samples;
		if(n_bytes)
			delete[] n_bytes;
	};
};

class keyNode : public ListNode2<keyNode> {
public:
	unsigned char packetno;
	ogg_int64_t granulepos;
	pageNode *page;

	keyNode(void) {
		packetno = 0;
		granulepos = 0;
		page = NULL;
	};
};

class streamNode : public ListNode2<streamNode> {
public:
	int serial;
	int type;
	char num_headers;
	bool has_comments;
	comment_list *comments;
	double sample_rate;
	ogg_stream_state *sstate;
	stream_header sh;
	__int64           n_samples;
	__int64           length;
	vorbis_info       vi;
	vorbis_comment    vc;
	int               prevW;
	List2<pageNode> pages;
	List2<keyNode> keys;
	ogg_int32_t max_packet_size;

	streamNode(void) {
		serial = -1;
		type = S_OTHER;
		num_headers = 0;
		has_comments = false;
		comments = NULL;
		sample_rate = -1;
		sstate = NULL;
		memset(&sh, 0, sizeof(sh));
		n_samples = 0;
		length = 0;
		vorbis_info_init(&vi);
		vorbis_comment_init(&vc);
		prevW = 0;
		max_packet_size = 0;
	};

	~streamNode(void) {
		if(sstate) {
			ogg_stream_clear(sstate);
			delete sstate;
		}
		pageNode *page;
		while(page = pages.RemoveTail())
			delete page;
		keyNode *key;
		while(key = keys.RemoveTail())
			delete key;
		// Comments should be deleted with stream information (CloseOGMInputs in Command.cpp)
	};
};

#define BLOCKSIZE 32768
#define MAX_PAGESIZE (256*256)

class OGMReadHandler : public IAVIReadHandler, private File64 {
public:
	bool		fDisableFastIO;

	OGMReadHandler(char *);
	~OGMReadHandler();

	void AddRef();
	void Release();
	IAVIReadStream *GetStream(DWORD fccType, LONG lParam);
	void EnableFastIO(bool);
	bool isOptimizedForRealtime();
	bool isStreaming();
	bool isIndexFabricated();
	bool AppendFile(const char *pszFile);
	bool getSegmentHint(const char **ppszPath);
	long ReadData(void *buffer, long nBytes);
	void SeekFile(__int64 filePos);
	__int64 skippedBytes(void) { return bytes_skipped; };
	List2<streamNode> &getStreams(void) { return streams; };

private:
	int ref_count;
	__int64 bytes_read;
	__int64 bytes_skipped;
	List2<streamNode> streams;
	streamNode *currentStream;
	char *read_buffer;
	ogg_sync_state oss;
	int num_pkt;

	void		_construct(char *pszFile);
	void    _parseFile(void);
	int     _getNextPage(ogg_page *page);
	void    _addStream(ogg_page *page);
	streamNode *_getStream(int serial);
	void    _addPage(ogg_page *page);
	void    _addPacket(ogg_packet *packet);
  void    _destruct(void);
};

////////////////////////////////////////////////////////////////////////

class OGMReadStream : public IAVIReadStream {
	friend OGMReadHandler;

private:
protected:
	bool can_cut_first_packet;
	ogg_sync_state oss;
	ogg_stream_state sstate;
	OGMReadHandler *parent;
	streamNode *stream;
	keyNode *firstKey;
	keyNode *currentKey;
	keyNode *lastKey;
	pageNode *lastPage;
	pageNode *currentPage;
	pageNode *firstData;
	int first_packet;
	long length;
	void *format;
	int format_size;
	int _getNextPage(ogg_page *page, WORD size=BLOCKSIZE);

public:
	OGMReadStream(OGMReadHandler *, streamNode *);
	virtual ~OGMReadStream();

	int getType(void) { return stream->type; };
	HRESULT BeginStreaming(long lStart, long lEnd, long lRate);
	HRESULT EndStreaming();
	HRESULT Info(AVISTREAMINFO *pasi, long lSize);
	virtual bool IsKeyFrame(long lFrame);
	HRESULT Read(long lStart, long lSamples, void *lpBuffer, long cbBuffer, long *plBytes, long *plSamples);
	long Start();
	long End();
	virtual long PrevKeyFrame(long lFrame);
	virtual long NextKeyFrame(long lFrame);
	virtual long NearestKeyFrame(long lFrame);
	HRESULT FormatSize(long lFrame, long *plSize);
	HRESULT ReadFormat(long lFrame, void *pFormat, long *plSize);
	bool isStreaming();
	virtual bool isKeyframeOnly();

	bool getVBRInfo(double& bitrate_mean, double& bitrate_stddev, double& maxdev);

	int nbHeaders(void) { return stream->num_headers; };
	void sendHeaders(Packetizer *packetizer);
	ogg_int64_t samplesLag(__int64 start);
	comment_list *getComments(void);
	void setComments(comment_list *comments) { stream->comments = comments; };
	__int64 FramePos(long lFrame);
};

class OGMAudioReadStream : public OGMReadStream {
private:
public:
	OGMAudioReadStream(OGMReadHandler *, streamNode *);
	virtual ~OGMAudioReadStream();

	bool IsKeyFrame(long lFrame);
	long PrevKeyFrame(long lFrame);
	long NextKeyFrame(long lFrame);
	long NearestKeyFrame(long lFrame);
	bool isKeyframeOnly();
};

class OGMTextReadStream : public OGMAudioReadStream {
private:
public:
	OGMTextReadStream(OGMReadHandler *, streamNode *);
	bool isEOS(void);
};

IAVIReadHandler *CreateOGMReadHandler(char *pszFile);

#endif
