#ifndef AVIFILE_ASFFMT_H
#define AVIFILE_ASFFMT_H

#include "avm_stl.h"
#include "formats.h" // GUID, WAVEFORMATEX
#include "utils.h"
#include "asf_guids.h"

AVM_BEGIN_NAMESPACE;

// Data is stored in ASF packets in a relatively complicated manner
// ( see details in http://divx.euro.ru/asf-1.0.htm ).
// The following objects represent that data in convenient form.

typedef struct
{
    uint32_t object_start_time;
    uint32_t fragment_offset;	// offset of fragment within object
    // ( needed for reconstruction of fragmented packets )
    uint32_t object_length;	// length of object that this fragment beints to
    uint32_t data_length;	// length of this fragment
    uint32_t seq_num;           // sequential id for fragmented packet
    const uint8_t* pointer;	// pointer to data inside packet
    // first byte of fragment N in packet p
    //   is p[p.fragments[N].pointer]
    uint8_t keyframe :1;        // is this a keyframe
    uint8_t stream_id :7;       // stream id - valid 1 - 127
} asf_packet_fragment;


struct asf_packet: avm::vector<unsigned char>
{
    uint8_t	length_flags;	// see spec  masks:
                                // 0x01 Multiple payloads present
                                // 0x06 Sequence_type
                                // 0x18 Padding length
                                // 0x60 Packet length
                                // 0x80 Error Correction Present
    uint8_t	property_flags; // masks:
                                // 0x03 Replicated data length type
                                // 0x0c Offset into media object type
                                // 0x30 Media object number length type
                                // 0xc0 Stream number length type

    uint16_t	packet_length;	// of data following header
    uint8_t	segsizetype;
    uint8_t	segments;	// number of segments
    uint32_t	send_time;	// timestamp in milliseconds
    uint16_t	duration;	// in milliseconds
    uint_t	length;		// of header itself

    avm::vector<asf_packet_fragment> fragments;

    asf_packet(uint_t psize = 0);
    int init(uint_t timeshift = 0); // packet could be time shifted
    void addRef() { refcount++; }
    void release() { if (!--refcount) delete this; }

protected:
    int segment(uint8_t* memory, uint_t offset, uint_t ts);
    int refcount;
};

typedef struct __attribute__((packed))
{
    GUID guid;			// generated by client computer
    uint64_t file_size;		// in bytes
                                // invalid if broadcasting
    uint64_t create_time;	// time of creation, in 100-nanosecond units since 1.1.1601
                                // invalid if broadcasting
    uint64_t pkts_count;	// how many packets are there in the file
                                // invalid if broadcasting
    uint64_t play_time;		// play time, in 100-nanosecond units
                                // invalid if broadcasting
    uint64_t send_time;		// time to send file, in 100-nanosecond units
                                // invalid if broadcasting (could be ignored)
    uint32_t preroll;		// timestamp of the first packet, in milliseconds
    				// if nonzero - substract from time
    uint32_t ignore;            // preroll is 64bit - but let's just ignore it
    uint32_t flags;		// 0x01 - broadcast
    				// 0x02 - seekable
                                // rest is reserved should be 0
    uint32_t min_pktsize;	// size of a data packet
                                // invalid if broadcasting
    uint32_t max_pktsize;	// shall be the same as for min_pktsize
                                // invalid if broadcasting
    uint32_t max_bitrate;	// bandwith of stream in bps
    				// should be the sum of bitrates of the
                                // individual media streams
} ASFMainHeader;

static inline void avm_get_leGUID(GUID* g)
{
    g->f1		= avm_get_le32(&g->f1);
    g->f2		= avm_get_le16(&g->f2);
    g->f3		= avm_get_le16(&g->f3);
}

static inline void avm_get_leAsfMainHeader(ASFMainHeader* h)
{
    avm_get_leGUID(&h->guid);
    h->file_size	= avm_get_le64(&h->file_size);
    h->create_time	= avm_get_le64(&h->create_time);
    h->pkts_count	= avm_get_le64(&h->pkts_count);
    h->play_time 	= avm_get_le64(&h->play_time);
    h->send_time	= avm_get_le64(&h->send_time);
    h->preroll		= avm_get_le32(&h->preroll);
    h->flags		= avm_get_le32(&h->flags);
    h->min_pktsize     	= avm_get_le32(&h->min_pktsize);
    h->max_pktsize	= avm_get_le32(&h->max_pktsize);
    h->max_bitrate	= avm_get_le32(&h->max_bitrate);
}

