#ifndef ka_demux_h
#define ka_demux_h

#include "ka_buffer.h"
#include "ka_input.h"
#include "ka_stack.h"
#include "ka_vars.h"
#include "ka_error.h"
#include "ka_subtitles.h"
#include "ka_vinfo.h"

typedef struct ka_demux_s ka_demux_t;

typedef void  (*ka_demux_FNdelete)(ka_demux_t** pdemux);
typedef void  (*ka_demux_FNreset)(ka_demux_t* pdemux, ka_stack_t* stack);
typedef int   (*ka_demux_FNclear)(ka_demux_t* pdemux, ka_stack_t* stack);
typedef int   (*ka_demux_FNparse)(ka_demux_t* pdemux, ka_stack_t* stack);
#define ka_demux_error ((const uint8_t*) -2)
typedef const uint8_t* (*ka_demux_FNpush)(ka_demux_t* pdemux, ka_stack_t* stack
                                        , const uint8_t* buf, const uint8_t* end);
typedef struct
{
  uint64_t curPos;
  uint64_t endPos;
  uint32_t titleNr;
  uint32_t chapterNr;
} ka_demux_pos_t;
typedef void  (*ka_demux_FNgetPos)(const ka_demux_t* pdemux, ka_buffer_t* pbuffer, ka_demux_pos_t* pos);
typedef int64_t (*ka_demux_FNsetPos)(ka_demux_t* pdemux, int64_t pos);
typedef int   (*ka_demux_FNsetChapter)(ka_demux_t* pdemux, uint32_t id);
typedef int64_t (*ka_demux_FNgetChapterPos)(ka_demux_t* pdemux, uint32_t id);

typedef struct
{
  uint32_t    nrStreams;
  uint32_t    streamId;
  ka_block_id kid;
  uint32_t    channels;
  char        lang[4];
  const char* type;
} ka_demux_audio_info_t;
typedef void  (*ka_demux_FNgetAudioInfo)(const ka_demux_t* pdemux, int32_t streamId, ka_demux_audio_info_t *pInfo);
typedef int   (*ka_demux_FNselectAudio)(ka_demux_t* pdemux, uint32_t streamId);

typedef struct
{
  uint32_t    nrStreams;
  uint32_t    streamId;
  ka_block_id kid;
  char        lang[4];
  const char* type;
} ka_demux_subtitle_info_t;
typedef void  (*ka_demux_FNgetSubsInfo)(const ka_demux_t* pdemux, int32_t streamId, ka_demux_subtitle_info_t* pInfo);
typedef int   (*ka_demux_FNselectSubs)(ka_demux_t* pdemux, uint32_t streamId);

typedef struct
{
  uint32_t    nrAngles;
  uint32_t    angleId;
} ka_demux_angle_info_t;
typedef void  (*ka_demux_FNgetAngle)(const ka_demux_t* pdemux, ka_demux_angle_info_t* pInfo);
typedef int   (*ka_demux_FNsetAngle)(ka_demux_t* pdemux, uint32_t angle);

#define ka_fill_normal        0 // More input to follow
#define ka_fill_completed     1 // End of input (for section)
typedef int   (*ka_demux_FNfillBuffer)(ka_demux_t* pdemux, ka_buffer_t* pbuffer, int* pdone);

typedef const void* (*ka_demux_FNparams)(const ka_demux_t* pdemux, uint32_t typ);
typedef int (*ka_demux_FNgetWaitTime)(ka_demux_t* pdemux, uint32_t rtc);
typedef void (*ka_demux_FNupdateVInfo)(ka_demux_t* pdemux, ka_vinfo_t* info);

typedef enum
{ KA_DEMUX_SECTION_DONE_DECODE  = 1
, KA_DEMUX_SECTION_DONE_DISPLAY = 2
} ka_demux_section_done;

