#ifndef dvd_vm_h
#define dvd_vm_h

#include "dvd_desc.h"

#define DVDREG_PREF_MENU_LANG       0 // ISO 539 code
#define DVDREG_AUDIO_STREAM_NR      1 // 0-7 or 15 for none
#define DVDREG_SUBS_STREAM_NR       2 // 0-31, 63 (none), 63 (forced) + bit 6 set if display
#define DVDREG_ANGLE_NR             3 // 1-9
#define DVDREG_TITLE_NR             4 // 1-99, -> VMG table of titles
#define DVDREG_VTS_TITLE_NR         5 // 1-99
#define DVDREG_PROGRAM_CHAIN_NR     6 // 1-32767
#define DVDREG_CHAPTER_NR           7 // 1-99
#define DVDREG_HIGHLIGHT_BUTTON_NR  8 // btn nr << 10
#define DVDREG_NAVIGATION_TIMER     9 // in seconds
#define DVDREG_PGC_FRO_NAV_TIMER   10 // 1-32767
#define DVDREG_KARAOKE_MIXING_MODE 11
#define DVDREG_PARENTAL_COUNTRY    12 // ISO 539 code
#define DVDREG_PARENTAL_LEVEL      13 // 1-8 or 15 for none
#define DVDREG_VIDEO_MODES         14 // prefered aspect ratio & current mode
#define DVDREG_AUDIO_CAPABILITIES  15
#define DVDREG_PREF_AUDIO_LANG     16 // ISO 539 code
#define DVDREG_PREF_AUDIO_LANG_EXT 17
#define DVDREG_PREF_SUBS_LANG      18 // ISO 539 code
#define DVDREG_PREF_SUBS_LANG_EXT  19
#define DVDREG_PLAYER_REGIONS_MASK 20

#define DVDVM_EXECUTE       0x01
#define DVDVM_LOGN          0x02
#define DVDVM_SHUFFLE_MODE  0x04 // Marks that current PGC works in shuffle mode
#define DVDVM_HAS_CELL      0x80 // Marks that VM has treated at least one cell (must not generate DVDEV_POS_CHANGE on first one)

#define DVDEV_CELL_CHANGE   0x01
#define DVDEV_AUDIO_CHANGE  0x02
#define DVDEV_SUBS_CHANGE   0x04
#define DVDEV_ANGLE_CHANGE  0x08
#define DVDEV_POS_CHANGE    0x10
#define DVDEV_EXIT          0x20
// internal
#define DVDEV_JUMP          0x100
#define DVDEV_JUMP_IN_PGC   0x200
#define DVDEV_RESUME        0x400

typedef struct
{
  uint32_t cellCount; // monotonic increasing cell count
  uint32_t workMode;
  const dvd_desc_t* dvdDesc;
  const dvd_vmg_ifo_t* vmg;
  const char* logPrefix;
  uint32_t events;
  char lang[4];
  dvd_pos_t pos;
  dvd_pos_t jump;
  dvd_pos_t resume;
  struct
  {
    uint32_t titleNr;
    uint32_t vtsTitleNr;
    uint32_t chapterNr;
  } chapter;
  struct
  {
    uint32_t menuId;
  } menu;
  struct
  {
    uint32_t counterMode; // 1 bit per register
    uint32_t modified; // 1 bit per register
    uint16_t regs[16];
  } general;
  struct
  {
    uint32_t modified; // 1 bit per register
    uint16_t regs[24];
  } system;
  uint32_t shuffleCount;
  uint32_t shuffleList[128];
} dvd_vm_state_t;

void dvd_vm_reset(dvd_vm_state_t* vm, const dvd_desc_t* dvdDesc, int flags);
void dvd_vm_setLogState(dvd_vm_state_t* vm, int on);
void dvd_vm_setLogPrefix(dvd_vm_state_t* vm, const char* logPrefix);
void dvd_vm_nextCell(dvd_vm_state_t* vm);
void dvd_vm_action(dvd_vm_state_t* vm, uint8_t* cmd);

// Just for simulation
void dvd_vm_playCmds(dvd_vm_state_t* vm, int nrCommands, const uint8_t* cmdlist);
int dvd_vm_setFirstPlay(dvd_vm_state_t* vm);
int dvd_vm_setMenu(dvd_vm_state_t* vm, int vtsNr, int menuId);
int dvd_vm_setChapter(dvd_vm_state_t* vm, int titleNr, int chapterNr, int playPreCmds);
int dvd_vm_setCellNr(dvd_vm_state_t* vm, int cellNr);

#endif
