#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <errno.h>
#include <limits.h>
#include <string.h>

/*
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
*/

#include <arpa/inet.h>
#include <string.h>

#include <cdio/cdio.h>
#include <cdio/cd_types.h>
#include <cdio/logging.h>
#include <cdio/device.h>
#include <cdio/mmc.h>

#pragma pack(push)
#pragma pack(1)
typedef struct {
    uint8_t magic[2];  // Always "DI"
    uint8_t format;
    uint8_t units_per_block;
    uint8_t reserved1;
    uint8_t sequence_number;
    uint8_t bytes_in_use;
    uint8_t reserved2;
    uint32_t disc_type_id:24;
    /*
                      BluSize: 120mm == 0, 80mm == 1
       DiscSize    = (BluSize)(disc_size_class_version & 0xC0) >> 6);
       DiscClass   =    (byte)(disc_size_class_version & 0x30) >> 4);
       DiscVersion =    (byte)(disc_size_class_version & 0x0F);
    */
    uint8_t disc_size_class_version;  // this is a bitfield
    uint8_t contents[52];
} di_unit_t;
#pragma pack(pop)
const unsigned char empty_di_unit[sizeof(di_unit_t)] = { 0 };

int main(int argc, char **argv) {
    CdIo_t *p_cdio;
    mmc_cdb_t cdb = {
        .field = {
            // CDIO_MMC_GPCMD_READ_DVD_STRUCTURE = 0xAD
            0xAD, 0x01, 0x00, 0x00, 0x00, 0x00,
            // First byte in the second row is the layer number
            // int(4100).to_bytes(length=2, byteorder='big') -> b'\x10\x04'
            // "10 04 " is the allocation size (which is always 4100 for Blu-Ray Disc Information)
            0x00, 0x00, 0x10, 0x04, 0x00, 0x00,
        },
    };
    // https://www.t10.org/ftp/t10/document.05/05-206r0.pdf
    // https://github.com/aaru-dps/Aaru.Decoders/blob/5c0bc744ab448203fb7b8f690ab44a29874763c9/Blu-ray/DI.cs
    unsigned char *rds_ptr, rds_buf[4100] = { 0 };
    di_unit_t *di;

    cdio_loglevel_default = CDIO_LOG_WARN;
    p_cdio = cdio_open("/dev/sr0", DRIVER_LINUX);
    if(!p_cdio) {
        printf("Failed 1\n");
        return 1;
    }
    // https://makemkv.com/forum/viewtopic.php?f=19&t=23042
    if(mmc_run_cmd(p_cdio, 1000, &cdb, SCSI_MMC_DATA_READ, 4100, rds_buf) != DRIVER_OP_SUCCESS) {
        printf("Failed 2\n");
        cdio_destroy(p_cdio);
        return 1;
    }
    /*
    int fd = open("dvd_structure.bin", O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
    write(fd, rds_buf, 4100);
    close(fd);
    */
    cdio_destroy(p_cdio);

    rds_ptr = rds_buf;
    printf("Data structure length: %u\n", ntohs(*(uint16_t *)rds_ptr));
    rds_ptr += 2;
    printf("Reserved bytes: 0x%02X 0x%02X\n", *rds_ptr, *(rds_ptr+1));
    rds_ptr += 2;

    printf("Magic Format Units/Block Reserved Sequence Bytes In Use Reserved  Type ID SCV\n");
    while((rds_ptr-rds_buf) < 4100) {
        if(!memcmp(rds_ptr, empty_di_unit, sizeof(di_unit_t))) {
            rds_ptr += sizeof(di_unit_t);
            continue;
        }
        di = (di_unit_t *)rds_ptr;
        printf("   %c%c     %02X          %02X       %02X       %02X           %02X       %02X %06X  %02X\n",
               di->magic[0], di->magic[1], di->format, di->units_per_block, di->reserved1, di->sequence_number,
               di->bytes_in_use, di->reserved2, ntohl(di->disc_type_id), di->disc_size_class_version
        );
        printf("AUN: First=%08X Last=%08X\n", ntohl(*(uint32_t *)di->contents+12), ntohl(*(uint32_t *)di->contents+16));
        rds_ptr += sizeof(di_unit_t);
    }

    return 0;
}
