#ifndef dvd_ifo_h
#define dvd_ifo_h

#include <stdint.h>
#include "dvd_commons.h"
#include "ka_error.h"

//--------------
// VMG: TT_SRPT

#define DVD_TITLE_TYPE_NON_SEQUENTIAL 0x40
#define DVD_TITLE_TYPE_JLC_MASK       0x3C
#define DVD_TITLE_TYPE_JLC_IN_CELL    0x20
#define DVD_TITLE_TYPE_JLC_IN_PREPOST 0x10
#define DVD_TITLE_TYPE_JLC_IN_BUTTON  0x80
#define DVD_TITLE_TYPE_JLC_USED       0x04
#define DVD_TITLE_TYPE_USEROP1_FLAG   0x02
#define DVD_TITLE_TYPE_USEROP0_FLAG   0x01

typedef struct
{
  uint8_t  type;
  uint8_t  nr_angles;
  uint16_t nr_chapters; // parts of title
  uint16_t parental_management_mask;
  uint8_t  vts_nr;
  uint8_t  vts_title_nr;
  uint32_t lb_vts_ifo_start; // LBA of fist sector of VTS
} dvd_title_t;

typedef struct
{
  uint16_t nr_titles;
  uint32_t size;
  dvd_title_t titles[];
} dvd_table_of_titles_t;

//--------------
// VTS_PTT_SRPT

typedef struct
{
  uint16_t program_chain_nr;
  uint16_t program_nr;
} dvd_part_t;

typedef struct
{
  uint16_t    nr_titles;
  uint16_t    nr_parts;
  uint32_t    size;
  dvd_part_t* parts; // 1 for each chapter of each title in VTS
  uint32_t    parts_indexes[];
} dvd_table_of_title_parts_t;

//-------------------------------------
// VMGM_C_ADT - VTSM_C_ADT - VTS_C_ADT

typedef struct
{
  uint16_t vob_id;
  uint8_t  cell_id;
  // relative to start of vob
  uint32_t lb_vob_start;
  uint32_t lb_vob_last;
} dvd_cell_address_t;

typedef struct
{
  uint16_t nr_cells;
  uint32_t table_size;
  dvd_cell_address_t cells[];
} dvd_cell_address_table_t;

//----------------------------------------------------
// VMGM_VOBU_ADMAP - VTSM_VOBU_ADMAP - VTS_VOBU_ADMAP

typedef struct
{
  uint16_t nr_vobus;
  // relative to start of vob
  uint32_t lb_vobu_start[];
} dvd_vobu_address_map_t;

//-------------------------------
// VMGM_PGC - VTSM_PGC - VTS_PGC

// PCG - Cell playback info

#define DVD_CELL_TYPE_MASK            0xC000
#define DVD_CELL_TYPE_NORMAL          0x0000
#define DVD_CELL_TYPE_FIRST_OF_ANGLE  0x4000
#define DVD_CELL_TYPE_MIDDLE_OF_ANGLE 0x8000
#define DVD_CELL_TYPE_LAST_OF_ANGLE   0xC000

#define DVD_CELL_BLOCK_TYPE_MASK      0x3000
#define DVD_CELL_BLOCK_TYPE_NORMAL    0x0000
#define DVD_CELL_BLOCK_TYPE_ANGLE     0x1000

#define DVD_CELL_SEAMLESS_MULTIPLEX   0x0800
#define DVD_CELL_INTERLEAVED          0x0400
#define DVD_CELL_SCR_DISCONTINUITY    0x0200
#define DVD_CELL_SEAMLESS_ANGLE       0x0100
#define DVD_CELL_VOBU_STILL_MODE      0x0040
#define DVD_CELL_RESTRICTED           0x0020
#define DVD_CELL_APP_TYPE_MASK        0x001F // Karaoke

