use super::{*, SectorWriter, {CDDATrack, CDDesc, Error}};
use super::super::types::{FileType, helper::{DirectoryRecordMember, PathTableMember}, layout::Layout, *};
use builder::SubModeBuilder;
use cdtypes::types::{cdstring::{AString, DString}, date::*, dir_record::*, helper::{round_bytes_mode2_form1, sector_count_mode2_form1, sector_count_mode2_form2}, lsb_msb::*, path_table::*, pvd as cd_pvd, sector::Mode2Form1};
use tool_helper::{BufferedInputFile, format_if_error, open_input_file_buffered, print_warning};
use std::{io::{Read, Seek, SeekFrom}, path::PathBuf};

const ROOT_DIR_NAME:&'static str     = "\x00";

pub mod size_of {
    use cdtypes::types::{sector::Mode0, helper::{sector_count_mode2_form1, sector_count_mode2_form2}};
    use crate::types::FileType;
    use super::{Directory, File, FullSizeInfo, RealSizeInfo, SizeInfo, PathTable};
    
    pub const SYSTEM_AREA_SECTOR_COUNT:usize = 16;
    pub const PVD_SECTOR_COUNT:usize         = 2;

    pub const fn system_area() -> SizeInfo {
        SizeInfo{
            real_size: RealSizeInfo{bytes: None, sectors: SYSTEM_AREA_SECTOR_COUNT},
            full_size: FullSizeInfo{bytes: None, sectors: SYSTEM_AREA_SECTOR_COUNT}
        }
    }

    pub const fn pvd() -> SizeInfo {
        SizeInfo{
            real_size: RealSizeInfo{bytes: None, sectors: PVD_SECTOR_COUNT},
            full_size: FullSizeInfo{bytes: None, sectors: PVD_SECTOR_COUNT}
        }
    }

    pub fn path_tables(root: &Directory) -> SizeInfo {
        let path_table_size = PathTable::calculate_size_for(root);
        SizeInfo{
            real_size: RealSizeInfo{bytes: Some(path_table_size), sectors: sector_count_mode2_form1(path_table_size)},
            full_size: FullSizeInfo{bytes: Some(path_table_size), sectors: sector_count_mode2_form1(path_table_size)}
        }
    }

    pub fn directory(dir: &Directory) -> SizeInfo {
        let real_size_bytes = dir.properties.borrow().get_real_size();
        let full_size_bytes = dir.get_extended_size();
        SizeInfo{
            real_size: RealSizeInfo{bytes: Some(real_size_bytes), sectors: sector_count_mode2_form1(real_size_bytes)},
            full_size: FullSizeInfo{bytes: Some(full_size_bytes), sectors: sector_count_mode2_form1(full_size_bytes)}
        }   
    }

    pub fn file(file: &File) -> SizeInfo {
        let real_size_bytes = file.properties.get_real_size();
        let full_size_bytes = file.get_extended_size();
        if matches!(file.content, FileType::XAAudio(_)) {
            SizeInfo{
                real_size: RealSizeInfo{bytes: Some(real_size_bytes), sectors: sector_count_mode2_form2(real_size_bytes)},
                full_size: FullSizeInfo{bytes: Some(full_size_bytes), sectors: sector_count_mode2_form2(full_size_bytes)}
            }
        }

        else {
            SizeInfo{
                real_size: RealSizeInfo{bytes: Some(real_size_bytes), sectors: sector_count_mode2_form1(real_size_bytes)},
                full_size: FullSizeInfo{bytes: Some(full_size_bytes), sectors: sector_count_mode2_form1(full_size_bytes)}
            }
        }        
    }

    pub fn lead_out(sectors: usize) -> SizeInfo {
        SizeInfo{
            real_size: RealSizeInfo{bytes: Some(sectors*Mode0::DATA_SIZE), sectors},
            full_size: FullSizeInfo{bytes: Some(sectors*Mode0::DATA_SIZE), sectors}
        }
    }
}

