use cdtypes::types::{helper::sector_count_audio, sector::{Mode2Form1, Mode2Form2}};

use super::*;

const CURRENT_DIR_NAME:&'static str = "\x00";
const PARENT_DIR_NAME:&'static str  = "\x01";

#[derive(Clone)]
pub enum InterleavedXASector {
    Empty,
    Data(Mode2Form1),
    Audio(Mode2Form2)
}

impl InterleavedXASector {
    pub fn empty() -> InterleavedXASector {
        InterleavedXASector::Empty
    }

    pub fn data(sector: Mode2Form1) -> InterleavedXASector {
        InterleavedXASector::Data(sector)
    }

    pub fn audio(sector: Mode2Form2) -> InterleavedXASector {
        InterleavedXASector::Audio(sector)
    }
}

pub struct PathTableMember {
    pub name:            String,
    pub track_rel_lba:   usize,
    pub parent_table_id: usize,
}

pub enum DirectoryRecordMember {
    Directory{name: String, track_rel_lba: usize, real_size: usize},
        File{name: String,  track_rel_lba: usize, real_size: usize},
}

impl DirectoryRecordMember {
    pub fn new_dir(name: String, properties: &Properties) -> DirectoryRecordMember {
        DirectoryRecordMember::Directory{name, track_rel_lba: properties.lba.get_track_relative(), real_size: properties.get_real_size()}
    }

    pub fn new_file(name: String, properties: &Properties) -> DirectoryRecordMember {
        DirectoryRecordMember::File{name, track_rel_lba: properties.lba.get_track_relative(), real_size: properties.get_real_size()}
    }

    pub fn get_name(&self) -> &String {
        match self {
            DirectoryRecordMember::Directory{name, track_rel_lba: _, real_size: _} => name,
            DirectoryRecordMember::File{name, track_rel_lba: _, real_size: _}      => name,
        }
    }
}

pub fn collect_path_table_member(root: &Directory) -> Vec<PathTableMember> {
    fn collect_path_table_for(collection: &mut Vec<PathTableMember>, dirs: &Vec<SharedPtr<Directory>>, parent_id: usize) {
        let mut cur_dirs = Vec::new();

        for dir in dirs {
            let dir = dir.borrow();
            if !dir.properties.borrow().is_hidden {
                cur_dirs.push(PathTableMember{name: dir.name.as_string().unwrap_or("".to_owned()), track_rel_lba: dir.get_track_rel_lba(), parent_table_id: parent_id});
            }
        }

        cur_dirs.sort_by(|a, b| {
            a.name.cmp(&b.name)
        });

        collection.append(&mut cur_dirs);

        for dir in dirs {
            collect_path_table_for(collection, &dir.borrow().dirs, parent_id + 1);
        }
    }
    let mut collection = Vec::new();
    
    collection.push(PathTableMember{name: CURRENT_DIR_NAME.to_owned(), track_rel_lba: root.get_track_rel_lba(), parent_table_id: 1});
    collect_path_table_for(&mut collection, &root.dirs, 1);
    collection
}

pub fn collect_directory_record_member(dir: &Directory) -> Vec<DirectoryRecordMember> {
    let properties        = dir.properties.borrow();
    let parent_properties = {
        if let Some(parent_properties) = &dir.parent_properties {
            parent_properties.clone()
        }

        else {
            dir.properties.clone()
        }
    };
    let mut collection = Vec::new();

    collection.push(DirectoryRecordMember::new_dir(CURRENT_DIR_NAME.to_owned(), &properties));
    collection.push(DirectoryRecordMember::new_dir(PARENT_DIR_NAME.to_owned(),  &parent_properties.borrow()));
    for file in &dir.files {
        let file = file.borrow();
        if !file.properties.is_hidden {
            if let Some(name) = file.name.as_string() {
                collection.push(DirectoryRecordMember::new_file(name, &file.properties));
            }
        }
    }

    for dir in &dir.dirs {
        let dir = dir.borrow();
        if !dir.properties.borrow().is_hidden {
            if let Some(name) = dir.name.as_string() {
                let properties = dir.properties.borrow();
                collection.push(DirectoryRecordMember::new_dir(name, &properties));
            }
        }
    }

    collection.sort_by(|a, b| {a.get_name().cmp(b.get_name())});
    collection
}

pub struct InterleavedXASizes {
    pub channels:             usize,
    pub biggest_file_sectors: usize,
    pub full_size_bytes:      usize,
}

impl InterleavedXASizes {
    pub fn new(content: &Vec<RawData>) -> InterleavedXASizes {
        let biggest_file_sectors = {
            let mut biggest_size = 0;

            for channel in content {
                if channel.len() > biggest_size {
                    biggest_size = channel.len();
                }
            }
            
            // Strip off all the unnecessary data
            sector_count_audio(biggest_size) + 1
            //                                 ^ encoding file end for streaming
        };
        let channels = content.len();
        InterleavedXASizes{
            channels, biggest_file_sectors: biggest_file_sectors, full_size_bytes: channels*biggest_file_sectors*Mode2Form2::DATA_SIZE
        }
    }
}