typedef struct
{
  uint16_t category;
  uint8_t  still_time;
  uint8_t  cell_command_nr;
  dvd_time_t time;
  // lbs are relative to vts ifo start
  uint32_t lb_first_vobu_start;
  uint32_t lb_first_ilvu_last; // 0 if no angles
  uint32_t lb_last_vobu_start;
  uint32_t lb_last_vobu_last;
  // extra calculated info
  uint32_t pgc_rel_time;
  uint32_t pgc_rel_lb_size;
} dvd_cell_playback_t;

// PCG - Cell

typedef struct
{
  uint16_t vob_id;
  uint8_t  cell_id;
} dvd_cell_t;

// PCG - Command Table

typedef struct
{
  uint16_t nr_pre_cmds;
  uint16_t nr_post_cmds;
  uint16_t nr_cell_cmds;
  uint32_t table_size;
  dvd_cmd_t commands[];
} dvd_command_table_t;

// PGC - Header

#define DVD_STREAM_AVAILABLE 0x80

#define DVD_ASPECTS 4
#define DVD_ASPECT_4_3       0
#define DVD_ASPECT_WIDE      1
#define DVD_ASPECT_LETTREBOX 2
#define DVD_ASPECT_PANSCAN   3

#define DVD_PLAYBACK_RANDOM     0x80

typedef struct
{
  uint8_t nr_programs;
  uint8_t nr_cells;
  dvd_time_t time;
  uint32_t prohibited_user_ops;
  uint8_t audio_stream[8]; // [0-7] + DVD_STREAM_AVAILABLE
  uint8_t subtitle_stream[32][DVD_ASPECTS]; // [0-31] + DVD_STREAM_AVAILABLE
  uint16_t next_pgcn;
  uint16_t prev_pgcn;
  uint16_t goup_pgcn; // 0xFFFF for resume
  uint8_t  playback_mode; // 0 sequential, [1-128] program count -1
                          // bit 7 0 random, 1 shuffle
  uint8_t  still_time; // 255 = infinite
  dvd_color_t color_lookup[16];
  uint16_t offset_commands;
  uint16_t offset_program_map;
  uint16_t offset_cell_playback_table;
  uint16_t offset_cell_position_table;
  dvd_command_table_t* commands;
  uint8_t*             program_cellnrs;
  dvd_cell_playback_t* cell_playbacks;
  dvd_cell_t*          cells;
  // extra calculated info
  uint32_t total_time;
  uint32_t total_lb_size;
} dvd_pgc_t;

// VMGM_LU - VTSM_LU

#define DVD_PGC_LU_ISENTRY 0x80

typedef struct
{
  uint8_t category; // menu type/title nr + DVD_PGC_LU_ISENTRY
  uint16_t parental_mask;
  uint32_t offset_pgc;
  dvd_pgc_t* pgc;
} dvd_pgc_lu_t;

typedef struct
{
  uint16_t nr_pgc_lus;
  uint32_t table_size;
  dvd_pgc_lu_t pgc_lus[];
} dvd_pgc_lu_table_t;

// VMGM_PGCI_UT - CTSM_PGCI_UT

#define DVD_MENU_FLAG_ROOT        0x80
#define DVD_MENU_FLAG_SUBPICTURE  0x40
#define DVD_MENU_FLAG_AUDIO       0x20
#define DVD_MENU_FLAG_ANGLE       0x10
#define DVD_MENU_FLAG_PARTOFTITLE 0x08

typedef struct
{
  char iso639code[3];
  uint8_t  menu_flags;
  uint32_t offset_lu;
  dvd_pgc_lu_table_t* lut;
} dvd_language_t;

typedef struct
{
  uint16_t nr_languages;
  uint32_t table_size;
  dvd_language_t languages[];
} dvd_table_of_menu_languages_t;

// VTMS_TMAPTI

#define DVD_TIME_NEXT_IS_NEW_CELL (1u << 31)

typedef struct
{
  uint8_t time_unit; // in seconds
  uint16_t nr_entries;
  uint32_t lb_offsets[]; // offset of vobu corresponding to time
} dvd_timemap_t;

typedef struct
{
  uint32_t nr_pgcs;
  uint32_t table_size;
  dvd_timemap_t times[];
} dvd_table_of_timemaps_t;