pub fn calculate_length_for(element: &Layout) -> SizeInfo {
    match element {
        Layout::SystemArea(_)        => size_of::system_area(),
        Layout::PVD(_)               => size_of::pvd(),
        Layout::PathTables(root, _)  => size_of::path_tables(&root.borrow()),
        Layout::Directory(dir)       => size_of::directory(&dir.borrow()),
        Layout::File(file)           => size_of::file(&file.borrow()),
    }
}

pub fn calculate_lbas(cd_desc: &mut CDDesc) {
    let mut cur_lba      = 0;
    let     track_offset = LBA::FIRST_TRACK_OFFSET;
    
    cd_desc.vol_sector_count = 0;
    CDDesc::for_each_dir_mut(cd_desc.root.clone(), &|dir| {dir.update_content_size();});

    for element in cd_desc.get_memory_layout() {
        fn update_lba(properties: &mut Properties, cur_lba: usize, track_offset: usize, content_sector_size: usize) -> usize {
            properties.lba.overwrite(cur_lba, track_offset);
            cur_lba + content_sector_size
        }
        
        let element_size_info = calculate_length_for(&element);
        match element {
            Layout::SystemArea(system_area) => {
                let mut system_area = system_area.borrow_mut();

                system_area.lba.overwrite(cur_lba, track_offset);
                cur_lba += element_size_info.full_size.sectors;
            },

            Layout::PVD(pvd) => {
                let mut pvd = pvd.borrow_mut();

                pvd.lba.overwrite(cur_lba, track_offset);
                cur_lba += element_size_info.full_size.sectors;
            },

            Layout::PathTables(_, path_table) => {
                let mut path_table = path_table.borrow_mut();

                path_table.lba.overwrite(cur_lba, track_offset);
                path_table.size_bytes    = element_size_info.full_size.bytes.unwrap_or(0);

                cur_lba += element_size_info.full_size.sectors*4;
            },

            Layout::Directory(dir) => {
                let dir            = dir.borrow_mut();
                let mut properties = dir.properties.borrow_mut();

                cd_desc.vol_sector_count += element_size_info.full_size.sectors;
                if properties.is_hidden {
                    properties.lba.overwrite(0, 0);
                }

                else {
                    cur_lba = update_lba(&mut properties, cur_lba, track_offset, element_size_info.full_size.sectors);
                }
            },

            Layout::File(file) => {
                let mut file = file.borrow_mut();

                cd_desc.vol_sector_count += element_size_info.full_size.sectors;
                cur_lba = update_lba(&mut file.properties, cur_lba, track_offset, element_size_info.full_size.sectors);
            }
        }
    }
}

pub fn encode_image(cd_desc: &CDDesc, sec_writer: &mut dyn SectorWriter) -> Result<(), Error> {
    let vol_sector_count = cd_desc.vol_sector_count;
    
    for element in cd_desc.get_memory_layout() {
        match element {
            Layout::SystemArea(system_area)      => process_system_area(&mut system_area.borrow_mut(), sec_writer)?,
            Layout::PVD(pvd)                     => process_pvd(&pvd.borrow(), cd_desc.path_table.clone(), cd_desc.root.clone(), vol_sector_count, sec_writer)?,
            Layout::PathTables(root, path_table) => process_path_table(&path_table.borrow(), root, sec_writer)?,
            Layout::Directory(dir)               => process_directory_record(&dir.borrow(), sec_writer)?,
            Layout::File(file)                   => process_file(&file.borrow(), sec_writer)?,
        }
    }

    process_lead_out(cd_desc.lead_out_sectors, sec_writer)?;
    process_end_dummy_section(cd_desc.end_dummy_padding, sec_writer)?;
    process_cd_da(&cd_desc.cd_da_tracks, sec_writer)
}

