/* Copyright 2008 David Moews (dmoews@fastmail.fm)

This program 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 2 of the License, or (at your option) any later version. 
This program 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, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
#ifndef ISO9660_INCL

#define ISO9660_INCL

#include <string>
#include <vector>
#include <iosfwd>

#include "Types.h"

struct Iso9660Date
{
  int year, month, day, hour, minute, second, hundredths, offset;
};

bool operator ==(const Iso9660Date &a, const Iso9660Date &b);
bool operator !=(const Iso9660Date &a, const Iso9660Date &b);

u32 get_721(const char *p_c);
u32 get_722(const char *p_c);

// Returns succ/failure
bool get_723(const char *p_c, u32 *p_i);

u32 get_731(const char *p_c);
u32 get_732(const char *p_c);

// Returns succ/failure
bool get_733(const char *p_c, u32 *p_i);

// Get date recorded according to 8.4.26.1
// Returns succ/failure
bool get_numeric_date(const char *p_c, Iso9660Date *p_d);

struct DirectoryContents;   // fwd decl

struct XAR
{
  u32 owner_id, group_id;
  u32 permissions;
  Iso9660Date creation_time, modification_time, expiration_time, effective_time;
  unsigned char record_format, record_attributes;
  u32 record_length;
  char system_identifier[32];
  char system_use[64];
  std::string escape_sequences;
  std::string application_use;
};

struct DirectoryRecord
{
  unsigned char extended_attributes_length;   // XAR length in blocks
  u32 start;                        
  u32 length;
  Iso9660Date recording_time;
  unsigned char flags, file_unit_size, interleave_gap;
  u32 volume_sequence_number;
  std::string name; 
  std::string system_use;

  std::string xar;                // XAR data, if read in
  XAR *p_xar;                     // Pointer to parsed XAR, if present

  DirectoryContents *p_contents;
  // List of directory records this file contains, if it is a directory.
  // This will be NULL for the 00 (.) and 01 (..) directory records.

  // Whether this is root, ., or .. .
  bool is_root, is_dot, is_dotdot;

  bool is_directory() const { return (flags & 2u) != 0u; }
};

// Doesn't compare `system use', XAR, contents, flags, or names
bool equal_directory_records
      (const DirectoryRecord &a, const DirectoryRecord &b);

struct DirectoryContents
{
  std::vector<DirectoryRecord> records;
};

struct VolumePartition
{
  char system_identifier[32];
  char volume_partition_identifier[32];
  u32 start;
  u32 size;
  char system_use[1960];
};

struct BootRecord
{
  char boot_system_identifier[32];
  char boot_identifier[32];
  char boot_system_use[1977];
};

struct VolumeInfo
{
  u32 volume_space_size;
  u32 volume_set_size;
  u32 volume_sequence_number;
  u32 logical_block_size;
  u32 logical_sector_size;
  int num_descriptor_sectors;
  Iso9660Date creation_time;
  Iso9660Date modification_time;
  Iso9660Date expiration_time;
  Iso9660Date effective_time;
};

bool operator ==(const VolumeInfo &a, const VolumeInfo &b);
bool operator !=(const VolumeInfo &a, const VolumeInfo &b);

struct VolumeDescriptor
{
  bool is_primary;
  int number_recorded;
  bool volume_flags;
  char system_identifier[32];
  char volume_identifier[32];
  char escape_sequences[32];
  u32 path_table_size;
  u32 path_l_location[2];
  u32 path_m_location[2];
  DirectoryRecord root_directory;

  char volume_set_identifier[128]; 
  char publisher_identifier[128];
  char data_preparer_identifier[128];
  char application_identifier[128];

  char copyright_file_identifier[37];
  char abstract_file_identifier[37];
  char bibliographic_file_identifier[37];

  char application_use[512];
};

// Shallow compare 
bool operator ==(const VolumeDescriptor &a, const VolumeDescriptor &b);
bool operator !=(const VolumeDescriptor &a, const VolumeDescriptor &b);

struct Volume
{
  VolumeInfo volume_info;
  std::vector<VolumeDescriptor> volume_descriptors;
  std::vector<BootRecord> boot_records;
  std::vector<VolumePartition> volume_partitions;
};

// Takes string with raw XAR data as input.
// Returns pointer to new XAR on success, or NULL on failure.
XAR *parse_xar(const std::string &s);

inline const char *yes_no(bool b) { return (b ? "yes" : "no"); }

std::ostream &operator <<(std::ostream &o, const Iso9660Date &d);

bool read_iso9660_header(std::istream &i, u32 sector_size, Volume *p_v);

bool read_iso9660(std::istream &i, u32 sector_size, Volume *p_v);

void print_iso9660(std::ostream &o, const Volume &v, bool print_hierarchies);

#endif
// Returns pointer to new XAR record, or NULL on failure.