#define dvd_parental_management_masks_t uint32_t
#define dvd_vts_attributes_t uint32_t
#define dvd_text_data_t char

// VMG IFO

typedef struct
{
  uint16_t video_attributes;
  uint16_t nr_audio_streams;
  uint8_t  audio_attributes[8][8];
  uint16_t nr_subtitle_streams;
  uint8_t  subtitle_attributes[32][6];
  uint8_t  multichannel_extension[8][24];
} dvd_attributes_t;


typedef struct
{
  uint32_t set_lba;     // logical block address of this IFO
  uint32_t set_lb_size; // size in blocks of the set : IFO + VTS + BUP
  uint32_t ifo_lb_size; // size in blocks IFO
  uint32_t category;
  uint16_t version;
  uint16_t nr_volumes;
  uint16_t cur_volume;
  uint8_t  side_id;
  uint16_t nr_vts;
  char provider[32];
  char vmg_pos[8];
  uint32_t mat_size; // nr of bytes used by the mat (from start of IFO)
  uint32_t offset_first_play_program_chain;

  uint32_t lb_offset_menu_vob;
  uint32_t lb_offset_table_of_titles; // is within ifo
  uint32_t lb_offset_menu_languages_table;
  uint32_t lb_offset_parental_management_masks;
  uint32_t lb_offset_vts_attributes;
  uint32_t lb_offset_text_data;
  uint32_t lb_offset_menu_cell_address_table;
  uint32_t lb_offset_menu_vobu_address_map;

  dvd_attributes_t menu_attributes;

  dvd_pgc_t*                       first_play_pgc;
  dvd_table_of_titles_t*           table_of_titles;
  dvd_table_of_menu_languages_t*   menu_languages;
  dvd_parental_management_masks_t* parental_management_masks;
  dvd_vts_attributes_t*            vts_attributes;
  dvd_text_data_t*                 text_data;
  dvd_cell_address_table_t*        menu_cells;
  dvd_vobu_address_map_t*          menu_vobus;
} dvd_vmg_ifo_t;

// VTS IFO

typedef struct
{
  uint32_t set_lba;     // logical block address of this IFO
  uint32_t set_lb_size; // size in blocks of the set : IFO + VTS + BUP
  uint32_t ifo_lb_size; // size in blocks IFO
  uint16_t version;
  uint32_t category;
  uint32_t mat_size; // nr of bytes used by the mat (from start of IFO)

  uint32_t lb_offset_menu_vob;
  uint32_t lb_offset_title_vob;
  uint32_t lb_offset_title_parts_table;
  uint32_t lb_offset_title_program_chains_table;
  uint32_t lb_offset_menu_languages_table;
  uint32_t lb_offset_time_map;
  uint32_t lb_offset_menu_cell_address_table;
  uint32_t lb_offset_menu_vobu_address_map;
  uint32_t lb_offset_title_cell_address_table;
  uint32_t lb_offset_title_vobu_address_map;

  dvd_attributes_t menu_attributes;
  dvd_attributes_t title_attributes;

  dvd_table_of_title_parts_t*      title_parts;
  dvd_pgc_lu_table_t*              title_pgcs;
  dvd_table_of_menu_languages_t*   menu_languages;
  dvd_table_of_timemaps_t*         time_map;
  dvd_cell_address_table_t*        menu_cells;
  dvd_vobu_address_map_t*          menu_vobus;
  dvd_cell_address_table_t*        title_cells;
  dvd_vobu_address_map_t*          title_vobus;
} dvd_vts_ifo_t;

int dvd_read_vmg_ifo(ka_error_t* pErrorBlock, dvd_vmg_ifo_t** pvmg, const uint8_t* buf, const uint8_t* end);
void dvd_free_vmg_info(dvd_vmg_ifo_t** pvmg);
int dvd_read_vts_ifo(ka_error_t* pErrorBlock, dvd_vts_ifo_t** pvts, int id, const uint8_t* buf, const uint8_t* end);
void dvd_free_vts_info(dvd_vts_ifo_t** pvts);

#endif