fn process_system_area(system_area: &SystemArea, sec_writer: &mut dyn SectorWriter) -> Result<(), Error> {
    fn write_data_zeros(sec_writer: &mut dyn SectorWriter) -> Result<(), Error> {
        write_dummy(sec_writer, 4)
    }

    fn write_audio_zeros(sec_writer: &mut dyn SectorWriter) -> Result<(), Error> {
        for _ in 0..4 {
            let sector = {
                let mut sector = builder::create_xa_audio_zero(0);

                sector.sub_header.sub_mode.clear_audio();
                sector
            };
            sec_writer.write_cd_xa_audio(sector)?;
        }
        
        Ok(())
    }

    fn write_license_string_raw(sec_writer: &mut dyn SectorWriter, data: &[u8; Mode2Form1::DATA_SIZE]) -> Result<usize, Error> {
        let sub_mode = SubModeBuilder::new_mode1().create();
        let sector   = builder::create_xa_data_for_raw(sub_mode, data);

        sec_writer.write_cd_xa_data(sector)
    }

    fn write_license_file(sec_writer: &mut dyn SectorWriter, mut license_file: BufferedInputFile) -> Result<(), Error> {
        fn write_license_string(sec_writer: &mut dyn SectorWriter, license_file: &mut BufferedInputFile) -> Result<(), Error> {
            const LICENSE_STRING_START:u64 = 0x2488;

            if license_file.get_ref().metadata()?.len() < LICENSE_STRING_START {
                return Err(Error::from_str("License file to short to contain license string. Is this is a valid license file?"));
            }

            let mut license_string_buffer = [0u8; Mode2Form1::DATA_SIZE];

            license_file.seek(SeekFrom::Start(LICENSE_STRING_START))?;
            license_file.read(&mut license_string_buffer)?;

            write_license_string_raw(sec_writer, &license_string_buffer)?;
            Ok(())
        }

        fn write_license_logo(sec_writer: &mut dyn SectorWriter, license_file: &mut BufferedInputFile) -> Result<(), Error> {
            const LICENSE_LOGO_START:u64 = 0x2DA8;

            license_file.seek(SeekFrom::Start(LICENSE_LOGO_START))?;
            for _ in 0..7 {
                const LICENSE_SECTOR_REST:i64 = 0x120;

                if license_file.get_ref().metadata()?.len() < license_file.stream_position()? + LICENSE_SECTOR_REST as u64 {
                    return Err(Error::from_str("License file to short to contain license logo. Is this is a valid license file?"));
                }

                let mut license_logo_buffer = [0u8; Mode2Form1::DATA_SIZE];

                license_file.read(&mut license_logo_buffer)?;
                license_file.seek(SeekFrom::Current(LICENSE_SECTOR_REST))?;

                let sub_mode = SubModeBuilder::new_mode1().create();
                let sector   = builder::create_xa_data_for_raw(sub_mode, &license_logo_buffer);

                sec_writer.write_cd_xa_data(sector)?;
            }
            Ok(())
        }

        format_if_error!(write_data_zeros(sec_writer),                        "Writing license data zeros failed with: {error_text}")?;
        format_if_error!(write_license_string(sec_writer, &mut license_file), "Writing license string from file failed with: {error_text}")?;
        format_if_error!(write_license_logo(sec_writer, &mut license_file),   "Writing license logo from file failed with: {error_text}")?;
        format_if_error!(write_audio_zeros(sec_writer),                       "Writing license audio zeros failed with: {error_text}")
    }

    fn write_tmd_file(sec_writer: &mut dyn SectorWriter, tmd_file: BufferedInputFile, lic_text: &Option<String>) -> Result<(), Error> {
        fn write_license_string(sec_writer: &mut dyn SectorWriter, text: &str) -> Result<(), Error> {
            let text_len = text.len();

            if text_len <= Mode2Form1::DATA_SIZE {
                let mut license_string_buffer = [0u8; Mode2Form1::DATA_SIZE];

                license_string_buffer[..text_len].copy_from_slice(text[0..text_len].as_bytes());
                write_license_string_raw(sec_writer, &license_string_buffer)?;
                Ok(())
            }

            else {
                Err(Error::from_text(format!("License string \"{}\" is longer then supported length {}", text, Mode2Form1::DATA_SIZE)))
            }
        }

        fn write_license_logo(sec_writer: &mut dyn SectorWriter, mut tmd_file: BufferedInputFile) -> Result<(), Error> {
            const SECTOR_COUNT:usize = 11 - 5 + 1;

            let bytes = {
                let mut bytes = vec![];
                tmd_file.read_to_end(&mut bytes)?;
                bytes
            };
            let mut sectors = builder::create_xa_data_for_vec(Some(SubModeBuilder::new_mode1().create()), &bytes, 0xFFu8);
            if sectors.len() > SECTOR_COUNT {
                return Err(Error::from_str("Provided TMD file for alt-license is larger then 8 sectors of data"));
            }

            while sectors.len() < SECTOR_COUNT {
                sectors.push(builder::create_xa_data_zero());
            }

            for sector in sectors {
                sec_writer.write_cd_xa_data(sector)?;
            }
            Ok(())
        }

        let lic_text = {
            if let Some(lic_text) = lic_text {
                lic_text.as_ref()
            }

            else {
                ""
            }
        };

        format_if_error!(write_data_zeros(sec_writer),               "Writing alt-license data zeros failed with: {error_text}")?;
        format_if_error!(write_license_string(sec_writer, lic_text), "Writing alt-license string from file failed with: {error_text}")?;
        format_if_error!(write_license_logo(sec_writer, tmd_file),   "Writing alt-license logo from file failed with: {error_text}")?;
        format_if_error!(write_audio_zeros(sec_writer),              "Writing alt-license audio zeros failed with: {error_text}")
    }
        
    let system_area_lba = system_area.lba.get_track_relative();
    if system_area_lba != 0 {
        return Err(Error::from_text(format!("System Area required to start at sector 0 of Track - found LBA: {}", system_area_lba)));
    }

    fn write_tmd_license(sec_writer: &mut dyn SectorWriter, tmd_path: &PathBuf, lic_text: &Option<String>) -> Result<(), Error> {
        print_warning(format!("WARNING: An alternative license file was provided. {}", NOT_BOOTING_CD_STR));
        let tmd_file = format_if_error!(open_input_file_buffered(tmd_path), "Loading TMD file from {} failed with: {error_text}", tmd_path.to_string_lossy())?;
        write_tmd_file(sec_writer, tmd_file, lic_text)
    }

    const NOT_BOOTING_CD_STR:&'static str = "Some emulators (No$PSX) and some consoles (japanese/some european PS1; All PS3) will not boot this CD.";
    match &system_area.license_file {        
        LicenseFile::Authentic(license_path) => {
            match open_input_file_buffered(license_path) {
                Ok(license_file) => write_license_file(sec_writer, license_file),
                Err(error)       => {
                    let err_str = format!("Loading license file from {} failed with: {}", license_path.to_string_lossy(), error);
                    if let LicenseFile::TMD(tmd_path, lic_text) = &system_area.backup_license_file {
                        print_warning(format!("{}. Now using backup license information", err_str));
                        write_tmd_license(sec_writer, tmd_path, lic_text)
                    }

                    else {
                        Err(Error::from_text(err_str))
                    }
                }
            }                
        },
        LicenseFile::TMD(tmd_path, lic_text) => write_tmd_license(sec_writer, tmd_path, lic_text),
        LicenseFile::None                    => {
            // No license specified - filling it with zeros
            print_warning(format!("WARNING: No license file provided. {}", NOT_BOOTING_CD_STR));
            write_dummy(sec_writer, size_of::SYSTEM_AREA_SECTOR_COUNT)
        },
    }
}

