/*
 * (C) 2003-2006 Gabest
 * (C) 2006-2017 see Authors.txt
 *
 * This file is part of MPC-BE.
 *
 * MPC-BE is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * MPC-BE is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#pragma once

#include <stdint.h>

class CIfo
{
public:
	CIfo();

	bool OpenFile(LPCWSTR strFile);
	bool SaveFile(LPCWSTR strFile);
	bool RemoveUOPs();

	~CIfo(void);

private :
#pragma pack(push, 1)
	struct pgci_sub_t {
		uint16_t id    : 16; // Language
		uint16_t       : 16; // don't know
		uint32_t start : 32; // Start of unit
	};

	struct dvd_time_t {
		uint8_t hour;
		uint8_t minute;
		uint8_t second;
		uint8_t frame_u; // The two high bits are the frame rate.
	};

	typedef uint8_t command_data_t[8];
#define COMMAND_DATA_SIZE 8

	struct pgc_command_tbl_t { // PGC Command Table
		uint16_t nr_of_pre;
		uint16_t nr_of_post;
		uint16_t nr_of_cell;
		uint16_t tbl_len;
		command_data_t *pre_commands;
		command_data_t *post_commands;
		command_data_t *cell_commands;
	};
#define PGC_COMMAND_TBL_SIZE 8

	typedef uint8_t pgc_program_map_t;

	struct ifo_pgci_caddr_t { // Cell Playback Information
		uint8_t chain_info:8; // 0x5e 0xde(2 angles, no overlay), 0x5f 0x9f 0x9f 0xdf(4 angles overlay), 0x2 0xa 0x8(1 angle)
		uint8_t foo;          // parental control ??
		uint8_t still_time;
		uint8_t cell_cmd;

		dvd_time_t playback_time;
		uint32_t vobu_start;  // 1st vobu start
		uint32_t ilvu_end;
		uint32_t vobu_last_start;
		uint32_t vobu_last_end;
	};

	struct ifo_pgc_cpos_t { // Cell Position Information
		uint16_t vob_id  : 16; // Video Object Identifier
		uint8_t  foo     : 8;  // Unknown
		uint8_t  cell_id : 8;  // Cell Identifier
	};

#ifndef CLUT_T
#define CLUT_T

	struct clut_t { // CLUT == Color LookUp Table
		uint8_t foo : 8;    // UNKNOWN: 0x00?
		uint8_t y   : 8;
		uint8_t cr  : 8;
		uint8_t cb  : 8;
	};
#endif

	struct audio_status_t { // Audio Status
#if BYTE_ORDER == BIG_ENDIAN
		uint8_t available : 1;
		uint8_t link      : 7;
#else
		uint8_t link      : 7;
		uint8_t available : 1;
#endif
		uint8_t foo       : 8; // UNKNOWN
	};

	struct subp_status_t { // Subpicture status
#if BYTE_ORDER == BIG_ENDIAN
		uint8_t available : 1;
		uint8_t format4_3 : 7;
#else
		uint8_t format4_3 : 7;
		uint8_t available : 1;
#endif
		uint8_t wide      : 8;
		uint8_t letter    : 8;
		uint8_t pan       : 8;
	};

	struct pgc_t { // Program Chain Information
		uint16_t zero_1;
		uint8_t  nr_of_programs;
		uint8_t  nr_of_cells;
		dvd_time_t playback_time;
		uint32_t prohibited_ops;    // New type?
		audio_status_t audio_status[8];
		subp_status_t subp_status[32];
		uint16_t next_pgc_nr;
		uint16_t prev_pgc_nr;
		uint16_t goup_pgc_nr;
		uint8_t  still_time;
		uint8_t  pg_playback_mode;
		clut_t   clut[16];
		uint16_t pgc_command_tbl_offset;
		uint16_t pgc_program_map_offset;
		uint16_t cell_playback_tbl_offset;
		uint16_t cell_position_tbl_offset;
		pgc_command_tbl_t *pgc_command_tbl;
		pgc_program_map_t *pgc_program_map;
		ifo_pgci_caddr_t  *cell_playback_tbl;
		ifo_pgc_cpos_t    *cell_position_tbl;
	};
#define PGC_SIZE 236

	struct ifo_hdr_t {
		uint16_t num : 16; // number of entries
		uint16_t     : 16; // UNKNOWN
		uint32_t len : 32; // length of table
	};

	struct lu_sub_t {
#if BYTE_ORDER == BIG_ENDIAN
		uint16_t foo1   : 4;  // don't know
		uint8_t menu_id : 4;  // 0=off, 3=root, 4=spu, 5=audio, 6=angle, 7=ptt
#else
		uint8_t menu_id : 4;  // 0=off, 3=root, 4=spu, 5=audio, 6=angle, 7=ptt
		uint16_t foo1   : 4;  // don't know
#endif
		uint16_t foo2   : 8;  // don't know
		uint16_t bar    : 16; // don't know
		uint32_t start  : 32; // Start of unit
	};
#pragma pack(pop)

	BYTE*       m_pBuffer;
	DWORD       m_dwSize;

	ifo_hdr_t*  m_pPGCI;
	ifo_hdr_t*  m_pPGCIT;

	bool        IsVTS();
	bool        IsVMG();

	pgc_t*      GetFirstPGC();
	pgc_t*      GetPGCI(const int title, const ifo_hdr_t* hdr);
	int         GetMiscPGCI(ifo_hdr_t *hdr, int title, uint8_t **ptr);
	void        RemovePgciUOPs(uint8_t *ptr);
};
