/*
 * Copyright (c) 2004-2009 Mike Matsnev.  All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice immediately at the beginning of the file, without modification,
 *    this list of conditions, and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Absolutely no warranty of function or purpose is made by the author
 *    Mike Matsnev.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <stdint.h>

#ifndef MATROSKA_PARSER_H
#define MATROSKA_PARSER_H

/* Random notes:
 *
 * The parser does not process frame data in any way and does not read it into
 * the queue. The app should read it via mkv_ReadData if it is interested.
 *
 * The code here is 64-bit clean and was tested on FreeBSD/sparc 64-bit big endian
 * system
 */

#ifdef MPDLLBUILD
#define X __declspec(dllexport)
#else
#ifdef MPDLL
#define X __declspec(dllimport)
#pragma comment(lib,"MatroskaParser")
#else
#define X
#endif
#endif

#define MATROSKA_COMPRESSION_SUPPORT
#define MATROSKA_INTEGER_ONLY

#ifdef __cplusplus
extern "C" {
#endif

/* MKFLOATing point */
#ifdef MATROSKA_INTEGER_ONLY
typedef struct {
  int64_t       v;
} MKFLOAT;
#else
typedef double  MKFLOAT;
#endif

/* generic I/O */
struct InputStream {
  /* read bytes from stream */
  int         (*read)(struct InputStream *cc,uint64_t pos,void *buffer,int count);
  /* scan for a four byte signature, bytes must be nonzero */
  int64_t    (*scan)(struct InputStream *cc,uint64_t start,unsigned signature);
  /* get cache size, this is used to cap readahead */
  unsigned    (*getcachesize)(struct InputStream *cc);
  /* fetch last error message */
  const char *(*geterror)(struct InputStream *cc);
  /* memory allocation */
  void        *(*memalloc)(struct InputStream *cc,size_t size);
  void        *(*memrealloc)(struct InputStream *cc,void *mem,size_t newsize);
  void        (*memfree)(struct InputStream *cc,void *mem);
  /* zero return causes parser to abort open */
  int         (*progress)(struct InputStream *cc,uint64_t cur,uint64_t max);
  /* get file size, optional, can be NULL or return -1 if filesize is unknown */
  int64_t    (*getfilesize)(struct InputStream *cc);
};

typedef struct InputStream InputStream;

/* matroska file */
struct MatroskaFile; /* opaque */

typedef struct MatroskaFile MatroskaFile;

#define COMP_ZLIB   0
#define COMP_BZIP   1
#define COMP_LZO1X  2
#define COMP_PREPEND 3

#define TT_VIDEO    1
#define TT_AUDIO    2
#define TT_SUB      17

struct TrackInfo {
  unsigned char   Number;
  unsigned char   Type;
  unsigned char   TrackOverlay;
  uint64_t        UID;
  uint64_t        MinCache;
  uint64_t        MaxCache;
  uint64_t        DefaultDuration;
  MKFLOAT         TimecodeScale;
  void            *CodecPrivate;
  unsigned        CodecPrivateSize;
  unsigned        CompMethod;
  void            *CompMethodPrivate;
  unsigned        CompMethodPrivateSize;
  unsigned        MaxBlockAdditionID;

  unsigned int  Enabled:1;
  unsigned int  Default:1;
  unsigned int  Lacing:1;
  unsigned int  DecodeAll:1;
  unsigned int  CompEnabled:1;

  union {
    struct {
      unsigned char   StereoMode;
      unsigned char   DisplayUnit;
      unsigned char   AspectRatioType;
      unsigned int    PixelWidth;
      unsigned int    PixelHeight;
      unsigned int    DisplayWidth;
      unsigned int    DisplayHeight;
      unsigned int    CropL, CropT, CropR, CropB;
      unsigned int    ColourSpace;
      MKFLOAT         GammaValue;

      unsigned int  Interlaced:1;
    } Video;
    struct {
      MKFLOAT         SamplingFreq;
      MKFLOAT         OutputSamplingFreq;
      unsigned char   Channels;
      unsigned char   BitDepth;
    } Audio;
  } AV;

  /* various strings */
  char                  *Name;
  char                  Language[4];
  char                  *CodecID;
};

typedef struct TrackInfo  TrackInfo;

struct SegmentInfo {
  char                  UID[16];
  char                  PrevUID[16];
  char                  NextUID[16];
  char                  *Filename;
  char                  *PrevFilename;
  char                  *NextFilename;
  char                  *Title;
  char                  *MuxingApp;
  char                  *WritingApp;
  uint64_t              TimecodeScale;
  uint64_t              Duration;
  int64_t               DateUTC;
  char                  DateUTCValid;
};

typedef struct SegmentInfo SegmentInfo;

struct Attachment {
  uint64_t              Position;
  uint64_t              Length;
  uint64_t              UID;
  char                  *Name;
  char                  *Description;
  char                  *MimeType;
};

typedef struct Attachment Attachment;

struct ChapterDisplay {
  char                  *String;
  char                  Language[4];
  char                  Country[4];
};

struct ChapterCommand {
  unsigned              Time;
  unsigned              CommandLength;
  void                  *Command;
};

struct ChapterProcess {
  unsigned              CodecID;
  unsigned              CodecPrivateLength;
  void                  *CodecPrivate;
  unsigned              nCommands,nCommandsSize;
  struct ChapterCommand *Commands;
};

struct Chapter {
  uint64_t              UID;
  uint64_t              Start;
  uint64_t              End;

  unsigned              nTracks,nTracksSize;
  uint64_t              *Tracks;
  unsigned              nDisplay,nDisplaySize;
  struct ChapterDisplay *Display;
  unsigned              nChildren,nChildrenSize;
  struct Chapter        *Children;
  unsigned              nProcess,nProcessSize;
  struct ChapterProcess *Process;

  char                  SegmentUID[16];

  unsigned int  Hidden:1;
  unsigned int  Enabled:1;

  // Editions
  unsigned int  Default:1;
  unsigned int  Ordered:1;
};

typedef struct Chapter  Chapter;

#define TARGET_TRACK      0
#define TARGET_CHAPTER    1
#define TARGET_ATTACHMENT 2
#define TARGET_EDITION    3
struct Target {
  uint64_t          UID;
  unsigned          Type;
};

struct SimpleTag {
  char              *Name;
  char              *Value;
  char              Language[4];
  unsigned          Default:1;
};

struct Tag {
  unsigned          nTargets,nTargetsSize;
  struct Target     *Targets;

  unsigned          nSimpleTags,nSimpleTagsSize;
  struct SimpleTag  *SimpleTags;
};

typedef struct Tag  Tag;

/* Open a matroska file
 * io pointer is recorded inside MatroskaFile
 */
X MatroskaFile  *mkv_Open(/* in */ InputStream *io,
                        /* out */ char *err_msg,
                        /* in */  unsigned msgsize);

#define MKVF_AVOID_SEEKS    1 /* use sequential reading only */

X MatroskaFile  *mkv_OpenEx(/* in */  InputStream *io,
                          /* in */  uint64_t base,
                          /* in */  unsigned flags,
                          /* out */ char *err_msg,
                          /* in */  unsigned msgsize);

/* Close and deallocate mf
 * NULL pointer is ok and is simply ignored
 */
X void          mkv_Close(/* in */ MatroskaFile *mf);

/* Fetch the error message of the last failed operation */
X const char    *mkv_GetLastError(/* in */ MatroskaFile *mf);

/* Get file information */
X SegmentInfo   *mkv_GetFileInfo(/* in */ MatroskaFile *mf);

/* Get track information */
X unsigned int  mkv_GetNumTracks(/* in */ MatroskaFile *mf);
X TrackInfo     *mkv_GetTrackInfo(/* in */ MatroskaFile *mf,/* in */ unsigned track);

/* chapters, tags and attachments */
X void        mkv_GetAttachments(/* in */   MatroskaFile *mf,
                                 /* out */  Attachment **at,
                                 /* out */  unsigned *count);
X void        mkv_GetChapters(/* in */  MatroskaFile *mf,
                              /* out */ Chapter **ch,
                              /* out */ unsigned *count);
X void        mkv_GetTags(/* in */  MatroskaFile *mf,
                          /* out */ Tag **tag,
                          /* out */ unsigned *count);

X uint64_t   mkv_GetSegmentTop(MatroskaFile *mf);

/* Seek to specified timecode,
 * if timecode is past end of file,
 * all tracks are set to return EOF
 * on next read
 */
#define MKVF_SEEK_TO_PREV_KEYFRAME          1
#define MKVF_SEEK_TO_PREV_KEYFRAME_STRICT   2

X void        mkv_Seek(/* in */ MatroskaFile *mf,
                       /* in */ uint64_t timecode /* in ns */,
                       /* in */ unsigned flags);

X void        mkv_SkipToKeyframe(MatroskaFile *mf);

X uint64_t   mkv_GetLowestQTimecode(MatroskaFile *mf);

X int         mkv_TruncFloat(MKFLOAT f);

/*************************************************************************
 * reading data, pull model
 */

/* frame flags */
#define FRAME_UNKNOWN_START   0x00000001
#define FRAME_UNKNOWN_END     0x00000002
#define FRAME_KF              0x00000004
#define FRAME_GAP             0x00800000
#define FRAME_STREAM_MASK     0xff000000
#define FRAME_STREAM_SHIFT    24

/* This sets the masking flags for the parser,
 *  masked tracks [with 1s in their bit positions]
 *  will be ignored when reading file data.
 * This call discards all parsed and queued frames
 */
X void        mkv_SetTrackMask(/* in */ MatroskaFile *mf,/* in */ unsigned int mask);

/* Read one frame from the queue.
 * mask specifies what tracks to ignore.
 * Returns -1 if there are no more frames in the specified
 * set of tracks, 0 on success
 */
X int         mkv_ReadFrame(/* in */  MatroskaFile *mf,
                            /* in */  unsigned int mask,
                            /* out */ unsigned int *track,
                            /* out */ uint64_t *StartTime /* in ns */,
                            /* out */ uint64_t *EndTime /* in ns */,
                            /* out */ uint64_t *FilePos /* in bytes from start of file */,
                            /* out */ unsigned int *FrameSize /* in bytes */,
                            /* out */ unsigned int *FrameFlags);

#ifdef MATROSKA_COMPRESSION_SUPPORT
/* Compressed streams support */
struct CompressedStream;

typedef struct CompressedStream CompressedStream;

X CompressedStream  *cs_Create(/* in */ MatroskaFile *mf,
                             /* in */   unsigned tracknum,
                             /* out */  char *errormsg,
                             /* in */   unsigned msgsize);
X void            cs_Destroy(/* in */ CompressedStream *cs);

/* advance to the next frame in matroska stream, you need to pass values returned
 * by mkv_ReadFrame */
X void            cs_NextFrame(/* in */ CompressedStream *cs,
                               /* in */ uint64_t pos,
                               /* in */ unsigned size);

/* read and decode more data from current frame, return number of bytes decoded,
 * 0 on end of frame, or -1 on error */
X int             cs_ReadData(CompressedStream *cs,char *buffer,unsigned bufsize);

/* return error message for the last error */
X const char      *cs_GetLastError(CompressedStream *cs);
#endif

#ifdef __cplusplus
}
#endif

#undef X

#endif