fn process_pvd(pvd: &PrimaryVolumeDescriptor, path_table: SharedPtr<PathTable>, root_dir: SharedPtr<Directory>, vol_sector_count: usize, sec_writer: &mut dyn SectorWriter) -> Result<(), Error> {
    const PLAYSATATION_STR:&'static str = "PLAYSTATION";
    
    let path_table = validate_and_unwrap_path_table(&path_table)?;
    let root_dir   = root_dir.borrow();
    let pvd_lba    = pvd.lba.get_track_relative();

    if pvd_lba != 16 {
        return Err(Error::from_text(format!("PVD required to start at sector 16 of Track - found LBA: {}", pvd_lba)));
    }

    let mut cd_pvd = cd_pvd::PrimaryVolumeDescriptor::new();
    let now        = Date::now();
    
    //Config pvd here
    cd_pvd.system_id = AString::from_str(PLAYSATATION_STR)?;
    cd_pvd.volume_id = DString::from_str("PSX")?;
    cd_pvd.vol_space_size.write(vol_sector_count as u32);

    //Set PathTable values
    cd_pvd.path_table_size.write(path_table.size_bytes as u32);
    cd_pvd.path_table_1.write(path_table.get_track_rel_lba_for(1, sector_count_mode2_form1) as u32);
    cd_pvd.path_table_2.write(path_table.get_track_rel_lba_for(2, sector_count_mode2_form1) as u32);
    cd_pvd.path_table_3.write(path_table.get_track_rel_lba_for(3, sector_count_mode2_form1) as u32);
    cd_pvd.path_table_4.write(path_table.get_track_rel_lba_for(4, sector_count_mode2_form1) as u32);

    //Set Root Directory Record
    write_dir_record(&mut cd_pvd.root_dir_record, &DirectoryRecordMember::new_dir(ROOT_DIR_NAME.to_owned(), &root_dir.properties.borrow()), false)?;
    
    //Set other stuff
    cd_pvd.publisher_id  = AString::from_str(pvd.publisher.as_str())?;
    cd_pvd.data_preparer = AString::from_str("JABYENGINE PSXCDGEN_EX")?;
    cd_pvd.app_id        = AString::from_str(PLAYSATATION_STR)?;

    cd_pvd.vol_create_time = now;

    cd_pvd.cd_xa_id  = ['C' as u8, 'D' as u8, '-' as u8, 'X' as u8, 'A' as u8, '0' as u8, '0' as u8, '1' as u8];
    
    //Write PVD and VDT
    sec_writer.write_cd_xa_data(builder::create_xa_data_for(SubModeBuilder::new_mode1().set_eor().create(),           &cd_pvd))?;
    sec_writer.write_cd_xa_data(builder::create_xa_data_for(SubModeBuilder::new_mode1().set_eor().set_eof().create(), &cd_pvd::VolumeDescriptorTerminator::new()))?;
    Ok(())
}

