/*************************************************************
 *   mpgtx an mpeg toolbox                                   *
 *   by Laurent Alacoque <laureck@users.sourceforge.net>     *   
 *   (c) 2001                                                *
 *   You may copy, modify and redistribute this              *
 *   source file under the terms of the GNU Public License   *
 ************************************************************/
#ifndef __mpeg_hh_
#define __mpeg_hh_

#include "common.hh"
#include "mpegOut.hh"

// [laureck] Aug 2002
// We really should find its place for the following (any idea Phil ?) : 

typedef struct {
  // this stores the junk header of mpeg files, in case someone needs it later
	off_t size;
	byte *buf;

} header_buf;

/**
 *
 * @return pointer to the junk
 */
header_buf *readHeader(FILE *myMpegfile, off_t offset, int rw);





typedef int PID;

class mpeg_descriptors
{
public:
	mpeg_descriptors()
	{
		video_coding_version=-1;
		audio_coding_version=-1;
		CA=-1;
		CA_PID=-1;
		lang_audio_type=-1;
		languages=0;
		copyright=false;
		n_unhandled_desc=0;
	}
	~mpeg_descriptors()
	{
		if (languages) delete[] languages;
	}
	void PrintInfos(char* prefix);	
	int video_coding_version;
	int audio_coding_version;
	int CA;
	PID CA_PID;
	int lang_audio_type;
	char* languages;
	bool copyright;
	byte n_unhandled_desc;
};


typedef struct ES_t {
	PID pid;
	byte type;
	ES_t *next;
	mpeg_descriptors descs;
	FILE* demuxFile;
	bool demuxFileOk;
} EStream;


typedef struct t_prog {
	EStream *TStreams;
	int nstreams;
	int prog_num;
	mpeg_descriptors descs;
} program;



class mpeg;

//#define MAX_PROGS 100 

//helper class that carries transport stream infos
class transport
{
	friend class mpeg;
	friend class demuxer;
public :
	transport();
	~transport();

protected:
	void delete_programs();
	void delete_ES(EStream* stream);
	void PrintInfos();
	program* programs;
	int n_progs;
	int n_audio_streams;
	int n_video_streams;
	int n_other_streams;
	PID network_PID;

	PID* PMT_PIDs;
	int n_PMT_PIDs;
	int read_pmts;
};




class mpeg
{
	friend class mpegSystemOut;
	friend class demuxer;
protected:
	// disallow basic constructors
	mpeg(){};

	// Functions
	//  General ToolBox

	/**
	 * Gives a byte from address offset.
	 * If offset is out of buffer, it creates new buffer beginning on offset
	 * and load mpeg file in for forward operations.
	 *
	 * @param  offset address to get byte
	 * @return byte value from offset 
	 */
	byte GetByte(off_t offset);

	/**
	 * Gives a byte from address offset.
	 * If offset is out of buffer, it creates new buffer ending on offset
	 * and load mpeg file in for backward operations.
	 *
	 * @param  offset address to get byte
	 * @return byte value from offset 
	 */
	byte bdGetByte(off_t offset);

	bool EnsureMPEG(off_t offset,marker mark);
	double ReadTS (off_t offset);
	double ReadTSMpeg2(off_t offset);
	long ReadPACKMuxRate(off_t offset);

	/**
	 * find any marker.
	 * find next 0x 00 00 01 xx sequence
	 *
	 * @param from start address of search
	 * @return offset of found mark or -1 on error
	 */
	off_t FindNextMarker(off_t from);

	/** Searchs forward for any next marker, change mark accordingly.
	 * find next 0x 00 00 01 xx sequence and set mark = xx
	 *
	 * @param from start address of search
	 * @param mark pointer to mark to change
	 * @return offset of found mark or -1 on error
	 */
	off_t FindNextMarker(off_t from, marker* mark);

	/** Searchs backward for any next marker, change mark accordingly.
	 * find next 0x 00 00 01 xx sequence and set mark = xx
	 *
	 * @param from start address of search
	 * @param mark pointer to mark to change
	 * @return offset of found mark or -1 on error
	 */
	off_t bdFindNextMarker(off_t from, marker* mark);

	/** Prints every timestamp that follows a "0x 00 00 01 kind" sequence.
	 * time stamp format depends on mpeg_version
	 *
	 * @param kind header mark of time stamp headers
	 */
	void print_all_ts(byte kind);

	/** Checks time line of increasing time stamps and return amount of breaks.
	 *
	 * @param kind header mark of time stamp headers
	 * @return amount of breaks in time line
	 */
	int CheckTimeline(byte kind);

public:
	/**
	 * Constuctor.
	 * Create a mpeg object from given file
	 *
	 * @param filename  MPEG file to read
	 * @param verbosity Amount of process messages:
	 *		    mpeg_SILENT  suppress all messages
	 *                  mpeg_VERBOSE additional error messages
	 *		    other	 normal process messages
	 */
	mpeg(const char* filename,int verbosity=mpeg_SILENT);
	~mpeg();

	//  General ToolBox