typedef enum
{ KA_DEMUX_SECTION_STATE_ERROR         = -1
, KA_DEMUX_SECTION_STATE_WAIT          =  0
, KA_DEMUX_SECTION_STATE_NEXTSECTION   =  1
, KA_DEMUX_SECTION_STATE_DISCONTINUITY =  2
, KA_DEMUX_SECTION_STATE_EXIT          =  3
} ka_demux_section_state;

typedef ka_demux_section_state (*ka_demux_FNsectionDone)(ka_demux_t* pdemux, ka_demux_section_done status);
#define ka_nav_key_used        0x01
#define ka_nav_redraw_frame    0x02
#define ka_nav_discontinuity   0x04
#define ka_nav_audio_change    0x08
#define ka_nav_subtitle_change 0x10
#define ka_nav_angle_change    0x20

typedef int   (*ka_demux_FNnavigate)(ka_demux_t* pdemux, uint32_t rtc, int key);
typedef int   (*ka_demux_FNsubtitleRect)(ka_demux_t* pdemux, uint32_t rtc, ka_sub_rect* rect, int step);

typedef struct
{
  const char*              name;
  ka_demux_FNdelete        FNdelete;
  ka_demux_FNreset         FNreset;
  ka_demux_FNclear         FNclear;
  ka_demux_FNparse         FNparse;
  ka_demux_FNgetPos        FNgetPos;
  ka_demux_FNsetPos        FNsetPos;
  ka_demux_FNsetChapter    FNsetChapter;
  ka_demux_FNgetChapterPos FNgetChapterPos;
  ka_demux_FNgetAudioInfo  FNgetAudioInfo;
  ka_demux_FNselectAudio   FNselectAudio;
  ka_demux_FNgetSubsInfo   FNgetSubsInfo;
  ka_demux_FNselectSubs    FNselectSubs;
  ka_demux_FNgetAngle      FNgetAngle;
  ka_demux_FNsetAngle      FNsetAngle;
  ka_demux_FNfillBuffer    FNfillBuffer;
  ka_demux_FNpush          FNpush;
  ka_demux_FNparams        FNparams;
  ka_demux_FNgetWaitTime   FNgetWaitTime;
  ka_demux_FNsectionDone   FNsectionDone;
  ka_demux_FNnavigate      FNnavigate;
  ka_demux_FNsubtitleRect  FNsubtitleRect;
  ka_demux_FNupdateVInfo   FNupdateVInfo;
} kav_demux_t;

typedef struct
{
  // input, global info
  ka_error_t*        pErrorBlock;
  const char*        pFilename;
  ka_stack_t*        pStack;
  ka_vars_t*         pVars;
  // input, for file selectors
  ka_input_t*        pInput;
  ka_buffer_t*       pBuffer;
} ka_demux_params_t;

typedef enum
{ ka_demux_select_error
, ka_demux_select_wait
, ka_demux_select_none
, ka_demux_select_found
} ka_demux_select;

// returns: error -1, no demux 0, demux 1
typedef ka_demux_select (*ka_demux_selector)(ka_demux_t** ppdemux, ka_demux_params_t* pParams);

// Selectors not relying on predefined input method
ka_demux_select demux_selector_dvd(ka_demux_t** ppdemux, ka_demux_params_t* pParams);

// Selectors relying on preopened input file and circular buffer
ka_demux_select demux_selector_mpeg_dvd(ka_demux_t** ppdemux, ka_demux_params_t* pParams);
ka_demux_select demux_selector_scanForMpeg(ka_demux_t** ppdemux, ka_demux_params_t* pParams);
ka_demux_select demux_selector_avi(ka_demux_t** ppdemux, ka_demux_params_t* pParams);

struct ka_demux_s
{
  const kav_demux_t*   vptr;
  ka_vars_t*           vars;
  ka_error_t*          pErrorBlock;
  uint32_t             demuxSection;
  uint32_t             demuxSectionPts;
  uint64_t             unstackedInputPos;
};

#endif