fn process_path_table(path_table: &PathTable, root_dir: SharedPtr<Directory>, sec_writer: &mut dyn SectorWriter) -> Result<(), Error> {
    macro_rules! write_path_table_twice {
        ($table:ident) => {
            for sector in $table.clone() {
                sec_writer.write_cd_xa_data(sector)?;
            }
        
            for sector in $table {
                sec_writer.write_cd_xa_data(sector)?;
            }    
        };
    }
    let mut bytes_used       = 0;
    let mut path_table_raw_l = vec![0u8; path_table.size_bytes];
    let mut path_table_raw_b = vec![0u8; path_table.size_bytes];
    
    validate_path_table(path_table)?;
    let path_table = PathTable::collect_member(&root_dir.borrow());   

    for entry in path_table {
        bytes_used += unsafe{update_path_table_entry(std::mem::transmute::<&mut u8, &mut PathTableL>(&mut path_table_raw_l[bytes_used]), std::mem::transmute::<&mut u8, &mut PathTableB>(&mut path_table_raw_b[bytes_used]), entry)?};
    }

    let path_table_l = builder::create_xa_data_for_vec(None, &path_table_raw_l, 0x0u8);
    let path_table_b = builder::create_xa_data_for_vec(None, &path_table_raw_b, 0x0u8);

    write_path_table_twice!(path_table_l);
    write_path_table_twice!(path_table_b);

    Ok(())
}

fn process_directory_record(dir: &Directory, sec_writer: &mut dyn SectorWriter) -> Result<(), Error> {
    if dir.properties.borrow().is_hidden {
        return Ok(());
    }

    let properties = dir.properties.borrow();
    if !properties.is_size_valid() {
        return Err(create_wrong_padding_error("Dir", dir.name.to_string(), properties.get_padded_size(), properties.get_real_size()));
    }

    let mut dir_record = vec![0u8; properties.get_real_size()];
    let     dir_length = dir_record.len();
    let mut raw_data   = &mut dir_record[0..dir_length];

    for member in dir.collect_member() {
        let raw_data_len  = raw_data.len();
        let bytes_written = write_dir_record(raw_data, &member, true)?;

        raw_data = &mut raw_data[bytes_written..raw_data_len];
    }
    
    let dir_record_sectors      = builder::create_xa_data_for_vec(None, &dir_record, 0x0u8);
    let dir_record_sector_count = dir_record_sectors.len();

    if dir_record_sector_count > 1 {
        return Err(Error::from_text(format!("Directory Record for {} spans {} sectors but PSX doesn't support more then one sector", dir.name.as_str().unwrap_or("<No name>"), dir_record_sector_count)));
    }

    for sector in dir_record_sectors {
        sec_writer.write_cd_xa_data(sector)?;
    }

    let extended_sector_count = sector_count_mode2_form1(dir.properties.borrow().get_padded_size()) - dir_record_sector_count;
    write_dummy(sec_writer, extended_sector_count)
}