	/**
	 * Prints infos about MPEG stream to stdout.
	 * Infos depending on MpegType:
	 * mpeg_AUDIO: 	duration, version, layer, bitrate, sampling rate, frame lengh, 
	 *		mode, emphasis, copyright, original/copy, ID3v1 tag
	 *
	 * mpeg_VIDEO:	duration, aspect ratio, interlace, video format, display size,
	 *		frame rate, bit rate, user data
	 *
	 * mpeg_SYSTEM:	infos above if available
	 *
	 * mpeg_TRANSPORT: call transport::PrintInfos()
	 *
	 */
	void PrintInfos();

	bool has_audio() {return HasAudio;}

	bool has_video() {return HasVideo;}

	off_t Size() {return FileSize;}

	const char* Name() {return FileName;}

	int  Version() {return mpeg_version;}


	/**
	 * Compare attributes of this and another MPEG
	 *
	 * @param  peer pointer to the other MPEG
	 * @return true if they fit, false otherwise
	 */
	bool Match(mpeg* peer);

	/// gets audio duration, video duration or zero (first valid will serve, of course ;-)
	float Duration();

	/**
	 * Calls mpeg::GetByte().
	 *
	 * @param  offset address to give to GetByte()
	 * @return a byte got from GetByte()
	 */
	byte Byte(off_t offset);

	/** search forward for specific marker.
	 * find next 0x 00 00 01 mark sequence
	 *
	 * @param  from start address of search
	 * @param  mark mark to look for
	 * @return offset of found marker or -1 on error
	 */
	off_t FindNextMarker(off_t from, marker mark);
	
	/** search backward for specific marker.
	 * find next 0x 00 00 01 mark sequence
	 *
	 * @param  from start address of search
	 * @param  mark mark to look for
	 * @return offset of found marker or -1 on error
	 */
	off_t bdFindNextMarker(off_t from, marker mark);

	/** gets two byte from address and next one.
	 *
	 * @param  offset address of higher byte, offset+1 is lower byte
	 * @return two byte value of offset and offset+1
	 */
	unsigned short int GetSize(off_t offset);

	/**
	 * Converts seconds in a String HH:MM:SS.xxs
	 *
	 * @param HMS char array (min. 13 bytes)
	 * @param duration in seconds
	 */
	void SecsToHMS(char* HMS, float duration);

	//  Audio ToolBox

	/// check if audio is present after myoffset
	bool ParseAudio(off_t myoffset);

	/// check if ID3v2 tag is present and audio follows.
	bool ParseID3();

	bool PrintID3();

	off_t FindMatchingAudio(off_t myoffset);
	off_t bdFindMatchingAudio(off_t myoffset);
	bool MatchAudio(off_t myoffset);
	//  Video ToolBox
	bool ParseVideo(off_t myoffset);
	bool ParseExtension(off_t myoffset);
	bool ParseSequenceExt(off_t myoffset);
	bool ParseSequenceDisplayExt(off_t myoffset);
	bool ParseUserData(off_t myoffset);
	//  System ToolBox
	bool ParseSystem();	
	bool ParseSystemPacket(off_t startofpacket, off_t startofpack);
	bool ParseSystemHeader(off_t myoffset);
	off_t SkipPacketHeader(off_t myoffset);

	void ParseFramesInGOP(off_t offset);
	bool ParseRIFF();
	bool ParseTransportStream(off_t offset);
	void ParseCAT(off_t offset);
	void ParsePAT(off_t offset);
	void ParsePMT(off_t offset);
	off_t ParseDescriptor(off_t offset, mpeg_descriptors* target);
	void DescCA(off_t offset, mpeg_descriptors* target);
	void DescLang(off_t offset, mpeg_descriptors* target);

	long Read12bitLength(off_t offset);
	long Read2Bytes(off_t offset);
	long ReadPID(off_t offset);

	PID  NextTrPacket(off_t* offset, off_t* payload_start, off_t* payload_end);

	/// file handle (I hate iostream and I can't use read/write on windows) 
	FILE* MpegFile;

	/// how much we speak
	int Verboseness;

	// mpeg Internals to be filled by constructor
	bool HasAudio,HasVideo;
	bool composite,editable;

	off_t FileSize;

	char* FileName;
	int  MpegType;

	// audio internals
	mpgtx_audio* Audio;
	byte n_audio;

	// video internals
	mpgtx_video* Video;
	byte n_video;

	/// system internals
	mpgtx_system* System;

	/// transport stream internals
	transport* Transport;

	// extentions
	sequence_ext* SExt;
	display_ext* DExt;
	user_data*    UData;

	/// start with id3v2 tag
	bool start_with_id3;

	/// for mpeg 1/ mpeg2
	byte mpeg_version;

	// for GetByte
	off_t buffstart;
	off_t buffend;
	byte* buffer;

};


typedef struct {
	char* file;
	off_t from;
	float sfrom;
	off_t to;
	float sto;
	bool from_included;
	bool to_included;
	bool until_file_size;
	bool unit_is_second;
	mpeg* mpegfile;
} chunk;

#endif // __mpeg_hh_