void avm_print_AsfMainHeader(const ASFMainHeader*);

union ASFStreamHeader
{
    char buf[1024];
    struct __attribute__((__packed__))
    {
	GUID stream_guid;	// type of media stream
	GUID error_guid;	// data error correction used
	uint64_t time_offset;	// presentation time (in 100-nanosecond unit)
	uint32_t stream_size;	// size of type-specific data
	uint32_t error_size;	// size of error correct data
	uint16_t stream;	// number ( 1, 2 ... )
	uint32_t reserved;	// usually the same in both streams:
				// Eyes on me.asf: 0x62dffd4
				// Alsou - Solo.asf: 0x10
				// Baby one more time.asf: 0x10
				// Cure_LastDayOfSummer.wma: 0x818f900c
				// Windows Movie Maker Sample File.wmv: 0x3f
				// KellssyV_500VBR.wmv: 0x0
        union
	{
         struct __attribute__((__packed__))
	 {
	     uint32_t width;	// witdth of encoded image
	     uint32_t height;	// height of encoded image
	     uint8_t  flags;	// shall be set to 2
	     uint16_t data_size;
	     BITMAPINFOHEADER bh;
         } vid;
         struct __attribute__((__packed__))
         {
           WAVEFORMATEX wfex;
	   // audio scramble data follows
         } aud;
       };
    } hdr;
};

static inline void avm_get_leAsfStreamHeader(ASFStreamHeader* h)
{
    avm_get_leGUID(&h->hdr.stream_guid);
    avm_get_leGUID(&h->hdr.error_guid);
    h->hdr.time_offset	= avm_get_le64(&h->hdr.time_offset);
    h->hdr.stream_size	= avm_get_le32(&h->hdr.stream_size);
    h->hdr.error_size	= avm_get_le32(&h->hdr.error_size);
    h->hdr.stream	= avm_get_le16(&h->hdr.stream);
    h->hdr.reserved    	= avm_get_le32(&h->hdr.reserved);

    if (!guid_is_guidid(&h->hdr.stream_guid, GUID_ASF_AUDIO_MEDIA))
    {
	h->hdr.vid.width = avm_get_le32(&h->hdr.vid.width);
	h->hdr.vid.height = avm_get_le32(&h->hdr.vid.height);
	h->hdr.vid.data_size = avm_get_le16(&h->hdr.vid.data_size);
	avm_get_leBITMAPINFOHEADER((BITMAPINFOHEADER*)&h->hdr.vid.bh.biSize);
    }
    else
	avm_get_leWAVEFORMATEX(&h->hdr.aud.wfex);
}

static inline char* avm_convert_asfstring(void* str, uint_t size)
{
    if (str)
    {
        char* s = (char*) str;
	uint_t i,j;
	for (i=0, j=0; i<size && s[i]!='\0'; i+=2, j++)
	    s[j]=s[i];
	s[j]='\0';
    }
    return (char*)str;
}

typedef struct __attribute__((__packed__))
{
    uint8_t  span;		// total number of audio blocks in each scramble group
    uint16_t packet_length;	// byte size of each scrambling chunk
    uint16_t chunk_length;	// usually = nBlockAlign  Chunks Per Data Unit
    uint16_t data_length;	// usually = nBlockAlign  Chunk Distance
    uint8_t  silence_data;
} AsfSpreadAudio;

static inline void avm_get_leAsfSpreadAudio(AsfSpreadAudio* h)
{
    h->packet_length	= avm_get_le16(&h->packet_length);
    h->chunk_length	= avm_get_le16(&h->chunk_length);
    h->data_length	= avm_get_le16(&h->data_length);
    //h->block_align_2	= avm_get_le16(&h->block_align_2);
}

AVM_END_NAMESPACE;

#endif // AVIFILE_ASFFMT_H