fn process_file(file: &File, sec_writer: &mut dyn SectorWriter) -> Result<(), Error> {
    fn process_regular_file(content: &RawData, sec_writer: &mut dyn SectorWriter, padded_size: usize) -> Result<(), Error> {
        let content_sectors      = builder::create_xa_data_for_vec(None, content, 0x0u8);
        let content_sector_count = content_sectors.len();

        for sector in content_sectors {
            sec_writer.write_cd_xa_data(sector)?;
        }
    
        let extended_sector_count = sector_count_mode2_form1(padded_size) - content_sector_count;
        write_dummy(sec_writer, extended_sector_count)
    }

    fn process_cd_xa_file(content: &Vec<RawData>, sec_writer: &mut dyn SectorWriter, padded_size: usize) -> Result<(), Error> {
        let content_sectors      = builder::create_xa_audio_for(content);
        let content_sector_count = content_sectors.len();

        for sector in content_sectors {
            sec_writer.write_interleaved(sector)?;
        }

        let extended_sector_count = sector_count_mode2_form2(padded_size) - content_sector_count;
        for _ in 0..extended_sector_count {
            sec_writer.write_cd_xa_audio(builder::create_xa_audio_zero(0))?;
        }

        Ok(())
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    if !file.properties.is_size_valid() {
        return Err(create_wrong_padding_error("File", file.name.to_string(), file.properties.get_padded_size(), file.properties.get_real_size()));
    }

    match &file.content {
        FileType::Regular(raw)   => process_regular_file(raw, sec_writer, file.properties.get_padded_size()),
        FileType::XAAudio(raw)   => process_cd_xa_file(raw, sec_writer, file.properties.get_padded_size()),
        FileType::Main(_, _)     => Err(Error::from_str("Trying to encode an unprocessed main file")),
        FileType::Overlay(_, _)  => Err(Error::from_str("Trying to encode an unprocessed overlay file")),
    }
}

fn write_dummy(sec_writer: &mut dyn SectorWriter, sectors: usize) -> Result<(), Error> {
    for _ in 0..sectors {
        sec_writer.write_cd_xa_data(builder::create_xa_data_zero())?;
    }
    Ok(())
}

fn process_lead_out(sectors: usize, sec_writer: &mut dyn SectorWriter) -> Result<(), Error> {
    for _ in 0..sectors {
        sec_writer.write_empty(builder::create_mode0_zero())?;
    }
    Ok(())
}

fn process_end_dummy_section(padding: usize, sec_writer: &mut dyn SectorWriter) -> Result<(), Error> {
    // The padding required for the PS3
    write_dummy(sec_writer, sector_count_mode2_form1(padding))
}

fn process_cd_da(cd_da_tracks: &Vec<CDDATrack>, sec_writer: &mut dyn SectorWriter) -> Result<(), Error> {
    for cd_da_track in cd_da_tracks {
        // The padding required for CDDA to start on a full second
        if cd_da_track.align {
            let missing_sectors = sec_writer.sectors_to_next_second();
            write_dummy(sec_writer, missing_sectors)?;
        }

        sec_writer.cd_da_start()?;
        for audio in builder::create_audio_for_vec(&cd_da_track.samples) {
            sec_writer.write_audio(audio)?;
        }
    }
    Ok(())
}

fn validate_path_table(path_table: &PathTable) -> Result<(), Error> {
    if path_table.size_bytes > Mode2Form1::DATA_SIZE {
        Err(Error::from_text(format!("Path Tables are not allowed to be bigger then {} bytes - Path Table has {} bytes", Mode2Form1::DATA_SIZE, path_table.size_bytes)))
    }

    else {
        Ok(())
    }
}

fn validate_and_unwrap_path_table(path_table: &SharedPtr<PathTable>) -> Result<std::cell::Ref<PathTable>, Error> {
    let path_table = path_table.borrow();

    validate_path_table(&path_table)?;
    Ok(path_table)
}

fn update_path_table_entry(path_table_l: &mut PathTableL, path_table_b: &mut PathTableB, entry: PathTableMember) -> Result<usize, Error> {
    let name_len = entry.name.len();
    if name_len > 8 {
        return Err(Error::from_text(format!("Directory name can not exceed size of 8 characters but folder {} has {} characters", entry.name, name_len)));
    }

    unsafe{
        let name = entry.name.as_str();

        path_table_l.new(name);
        path_table_b.new(name);
    }
    
    path_table_l.directory_logical_block.write(entry.track_rel_lba as u32);
    path_table_b.directory_logical_block.write(entry.track_rel_lba as u32);
    path_table_l.parent_table_id.write(entry.parent_table_id as u16);
    path_table_b.parent_table_id.write(entry.parent_table_id as u16);

    Ok(path_table_l.get_size())
}

fn create_dir_record_raw<'a>(dst: &'a mut [u8], name: &str, track_rel_lba: u32, size_bytes: u32, system_use: Option<CDXASystemUse>) -> Result<&'a mut DirectoryRecord, Error> {
    let has_system_use = system_use.is_some();
    let bytes_needed   = DirectoryRecord::calculate_size_for(name, has_system_use);

    if dst.len() < bytes_needed {
        return Err(Error::from_text(format!("DirectoryRecord for entry {} needs {} bytes but {} bytes were provided", name, bytes_needed, dst.len())));
    }

    unsafe {
        let dir_record = std::mem::transmute::<&mut u8, &mut DirectoryRecord>(&mut dst[0]);
        dir_record.new(name, has_system_use);

        dir_record.data_block_number.write(track_rel_lba);
        dir_record.data_size.write(size_bytes);
        dir_record.time_stamp = SmallDate::now();

        if let Some(system_use) = system_use {
            if let Some(record_system_use) = dir_record.get_cdxa_system_use_mut() {
                *record_system_use = system_use;
            }
        }

        Ok(dir_record)
    }
}

