#pragma once

typedef struct {
	char year[4];
	char month[2];
	char day[2];
	char hour[2];
	char minute[2];
	char second[2];
	char hundredths[2];
	int8_t timezone;
} __attribute__((packed)) iso_9660_datetime_t;

typedef struct {
	uint8_t year;
	uint8_t month;
	uint8_t day;
	uint8_t hour;
	uint8_t minute;
	uint8_t second;
	int8_t timezone;
} __attribute__((packed)) iso_9660_rec_date_t;

typedef struct {
	uint8_t length;
	uint8_t ext_length;

	uint32_t extent_start_LSB;
	uint32_t extent_start_MSB;

	uint32_t extent_length_LSB;
	uint32_t extent_length_MSB;

	iso_9660_rec_date_t record_date;

	uint8_t flags;
	uint8_t interleave_units;
	uint8_t interleave_gap;

	uint16_t volume_seq_LSB;
	uint16_t volume_seq_MSB;

	uint8_t name_len;
	char name[];
} __attribute__((packed)) iso_9660_directory_entry_t;

typedef struct {
	uint8_t type; /* 0x01 */
	char id[5]; /* CD001 */

	uint8_t version;
	uint8_t _unused0;

	char system_id[32];
	char volume_id[32];

	uint8_t _unused1[8];

	uint32_t volume_space_LSB;
	uint32_t volume_space_MSB;

	uint8_t _unused2[32];

	uint16_t volume_set_LSB;
	uint16_t volume_set_MSB;

	uint16_t volume_seq_LSB;
	uint16_t volume_seq_MSB;

	uint16_t logical_block_size_LSB;
	uint16_t logical_block_size_MSB;

	uint32_t path_table_size_LSB;
	uint32_t path_table_size_MSB;

	uint32_t path_table_LSB;
	uint32_t optional_path_table_LSB;

	uint32_t path_table_MSB;
	uint32_t optional_path_table_MSB;

	/* iso_9660_directory_entry_t */
	char root[34];

	char volume_set_id[128];
	char volume_publisher[128];
	char data_preparer[128];
	char application_id[128];

	char copyright_file[38];
	char abstract_file[36];
	char bibliographic_file[37];

	iso_9660_datetime_t creation;
	iso_9660_datetime_t modification;
	iso_9660_datetime_t expiration;
	iso_9660_datetime_t effective;

	uint8_t file_structure_version;
	uint8_t _unused_3;

	char application_use[];
} __attribute__((packed)) iso_9660_volume_descriptor_t;

#define ISO_SECTOR_SIZE 2048

#define FLAG_HIDDEN      0x01
#define FLAG_DIRECTORY   0x02
#define FLAG_ASSOCIATED  0x04
#define FLAG_EXTENDED    0x08
#define FLAG_PERMISSIONS 0x10
#define FLAG_CONTINUES   0x80

static int root_sector = 0;
static iso_9660_volume_descriptor_t * root = (iso_9660_volume_descriptor_t *)((uint8_t *)0x20000);
static iso_9660_directory_entry_t * dir_entry = (iso_9660_directory_entry_t *)((uint8_t *)0x20800);
static uint8_t * mod_dir = (uint8_t *)0x21000;
static uint8_t * dir_entries = (uint8_t *)(0x30000);
static struct ata_device * device = 0;

static int navigate(char * name) {
	memset(dir_entries, 2048, 0xA5);
	//print("reading from sector ");
	//print_hex(dir_entry->extent_start_LSB);
	//print("\n");
	ata_device_read_sector_atapi(device, dir_entry->extent_start_LSB, dir_entries);
	ata_device_read_sector_atapi(device, dir_entry->extent_start_LSB+1, dir_entries + 2048);
	ata_device_read_sector_atapi(device, dir_entry->extent_start_LSB+2, dir_entries + 4096);

	long offset = 0;
	while (1) {
		iso_9660_directory_entry_t * dir = (iso_9660_directory_entry_t *)(dir_entries + offset);
		if (dir->length == 0) {
			if (offset < dir_entry->extent_length_LSB) {
				offset += 1; // this->block_size - ((uintptr_t)offset % this->block_size);
				goto try_again;
			}
			break;
		}
		if (!(dir->flags & FLAG_HIDDEN)) {
			char file_name[dir->name_len + 1];
			memcpy(file_name, dir->name, dir->name_len);
			file_name[dir->name_len] = 0;
			char * s = strchr(file_name,';');
			if (s) {
				*s = '\0';
			}
#if 0
			print("Found a file: ");
			print(" Name: ");
			print(file_name); print("\n");
#endif
			if (!strcmp(file_name, name)) {
				memcpy(dir_entry, dir, sizeof(iso_9660_directory_entry_t));
				return 1;
			}
		}
		offset += dir->length;
try_again:
		if ((long)(offset) > dir_entry->extent_length_LSB) break;
	}

	return 0;
}

static void restore_root(void) {
	memcpy(dir_entry, (iso_9660_directory_entry_t *)&root->root, sizeof(iso_9660_directory_entry_t));

#if 0
	print("Root restored.");
	print("\n Entry len:  "); print_hex( dir_entry->length);
	print("\n File start: "); print_hex( dir_entry->extent_start_LSB);
	print("\n File len:   "); print_hex( dir_entry->extent_length_LSB);
	print("\n");
#endif
}

static void restore_mod(void) {
	memcpy(dir_entry, (iso_9660_directory_entry_t *)mod_dir, sizeof(iso_9660_directory_entry_t));
#if 0
	print("mod restored.");
	print("\n Entry len:  "); print_hex( dir_entry->length);
	print("\n File start: "); print_hex( dir_entry->extent_start_LSB);
	print("\n File len:   "); print_hex( dir_entry->extent_length_LSB);
	print("\n");
#endif
}