fn write_dir_record(dir_record: &mut [u8], dir_member: &DirectoryRecordMember, has_system_use: bool) -> Result<usize, Error> {
    match dir_member {
        DirectoryRecordMember::Directory{name, track_rel_lba, real_size} => {
            let system_use = {
                if has_system_use {
                    let mut system_use = CDXASystemUse::default();

                    system_use.file_attribute.set_mode2();
                    system_use.file_attribute.set_directory();
            
                    Some(system_use)
                }

                else {
                    None
                }
            };

            let dir_record = create_dir_record_raw(dir_record, name.as_str(), *track_rel_lba as u32, round_bytes_mode2_form1(*real_size as usize) as u32, system_use)?;

            dir_record.set_directory();
            return Ok(dir_record.length[0] as usize);
        },
     
        DirectoryRecordMember::File{name, track_rel_lba, real_size} => {
            let system_use = {
                if has_system_use {
                    let mut system_use = CDXASystemUse::default();

                    system_use.file_attribute.set_mode2();
                    Some(system_use)
                }

                else {
                    None
                }
            };

            let dir_record = create_dir_record_raw(dir_record, name.as_str(), *track_rel_lba as u32, *real_size as u32, system_use)?;

            dir_record.set_file();
            return Ok(dir_record.length[0] as usize);
        }
    }
}

fn create_wrong_padding_error(for_who: &str, name: String, padded_size: usize, real_size: usize) -> Error {
    Error::from_text(format!("Encoding-Error for {} {}: Padded size ({}) is smaller then the original size ({}).", for_who, name, padded_size, real_size))
}